Merge "[PiP2] Find config-at-end activity in AE case" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 4e05cbc..f9cc125 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -73,6 +73,7 @@
         "android.service.dreams.flags-aconfig-java",
         "android.service.notification.flags-aconfig-java",
         "android.service.quickaccesswallet.flags-aconfig-java",
+        "android.service.selinux.flags-aconfig-java",
         "android.service.voice.flags-aconfig-java",
         "android.speech.flags-aconfig-java",
         "android.systemserver.flags-aconfig-java",
@@ -264,6 +265,7 @@
 cc_aconfig_library {
     name: "com.android.window.flags.window-aconfig_flags_c_lib",
     aconfig_declarations: "com.android.window.flags.window-aconfig",
+    host_supported: true,
 }
 
 // DeviceStateManager
@@ -346,6 +348,7 @@
     name: "android.location.flags-aconfig",
     package: "android.location.flags",
     container: "system",
+    exportable: true,
     srcs: [
         "location/java/android/location/flags/*.aconfig",
     ],
@@ -1943,3 +1946,19 @@
     aconfig_declarations: "android.service.quickaccesswallet.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// SELinux log collector
+aconfig_declarations {
+    name: "android.service.selinux.flags-aconfig",
+    package: "com.android.server.selinux.flags",
+    container: "system",
+    srcs: [
+        "services/core/java/com/android/server/selinux/*.aconfig",
+    ],
+}
+
+java_aconfig_library {
+    name: "android.service.selinux.flags-aconfig-java",
+    aconfig_declarations: "android.service.selinux.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 444725e..127556f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -415,6 +415,7 @@
         "mimemap",
         "av-types-aidl-java",
         "tv_tuner_resource_manager_aidl_interface-java",
+        "media_quality_aidl_interface-java",
         "soundtrigger_middleware-aidl-java",
         "modules-utils-binary-xml",
         "modules-utils-build",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e469f16..ce0da7e 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -25,6 +25,12 @@
       "name": "FrameworksUiServicesTests"
     },
     {
+      "name": "FrameworksUiServicesNotificationTests"
+    },
+    {
+      "name": "FrameworksUiServicesZenTests"
+    },
+    {
       "name": "FrameworksInputMethodSystemServerTests_server_inputmethod"
     },
     {
diff --git a/apct-tests/perftests/core/src/android/input/OWNERS b/apct-tests/perftests/core/src/android/input/OWNERS
deleted file mode 100644
index 95e3f02..0000000
--- a/apct-tests/perftests/core/src/android/input/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-include platform/frameworks/base:/INPUT_OWNERS
-
-# Bug component: 136048
diff --git a/apct-tests/perftests/input/Android.bp b/apct-tests/perftests/input/Android.bp
new file mode 100644
index 0000000..21b66cf
--- /dev/null
+++ b/apct-tests/perftests/input/Android.bp
@@ -0,0 +1,44 @@
+// Copyright 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_team: "trendy_team_input_framework",
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "InputPerfTests",
+    srcs: ["src/**/*.kt"],
+    kotlincflags: [
+        "-Werror",
+    ],
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.rules",
+        "apct-perftests-utils",
+        "collector-device-lib",
+        "compatibility-device-util-axt",
+        "cts-input-lib",
+        "platform-test-annotations",
+    ],
+    test_suites: ["device-tests"],
+    data: [":perfetto_artifacts"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/apct-tests/perftests/input/AndroidManifest.xml b/apct-tests/perftests/input/AndroidManifest.xml
new file mode 100644
index 0000000..e9e4fd0
--- /dev/null
+++ b/apct-tests/perftests/input/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.perftests.input">
+
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="com.android.cts.input.CaptureEventActivity"
+            android:label="Capture events"
+            android:configChanges="touchscreen|uiMode|orientation|screenSize|screenLayout|keyboardHidden|uiMode|navigation|keyboard|density|fontScale|layoutDirection|locale|mcc|mnc|smallestScreenSize"
+            android:enableOnBackInvokedCallback="false"
+            android:turnScreenOn="true"
+            android:exported="true">
+        </activity>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.perftests.input">
+        <meta-data android:name="listener" android:value="android.input.InputPerfRunPrecondition" />
+    </instrumentation>
+</manifest>
diff --git a/apct-tests/perftests/input/OWNERS b/apct-tests/perftests/input/OWNERS
new file mode 100644
index 0000000..3cffce9
--- /dev/null
+++ b/apct-tests/perftests/input/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 136048
+include /core/java/android/hardware/input/OWNERS
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/apct-tests/perftests/input/src/android/input/InputPerfRunPrecondition.kt
similarity index 67%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to apct-tests/perftests/input/src/android/input/InputPerfRunPrecondition.kt
index 5d14631..d992380 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/apct-tests/perftests/input/src/android/input/InputPerfRunPrecondition.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2025 The Android Open 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,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package android.input
 
-parcelable PictureProfileHandle;
+import android.perftests.utils.WindowPerfRunPreconditionBase
+
+/** Prepare the preconditions before running performance test. */
+class InputPerfRunPrecondition : WindowPerfRunPreconditionBase()
diff --git a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt b/apct-tests/perftests/input/src/android/input/MotionPredictorBenchmark.kt
similarity index 100%
rename from apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt
rename to apct-tests/perftests/input/src/android/input/MotionPredictorBenchmark.kt
diff --git a/apct-tests/perftests/input/src/android/input/TouchPerfTest.kt b/apct-tests/perftests/input/src/android/input/TouchPerfTest.kt
new file mode 100644
index 0000000..26f101d
--- /dev/null
+++ b/apct-tests/perftests/input/src/android/input/TouchPerfTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.input
+
+import android.cts.input.EventVerifier
+import android.perftests.utils.PerfStatusReporter
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_UP
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.cts.input.CaptureEventActivity
+import com.android.cts.input.UinputTouchScreen
+import com.android.cts.input.VirtualDisplayActivityScenario
+import com.android.cts.input.inputeventmatchers.withMotionAction
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class TouchPerfTest {
+    @get:Rule val testName = TestName()
+    @get:Rule val perfStatusReporter = PerfStatusReporter()
+    @get:Rule
+    val virtualDisplayRule = VirtualDisplayActivityScenario.Rule<CaptureEventActivity>(testName)
+
+    private val instrumentation = InstrumentationRegistry.getInstrumentation()
+
+    @Test
+    fun testDownMoveUp() {
+        UinputTouchScreen(instrumentation, virtualDisplayRule.virtualDisplay.display).use {
+            touchScreen ->
+            val verifier = EventVerifier(virtualDisplayRule.activity::getInputEvent)
+            val state = perfStatusReporter.benchmarkState
+
+            while (state.keepRunning()) {
+                val x = 100
+                val y = 100
+
+                val pointer = touchScreen.touchDown(x, y)
+                verifier.assertReceivedMotion(withMotionAction(ACTION_DOWN))
+
+                pointer.moveTo(x + 1, y + 1)
+                verifier.assertReceivedMotion(withMotionAction(ACTION_MOVE))
+
+                pointer.lift()
+                verifier.assertReceivedMotion(withMotionAction(ACTION_UP))
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt b/apct-tests/perftests/input/src/android/input/VelocityTrackerBenchmarkTest.kt
similarity index 99%
rename from apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt
rename to apct-tests/perftests/input/src/android/input/VelocityTrackerBenchmarkTest.kt
index c6fe324..df58cca 100644
--- a/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt
+++ b/apct-tests/perftests/input/src/android/input/VelocityTrackerBenchmarkTest.kt
@@ -19,8 +19,8 @@
 import android.view.InputDevice
 import android.view.MotionEvent
 import android.view.VelocityTracker
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
-import androidx.test.runner.AndroidJUnit4
 import java.time.Duration
 import org.junit.Assert
 import org.junit.Before
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index cc2d104..d48af2c 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -810,7 +810,7 @@
 
     /**
      * <p class="caution"><strong>Note:</strong> Beginning with
-     * {@link android.os.Build.VERSION_CODES#B}, this flag will be ignored and no longer
+     * {@link android.os.Build.VERSION_CODES#BAKLAVA}, this flag will be ignored and no longer
      * function effectively, regardless of the calling app's target SDK version.
      * Calling this method will always return {@code false}.
      *
@@ -2137,9 +2137,9 @@
          * Jobs marked as important-while-foreground are given {@link #PRIORITY_HIGH} by default.
          *
          * <p class="caution"><strong>Note:</strong> Beginning with
-         * {@link android.os.Build.VERSION_CODES#B}, this flag will be ignored and no longer
+         * {@link android.os.Build.VERSION_CODES#BAKLAVA}, this flag will be ignored and no longer
          * function effectively, regardless of the calling app's target SDK version.
-         * {link #isImportantWhileForeground()} will always return {@code false}.
+         * {@link #isImportantWhileForeground()} will always return {@code false}.
          * Apps should use {link #setExpedited(boolean)} with {@code true} to indicate
          * that this job is important and needs to run as soon as possible.
          *
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 876274e..aae5bb3 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -126,3 +126,15 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "tune_quota_window_default_parameters"
+    namespace: "backstage_power"
+    description: "Tune default active/exempted bucket quota parameters"
+    bug: "401767691"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 251776e..44e4999 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -5369,7 +5369,9 @@
                         // to do any wakelock or stats tracking, so we have nothing
                         // left to do here but go on to the next thing.
                         mSendFinishCount++;
-                        if (Flags.acquireWakelockBeforeSend()) {
+                        if (Flags.acquireWakelockBeforeSend() && mBroadcastRefCount == 0) {
+                            // No other alarms are in-flight and this dispatch failed. We will
+                            // acquire the wakelock again before the next dispatch.
                             mWakeLock.release();
                         }
                         return;
@@ -5409,7 +5411,9 @@
                         // stats management to do.  It threw before we posted the delayed
                         // timeout message, so we're done here.
                         mListenerFinishCount++;
-                        if (Flags.acquireWakelockBeforeSend()) {
+                        if (Flags.acquireWakelockBeforeSend() && mBroadcastRefCount == 0) {
+                            // No other alarms are in-flight and this dispatch failed. We will
+                            // acquire the wakelock again before the next dispatch.
                             mWakeLock.release();
                         }
                         return;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 54d337e..a9c4a15 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -360,13 +360,13 @@
 
     /** How much time each app will have to run jobs within their standby bucket window. */
     private final long[] mAllowedTimePerPeriodMs = new long[]{
-            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+            QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
             QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
             QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
             QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS,
             0, // NEVER
             QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
-            QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
+            QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
     };
 
     /**
@@ -3178,9 +3178,11 @@
         static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
                 QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
 
-        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+        // Legacy default time each app will have to run jobs within EXEMPTED bucket
+        private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
                 10 * 60 * 1000L; // 10 minutes
-        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+        // Legacy default time each app will have to run jobs within ACTIVE bucket
+        private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
                 10 * 60 * 1000L; // 10 minutes
         private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS =
                 10 * 60 * 1000L; // 10 minutes
@@ -3192,14 +3194,26 @@
                 10 * 60 * 1000L; // 10 minutes
         private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
                 10 * 60 * 1000L; // 10 minutes
+
+        // Current default time each app will have to run jobs within EXEMPTED bucket
+        private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+                20 * 60 * 1000L; // 20 minutes
+        // Current default time each app will have to run jobs within ACTIVE bucket
+        private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+                20 * 60 * 1000L; // 20 minutes
+        private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+                20 * 60 * 1000L; // 20 minutes
+
         private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
                 30 * 1000L; // 30 seconds
         // Legacy default window size for EXEMPTED bucket
+        // EXEMPT apps can run jobs at any time
         private static final long DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS =
-                DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
+                DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
         // Legacy default window size for ACTIVE bucket
+        // ACTIVE apps can run jobs at any time
         private static final long DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS =
-                DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time
+                DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
         // Legacy default window size for WORKING bucket
         private static final long DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS =
                 2 * 60 * 60 * 1000L; // 2 hours
@@ -3216,6 +3230,13 @@
         private static final long DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS =
                 12 * 60 * 60 * 1000L; // 12 hours
 
+        // Latest default window size for EXEMPTED bucket.
+        private static final long DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS =
+                40 * 60 * 1000L; // 40 minutes.
+        // Latest default window size for ACTIVE bucket.
+        private static final long DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS =
+                60 * 60 * 1000L; // 60 minutes.
+
         private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
                 24 * 60 * 60 * 1000L; // 24 hours
         private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS =
@@ -3276,12 +3297,13 @@
          * bucket window.
          */
         public long ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
-                DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+                DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
         /**
          * How much time each app in the active bucket will have to run jobs within their standby
          * bucket window.
          */
-        public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+                DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
         /**
          * How much time each app in the working set bucket will have to run jobs within their
          * standby bucket window.
@@ -3575,11 +3597,30 @@
         public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
 
         void adjustDefaultBucketWindowSizes() {
-            WINDOW_SIZE_EXEMPTED_MS = DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
-            WINDOW_SIZE_ACTIVE_MS = DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
+            ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+                    DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+            ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+                    DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+            ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS = Flags.tuneQuotaWindowDefaultParameters()
+                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS :
+                    DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
+            WINDOW_SIZE_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+                    ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+                    DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
+            WINDOW_SIZE_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+                    ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+                    DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
             WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
             WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
 
+            mAllowedTimePerPeriodMs[EXEMPTED_INDEX] = Math.min(MAX_PERIOD_MS,
+                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS));
+            mAllowedTimePerPeriodMs[ACTIVE_INDEX] = Math.min(MAX_PERIOD_MS,
+                    Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_ACTIVE_MS));
+
             mBucketPeriodsMs[EXEMPTED_INDEX] = Math.max(
                     mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
                     Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
@@ -3592,6 +3633,11 @@
             mBucketPeriodsMs[FREQUENT_INDEX] = Math.max(
                     mAllowedTimePerPeriodMs[FREQUENT_INDEX],
                     Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
+
+            mAllowedTimePeriodAdditionaInstallerMs =
+                    Math.min(mBucketPeriodsMs[EXEMPTED_INDEX]
+                                    - mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
+                            ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
         }
 
         void adjustDefaultEjLimits() {
@@ -3882,10 +3928,14 @@
                     KEY_WINDOW_SIZE_RESTRICTED_MS);
             ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
                     properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
-                            DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
+                            Flags.tuneQuotaWindowDefaultParameters()
+                                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+                                    DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
             ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
                     properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
-                            DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
+                            Flags.tuneQuotaWindowDefaultParameters()
+                                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+                                    DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
             ALLOWED_TIME_PER_PERIOD_WORKING_MS =
                     properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
                             DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS);
@@ -3900,19 +3950,27 @@
                             DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
             ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
                     properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
-                            DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
+                            Flags.tuneQuotaWindowDefaultParameters()
+                                    ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS
+                                    : DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
             IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
                     DEFAULT_IN_QUOTA_BUFFER_MS);
             MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
                     DEFAULT_MAX_EXECUTION_TIME_MS);
             WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
-                    Flags.adjustQuotaDefaultConstants()
-                            ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
-                            DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS);
+                    (Flags.adjustQuotaDefaultConstants()
+                            && Flags.tuneQuotaWindowDefaultParameters())
+                            ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+                            (Flags.adjustQuotaDefaultConstants()
+                                    ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
+                                    DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS));
             WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
-                    Flags.adjustQuotaDefaultConstants()
-                            ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
-                            DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS);
+                    (Flags.adjustQuotaDefaultConstants()
+                            && Flags.tuneQuotaWindowDefaultParameters())
+                            ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+                            (Flags.adjustQuotaDefaultConstants()
+                                    ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
+                                    DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS));
             WINDOW_SIZE_WORKING_MS =
                     properties.getLong(KEY_WINDOW_SIZE_WORKING_MS,
                             Flags.adjustQuotaDefaultConstants()
diff --git a/boot/boot-image-profile-extra.txt b/boot/boot-image-profile-extra.txt
index ce99bfe..cc02c8ae3 100644
--- a/boot/boot-image-profile-extra.txt
+++ b/boot/boot-image-profile-extra.txt
@@ -70,3 +70,10 @@
 HSPLandroid/os/PerfettoTrackEventExtra$FieldNested;->*
 HSPLandroid/os/PerfettoTrackEventExtra$Pool;->*
 HSPLandroid/os/PerfettoTrackEventExtra$RingBuffer;->*
+
+# While the SystemFeaturesMetadata static cache isn't heavyweight, ensure it's
+# pre-initialized in the boot image to avoid redundant per-process overhead.
+# TODO(b/326623529): Consider removing this after the feature has fully ramped
+# and is captured with the boot image profiling pipeline.
+HSPLcom/android/internal/pm/SystemFeaturesMetadata;->*
+Lcom/android/internal/pm/SystemFeaturesMetadata;
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index f87828e..7f4b324 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -11885,6 +11885,7 @@
 com.android.internal.os.ZygoteServer
 com.android.internal.os.logging.MetricsLoggerWrapper
 com.android.internal.pm.RoSystemFeatures
+com.android.internal.pm.SystemFeaturesMetadata
 com.android.internal.pm.parsing.PackageParser2$Callback
 com.android.internal.pm.parsing.PackageParserException
 com.android.internal.pm.pkg.component.flags.FeatureFlags
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 696bc82..378bb2d 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -1138,6 +1138,10 @@
                 }
                 out.append("]");
             }
+            if (event.containsKey(BackupManagerMonitor.EXTRA_LOG_CANCELLATION_REASON)) {
+                out.append(" cancellationReason: ");
+                out.append(event.getInt(BackupManagerMonitor.EXTRA_LOG_CANCELLATION_REASON));
+            }
             if (mVerbose) {
                 Set<String> remainingKeys = new ArraySet<>(event.keySet());
                 remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
@@ -1309,6 +1313,8 @@
                 return "AGENT_FAILURE_DURING_RESTORE";
             case BackupManagerMonitor.LOG_EVENT_ID_FAILED_TO_READ_DATA_FROM_TRANSPORT:
                 return "FAILED_TO_READ_DATA_FROM_TRANSPORT";
+            case BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_AGENT_PIPE_BROKEN:
+                return "LOG_EVENT_ID_FULL_BACKUP_AGENT_PIPE_BROKEN";
             default:
                 return "UNKNOWN_ID";
         }
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index 26e20f6..6542d08 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -89,6 +89,11 @@
             IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
                     Context.USB_SERVICE));
 
+            if (usbMgr == null) {
+                System.err.println("Could not obtain USB service. Try again later.");
+                return;
+            }
+
             Executor executor = context.getMainExecutor();
             Consumer<Integer> consumer = new Consumer<Integer>(){
                 public void accept(Integer status){
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 4147fd7..707acb0 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -11921,6 +11921,7 @@
 com.android.internal.os.ZygoteServer
 com.android.internal.os.logging.MetricsLoggerWrapper
 com.android.internal.pm.RoSystemFeatures
+com.android.internal.pm.SystemFeaturesMetadata
 com.android.internal.pm.parsing.PackageParser2$Callback
 com.android.internal.pm.parsing.PackageParserException
 com.android.internal.pm.pkg.component.flags.FeatureFlags
diff --git a/core/api/current.txt b/core/api/current.txt
index 7bc0fb2..216bbab 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -34264,6 +34264,7 @@
     method public boolean hasFileDescriptors();
     method public boolean hasFileDescriptors(int, int);
     method public byte[] marshall();
+    method @FlaggedApi("android.os.parcel_marshall_bytebuffer") public void marshall(@NonNull java.nio.ByteBuffer);
     method @NonNull public static android.os.Parcel obtain();
     method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
     method @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader);
@@ -34333,6 +34334,7 @@
     method public void setDataSize(int);
     method public void setPropagateAllowBlocking();
     method public void unmarshall(@NonNull byte[], int, int);
+    method @FlaggedApi("android.os.parcel_marshall_bytebuffer") public void unmarshall(@NonNull java.nio.ByteBuffer);
     method public void writeArray(@Nullable Object[]);
     method public void writeBinderArray(@Nullable android.os.IBinder[]);
     method public void writeBinderList(@Nullable java.util.List<android.os.IBinder>);
@@ -46681,16 +46683,16 @@
     method public int getLastCauseCode();
     method @Nullable public android.net.LinkProperties getLinkProperties();
     method public int getNetworkType();
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
+    method public int getNetworkValidationStatus();
     method public int getState();
     method public int getTransportType();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
-    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_FAILURE = 4; // 0x4
-    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_IN_PROGRESS = 2; // 0x2
-    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1; // 0x1
-    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_SUCCESS = 3; // 0x3
-    field @FlaggedApi("com.android.internal.telephony.flags.network_validation") public static final int NETWORK_VALIDATION_UNSUPPORTED = 0; // 0x0
+    field public static final int NETWORK_VALIDATION_FAILURE = 4; // 0x4
+    field public static final int NETWORK_VALIDATION_IN_PROGRESS = 2; // 0x2
+    field public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1; // 0x1
+    field public static final int NETWORK_VALIDATION_SUCCESS = 3; // 0x3
+    field public static final int NETWORK_VALIDATION_UNSUPPORTED = 0; // 0x0
   }
 
   public final class RadioAccessSpecifier implements android.os.Parcelable {
@@ -55423,6 +55425,7 @@
     method public void dispatchOnDraw();
     method public void dispatchOnGlobalLayout();
     method public boolean dispatchOnPreDraw();
+    method @FlaggedApi("android.view.flags.enable_dispatch_on_scroll_changed") public void dispatchOnScrollChanged();
     method public boolean isAlive();
     method public void registerFrameCommitCallback(@NonNull Runnable);
     method @Deprecated public void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
@@ -58287,7 +58290,9 @@
   }
 
   public final class WindowInspector {
+    method @FlaggedApi("android.view.flags.root_view_changed_listener") public static void addGlobalWindowViewsListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.view.View>>);
     method @NonNull public static java.util.List<android.view.View> getGlobalWindowViews();
+    method @FlaggedApi("android.view.flags.root_view_changed_listener") public static void removeGlobalWindowViewsListener(@NonNull java.util.function.Consumer<java.util.List<android.view.View>>);
   }
 
 }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 984bc68..35720fd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2941,6 +2941,7 @@
 package android.app.supervision {
 
   @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") public class SupervisionManager {
+    method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public android.content.Intent createConfirmSupervisionCredentialsIntent();
     method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSupervisionEnabled();
   }
 
@@ -3642,11 +3643,26 @@
     method public int getDeviceId();
     method @NonNull public String getName();
     method public int getType();
+    method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public void sendAdditionalInfo(@NonNull android.companion.virtual.sensor.VirtualSensorAdditionalInfo);
     method public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensor> CREATOR;
   }
 
+  @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public final class VirtualSensorAdditionalInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getType();
+    method @NonNull public java.util.List<float[]> getValues();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorAdditionalInfo> CREATOR;
+  }
+
+  public static final class VirtualSensorAdditionalInfo.Builder {
+    ctor public VirtualSensorAdditionalInfo.Builder(int);
+    method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo.Builder addValues(@NonNull float[]);
+    method @NonNull public android.companion.virtual.sensor.VirtualSensorAdditionalInfo build();
+  }
+
   public interface VirtualSensorCallback {
     method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration);
   }
@@ -3664,6 +3680,7 @@
     method public float getResolution();
     method public int getType();
     method @Nullable public String getVendor();
+    method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") public boolean isAdditionalInfoSupported();
     method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public boolean isWakeUpSensor();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorConfig> CREATOR;
@@ -3672,6 +3689,7 @@
   public static final class VirtualSensorConfig.Builder {
     ctor public VirtualSensorConfig.Builder(@IntRange(from=1) int, @NonNull String);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
+    method @FlaggedApi("android.companion.virtualdevice.flags.virtual_sensor_additional_info") @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setAdditionalInfoSupported(boolean);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setMaxDelay(int);
@@ -11725,6 +11743,7 @@
     field public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST";
     field public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
     field public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
+    field @FlaggedApi("android.multiuser.allow_supervising_profile") public static final String USER_TYPE_PROFILE_SUPERVISING = "android.os.usertype.profile.SUPERVISING";
     field public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
   }
 
@@ -16441,7 +16460,7 @@
     method @Deprecated public int getMtu();
     method public int getMtuV4();
     method public int getMtuV6();
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public int getNetworkValidationStatus();
+    method public int getNetworkValidationStatus();
     method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses();
     method public int getPduSessionId();
     method public int getProtocolType();
@@ -16478,7 +16497,7 @@
     method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") @NonNull public android.telephony.data.DataCallResponse.Builder setNetworkValidationStatus(int);
+    method @NonNull public android.telephony.data.DataCallResponse.Builder setNetworkValidationStatus(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
@@ -16558,7 +16577,7 @@
     method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
     method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
     method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
     method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
@@ -16620,7 +16639,7 @@
     method public final int getSlotIndex();
     method public void reportEmergencyDataNetworkPreferredTransportChanged(int);
     method public void reportThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ThrottleStatus>);
-    method @FlaggedApi("com.android.internal.telephony.flags.network_validation") public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method public void requestNetworkValidation(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d651010..4c82839 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1199,6 +1199,7 @@
     method public boolean isProfile();
     method public boolean isQuietModeEnabled();
     method public boolean isRestricted();
+    method @FlaggedApi("android.multiuser.allow_supervising_profile") public boolean isSupervisingProfile();
     method public boolean supportsSwitchTo();
     method @Deprecated public boolean supportsSwitchToByUser();
     method public void writeToParcel(android.os.Parcel, int);
@@ -4506,7 +4507,7 @@
     method @NonNull public android.window.WindowContainerTransaction requestFocusOnTaskFragment(@NonNull android.os.IBinder);
     method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
     method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
-    method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken);
+    method @Deprecated @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken);
     method @NonNull public android.window.WindowContainerTransaction setAdjacentTaskFragments(@NonNull android.os.IBinder, @NonNull android.os.IBinder, @Nullable android.window.WindowContainerTransaction.TaskFragmentAdjacentParams);
     method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
     method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
diff --git a/core/java/android/animation/AnimationHandler.java b/core/java/android/animation/AnimationHandler.java
index d84a4c1..9f78932 100644
--- a/core/java/android/animation/AnimationHandler.java
+++ b/core/java/android/animation/AnimationHandler.java
@@ -110,7 +110,7 @@
         }
     };
 
-    public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
+    public static final ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
     private static AnimationHandler sTestHandler = null;
     private boolean mListDirty = false;
 
@@ -118,10 +118,12 @@
         if (sTestHandler != null) {
             return sTestHandler;
         }
-        if (sAnimatorHandler.get() == null) {
-            sAnimatorHandler.set(new AnimationHandler());
+        AnimationHandler animatorHandler = sAnimatorHandler.get();
+        if (animatorHandler == null) {
+            animatorHandler = new AnimationHandler();
+            sAnimatorHandler.set(animatorHandler);
         }
-        return sAnimatorHandler.get();
+        return animatorHandler;
     }
 
     /**
@@ -384,6 +386,12 @@
         });
     }
 
+    void removePendingEndAnimationCallback(Runnable notifyEndAnimation) {
+        if (mPendingEndAnimationListeners != null) {
+            mPendingEndAnimationListeners.remove(notifyEndAnimation);
+        }
+    }
+
     private void doAnimationFrame(long frameTime) {
         long currentTime = SystemClock.uptimeMillis();
         final int size = mAnimationCallbacks.size();
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 4bf87f91..e62cd556a 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -82,6 +82,12 @@
     static boolean sPostNotifyEndListenerEnabled;
 
     /**
+     * If {@link #sPostNotifyEndListenerEnabled} is enabled, it will be set when the end callback
+     * is scheduled. It is cleared when it runs or finishes immediately, e.g. cancel.
+     */
+    private Runnable mPendingEndCallback;
+
+    /**
      * A cache of the values in a list. Used so that when calling the list, we have a copy
      * of it in case the list is modified while iterating. The array can be reused to avoid
      * allocation on every notification.
@@ -660,10 +666,33 @@
         }
     }
 
+    /**
+     * This is called when the animator needs to finish immediately. This is usually no-op unless
+     * {@link #sPostNotifyEndListenerEnabled} is enabled and a finish request calls around the last
+     * animation frame.
+     *
+     * @param notifyListeners Whether to invoke {@link AnimatorListener#onAnimationEnd}.
+     * @return {@code true} if the pending listeners are removed.
+     */
+    boolean consumePendingEndListeners(boolean notifyListeners) {
+        if (mPendingEndCallback == null) {
+            return false;
+        }
+        AnimationHandler.getInstance().removePendingEndAnimationCallback(mPendingEndCallback);
+        mPendingEndCallback = null;
+        if (notifyListeners) {
+            notifyEndListeners(false /* isReversing */);
+        }
+        return true;
+    }
+
     void notifyEndListenersFromEndAnimation(boolean isReversing, boolean postNotifyEndListener) {
         if (postNotifyEndListener) {
-            AnimationHandler.getInstance().postEndAnimationCallback(
-                    () -> completeEndAnimation(isReversing, "postNotifyAnimEnd"));
+            mPendingEndCallback = () -> {
+                completeEndAnimation(isReversing, "postNotifyAnimEnd");
+                mPendingEndCallback = null;
+            };
+            AnimationHandler.getInstance().postEndAnimationCallback(mPendingEndCallback);
         } else {
             completeEndAnimation(isReversing, "notifyAnimEnd");
         }
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 78566d2..36308e5 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -423,10 +423,21 @@
             notifyListeners(AnimatorCaller.ON_CANCEL, false);
             callOnPlayingSet(Animator::cancel);
             mPlayingSet.clear();
-            endAnimation();
+            endAnimationAndNotifyEndListenersImmediately();
         }
     }
 
+    private void endAnimationAndNotifyEndListenersImmediately() {
+        // If the end callback is pending, invoke the end callbacks of the animator nodes before
+        // ending this set. Pass notifyListeners=false because endAnimation will do that.
+        if (consumePendingEndListeners(false /* notifyListeners */)) {
+            for (int i = mNodeMap.size() - 1; i >= 0; i--) {
+                mNodeMap.keyAt(i).consumePendingEndListeners(true /* notifyListeners */);
+            }
+        }
+        endAnimation();
+    }
+
     /**
      * Calls consumer on every Animator of mPlayingSet.
      *
@@ -522,7 +533,7 @@
                 }
             }
         }
-        endAnimation();
+        endAnimationAndNotifyEndListenersImmediately();
     }
 
     /**
@@ -1448,8 +1459,6 @@
     private void endAnimation(boolean fromLastFrame) {
         final boolean postNotifyEndListener = sPostNotifyEndListenerEnabled && mListeners != null
                 && fromLastFrame && mTotalDuration > 0;
-        mStarted = false;
-        mLastFrameTime = -1;
         mFirstFrame = -1;
         mLastEventId = -1;
         mPaused = false;
@@ -1459,11 +1468,18 @@
 
         // No longer receive callbacks
         removeAnimationCallback();
+        // If postNotifyEndListener is false (most cases), then it is the same as calling
+        // completeEndAnimation directly.
         notifyEndListenersFromEndAnimation(mReversing, postNotifyEndListener);
     }
 
     @Override
     void completeEndAnimation(boolean isReversing, String notifyListenerTraceName) {
+        // The mStarted and mLastFrameTime are reset here because isStarted() and isRunning()
+        // can be true before notifying the end listeners. When notifying the end listeners,
+        // isStarted() and isRunning() should be false.
+        mStarted = false;
+        mLastFrameTime = -1;
         super.completeEndAnimation(isReversing, notifyListenerTraceName);
         removeAnimationEndListener();
         mSelfPulse = true;
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 492c2ff..8d34090 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1182,6 +1182,7 @@
         // If end has already been requested, through a previous end() or cancel() call, no-op
         // until animation starts again.
         if (mAnimationEndRequested) {
+            consumePendingEndListeners(true /* notifyListeners */);
             return;
         }
 
@@ -1211,6 +1212,10 @@
             initAnimation();
         }
         animateValue(shouldPlayBackward(mRepeatCount, mReversing) ? 0f : 1f);
+        if (mAnimationEndRequested) {
+            consumePendingEndListeners(true /* notifyListeners */);
+            return;
+        }
         endAnimation();
     }
 
@@ -1307,8 +1312,8 @@
         mLastFrameTime = -1;
         mFirstFrameTime = -1;
         mStartTime = -1;
-        mRunning = false;
-        mStarted = false;
+        // If postNotifyEndListener is false (most cases), then it is the same as calling
+        // completeEndAnimation directly.
         notifyEndListenersFromEndAnimation(mReversing, postNotifyEndListener);
         if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
             Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
@@ -1318,6 +1323,11 @@
 
     @Override
     void completeEndAnimation(boolean isReversing, String notifyListenerTraceName) {
+        // The mRunning and mStarted are reset here because isStarted() and isRunning()
+        // can be true before notifying the end listeners. When notifying the end listeners,
+        // isStarted() and isRunning() should be false.
+        mRunning = false;
+        mStarted = false;
         super.completeEndAnimation(isReversing, notifyListenerTraceName);
         // mReversing needs to be reset *after* notifying the listeners for the end callbacks.
         mReversing = false;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b38f5da..62816a2 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -55,9 +55,7 @@
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.graphics.drawable.Icon;
-import android.hardware.HardwareBuffer;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Build;
@@ -86,7 +84,6 @@
 import android.util.Singleton;
 import android.util.Size;
 import android.view.WindowInsetsController.Appearance;
-import android.window.TaskSnapshot;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.LocalePicker;
@@ -3102,7 +3099,8 @@
     /**
      * Flag for {@link #moveTaskToFront(int, int)}: also move the "home"
      * activity along with the task, so it is positioned immediately behind
-     * the task.
+     * the task. This flag is ignored if the task's windowing mode is
+     * {@link WindowConfiguration#WINDOWING_MODE_MULTI_WINDOW}.
      */
     public static final int MOVE_TASK_WITH_HOME = 0x00000001;
 
@@ -5420,10 +5418,11 @@
      *
      * @hide
      */
+    @Nullable
     @RequiresPermission(Manifest.permission.MANAGE_USERS)
-    public @Nullable String getSwitchingFromUserMessage() {
+    public String getSwitchingFromUserMessage(@UserIdInt int userId) {
         try {
-            return getService().getSwitchingFromUserMessage();
+            return getService().getSwitchingFromUserMessage(userId);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -5434,10 +5433,11 @@
      *
      * @hide
      */
+    @Nullable
     @RequiresPermission(Manifest.permission.MANAGE_USERS)
-    public @Nullable String getSwitchingToUserMessage() {
+    public String getSwitchingToUserMessage(@UserIdInt int userId) {
         try {
-            return getService().getSwitchingToUserMessage();
+            return getService().getSwitchingToUserMessage(userId);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index a12c067..e5f7889 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -292,14 +292,14 @@
     public abstract boolean canStartMoreUsers();
 
     /**
-     * Sets the user switcher message for switching from {@link android.os.UserHandle#SYSTEM}.
+     * Sets the user switcher message for switching from a user.
      */
-    public abstract void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage);
+    public abstract void setSwitchingFromUserMessage(@UserIdInt int user, @Nullable String message);
 
     /**
-     * Sets the user switcher message for switching to {@link android.os.UserHandle#SYSTEM}.
+     * Sets the user switcher message for switching to a user.
      */
-    public abstract void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage);
+    public abstract void setSwitchingToUserMessage(@UserIdInt int user, @Nullable String message);
 
     /**
      * Returns maximum number of users that can run simultaneously.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2c1df73..96b5096 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -273,6 +273,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
+import java.lang.reflect.Executable;
 import java.lang.reflect.Method;
 import java.net.InetAddress;
 import java.nio.file.DirectoryStream;
@@ -2268,10 +2269,16 @@
         public void getExecutableMethodFileOffsets(
                 @NonNull MethodDescriptor methodDescriptor,
                 @NonNull IOffsetCallback resultCallback) {
-            Method method = MethodDescriptorParser.parseMethodDescriptor(
+            Executable executable = MethodDescriptorParser.parseMethodDescriptor(
                     getClass().getClassLoader(), methodDescriptor);
-            VMDebug.ExecutableMethodFileOffsets location =
-                    VMDebug.getExecutableMethodFileOffsets(method);
+            VMDebug.ExecutableMethodFileOffsets location;
+            if (com.android.art.flags.Flags.executableMethodFileOffsetsV2()) {
+                location = VMDebug.getExecutableMethodFileOffsets(executable);
+            } else if (executable instanceof Method) {
+                location = VMDebug.getExecutableMethodFileOffsets((Method) executable);
+            } else {
+                throw new UnsupportedOperationException();
+            }
             try {
                 if (location == null) {
                     resultCallback.onResult(null);
@@ -3995,6 +4002,10 @@
                         + (fromIpc ? " (from ipc" : ""));
             }
         }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                    "updateProcessState: processState=" + processState);
+        }
     }
 
     /** Converts a process state to a VM process state. */
@@ -7820,9 +7831,10 @@
 
         // Register callback to report native memory metrics post GC cleanup
         // Note: we do not report memory metrics of isolated processes unless
-        // their native allocations become more significant
-        if (!Process.isIsolated() && Flags.reportPostgcMemoryMetrics() &&
-            com.android.libcore.readonly.Flags.postCleanupApis()) {
+        // their native allocations become more significant. Instrumentation is
+        // also excluded because the metrics from test cases are not meaningful.
+        if (!Process.isIsolated() && ii == null && Flags.reportPostgcMemoryMetrics()
+                && com.android.libcore.readonly.Flags.postCleanupApis()) {
             VMRuntime.addPostCleanupCallback(new Runnable() {
                 @Override public void run() {
                     MetricsLoggerWrapper.logPostGcMemorySnapshot();
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 599f1a8..3fd9d8b 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -102,6 +102,10 @@
     private static final int FLAG_FULLSCREEN_OVERRIDE_USER = FLAG_BASE << 8;
     /** Top activity flag for whether min aspect ratio of the activity has been overridden.*/
     public static final int FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE = FLAG_BASE << 9;
+    /** Top activity flag for whether restart menu is shown due to display move. */
+    private static final int FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE = FLAG_BASE << 10;
+    /** Top activity flag for whether activity opted out of edge to edge. */
+    public static final int FLAG_OPT_OUT_EDGE_TO_EDGE = FLAG_BASE << 11;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, value = {
@@ -115,7 +119,9 @@
             FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON,
             FLAG_FULLSCREEN_OVERRIDE_SYSTEM,
             FLAG_FULLSCREEN_OVERRIDE_USER,
-            FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE
+            FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE,
+            FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE,
+            FLAG_OPT_OUT_EDGE_TO_EDGE
     })
     public @interface TopActivityFlag {}
 
@@ -129,11 +135,13 @@
     @TopActivityFlag
     private static final int FLAGS_ORGANIZER_INTERESTED = FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP
             | FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON | FLAG_FULLSCREEN_OVERRIDE_SYSTEM
-            | FLAG_FULLSCREEN_OVERRIDE_USER | FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE;
+            | FLAG_FULLSCREEN_OVERRIDE_USER | FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE
+            | FLAG_OPT_OUT_EDGE_TO_EDGE;
 
     @TopActivityFlag
     private static final int FLAGS_COMPAT_UI_INTERESTED = FLAGS_ORGANIZER_INTERESTED
-            | FLAG_IN_SIZE_COMPAT | FLAG_ELIGIBLE_FOR_LETTERBOX_EDU | FLAG_LETTERBOX_EDU_ENABLED;
+            | FLAG_IN_SIZE_COMPAT | FLAG_ELIGIBLE_FOR_LETTERBOX_EDU | FLAG_LETTERBOX_EDU_ENABLED
+            | FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE;
 
     private AppCompatTaskInfo() {
         // Do nothing
@@ -300,6 +308,21 @@
     }
 
     /**
+     * @return {@code true} if the restart menu is enabled for the top activity due to display move.
+     */
+    public boolean isRestartMenuEnabledForDisplayMove() {
+        return isTopActivityFlagEnabled(FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE);
+    }
+
+    /**
+     * Sets the top activity flag for whether the restart menu is enabled for the top activity due
+     * to display move.
+     */
+    public void setRestartMenuEnabledForDisplayMove(boolean enable) {
+        setTopActivityFlag(FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE, enable);
+    }
+
+    /**
      * @return {@code true} if the top activity bounds are letterboxed.
      */
     public boolean isTopActivityLetterboxed() {
@@ -328,6 +351,20 @@
         setTopActivityFlag(FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE, enable);
     }
 
+    /**
+     * Sets the top activity flag for whether the activity has opted out of edge to edge.
+     */
+    public void setOptOutEdgeToEdge(boolean enable) {
+        setTopActivityFlag(FLAG_OPT_OUT_EDGE_TO_EDGE, enable);
+    }
+
+    /**
+     * @return {@code true} if the top activity has opted out of edge to edge.
+     */
+    public boolean hasOptOutEdgeToEdge() {
+        return isTopActivityFlagEnabled(FLAG_OPT_OUT_EDGE_TO_EDGE);
+    }
+
     /** Clear all top activity flags and set to false. */
     public void clearTopActivityFlags() {
         mTopActivityFlags = FLAG_UNDEFINED;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 248f191..1864d4a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -18,7 +18,6 @@
 
 
 import static android.location.flags.Flags.FLAG_LOCATION_BYPASS;
-import static android.media.audio.Flags.roForegroundAudioControl;
 import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER;
 import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
 import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
@@ -3481,6 +3480,16 @@
     }
 
     /**
+     * Whether an app op is backed by a runtime permission or not.
+     * @hide
+     */
+    public static boolean opIsRuntimePermission(int op) {
+        if (op == OP_NONE) return false;
+
+        return ArrayUtils.contains(RUNTIME_PERMISSION_OPS, op);
+    }
+
+    /**
      * Retrieve the user restriction associated with an operation, or null if there is not one.
      * @hide
      */
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1ed64f9..bdecbae 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -200,6 +200,8 @@
     @GuardedBy("mPackageMonitorCallbacks")
     private final ArraySet<IRemoteCallback> mPackageMonitorCallbacks = new ArraySet<>();
 
+    private final boolean mUseSystemFeaturesCache;
+
     UserManager getUserManager() {
         if (mUserManager == null) {
             mUserManager = UserManager.get(mContext);
@@ -300,13 +302,23 @@
 
     @Override
     public Intent getLaunchIntentForPackage(String packageName) {
+        return getLaunchIntentForPackage(packageName, false);
+    }
+
+    @Override
+    @Nullable
+    public Intent getLaunchIntentForPackage(@NonNull String packageName,
+            boolean includeDirectBootUnaware) {
+        ResolveInfoFlags queryFlags = ResolveInfoFlags.of(
+                includeDirectBootUnaware ? MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE : 0);
+
         // First see if the package has an INFO activity; the existence of
         // such an activity is implied to be the desired front-door for the
         // overall package (such as if it has multiple launcher entries).
         Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
         intentToResolve.addCategory(Intent.CATEGORY_INFO);
         intentToResolve.setPackage(packageName);
-        List<ResolveInfo> ris = queryIntentActivities(intentToResolve, 0);
+        List<ResolveInfo> ris = queryIntentActivities(intentToResolve, queryFlags);
 
         // Otherwise, try to find a main launcher activity.
         if (ris == null || ris.size() <= 0) {
@@ -314,7 +326,7 @@
             intentToResolve.removeCategory(Intent.CATEGORY_INFO);
             intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
             intentToResolve.setPackage(packageName);
-            ris = queryIntentActivities(intentToResolve, 0);
+            ris = queryIntentActivities(intentToResolve, queryFlags);
         }
         if (ris == null || ris.size() <= 0) {
             return null;
@@ -824,8 +836,7 @@
         if (maybeHasSystemFeature != null) {
             return maybeHasSystemFeature;
         }
-        if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()
-                && android.content.pm.Flags.cacheSdkSystemFeatures()) {
+        if (mUseSystemFeaturesCache) {
             maybeHasSystemFeature =
                     SystemFeaturesCache.getInstance().maybeHasFeature(name, version);
             if (maybeHasSystemFeature != null) {
@@ -2221,6 +2232,25 @@
     protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) {
         mContext = context;
         mPM = pm;
+        mUseSystemFeaturesCache = isSystemFeaturesCacheEnabledAndAvailable();
+    }
+
+    private static boolean isSystemFeaturesCacheEnabledAndAvailable() {
+        if (!android.content.pm.Flags.cacheSdkSystemFeatures()) {
+            return false;
+        }
+        if (!com.android.internal.os.Flags.applicationSharedMemoryEnabled()) {
+            return false;
+        }
+        if (ActivityThread.isSystem() && !SystemFeaturesCache.hasInstance()) {
+            // There are a handful of utility "system" processes that are neither system_server nor
+            // bound as applications. For these processes, we don't have access to application
+            // shared memory or the dependent system features cache.
+            // TODO(b/400713460): Revisit this exception after deprecating these command-like
+            // system processes.
+            return false;
+        }
+        return true;
     }
 
     /**
diff --git a/core/java/android/app/AutomaticZenRule.aidl b/core/java/android/app/AutomaticZenRule.aidl
index feb21d6..92f7d52 100644
--- a/core/java/android/app/AutomaticZenRule.aidl
+++ b/core/java/android/app/AutomaticZenRule.aidl
@@ -16,4 +16,6 @@
 
 package android.app;
 
-parcelable AutomaticZenRule;
\ No newline at end of file
+parcelable AutomaticZenRule;
+
+parcelable AutomaticZenRule.AzrWithId;
\ No newline at end of file
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 2daa52b..1ce38ac 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -843,4 +843,41 @@
             return rule;
         }
     }
+
+    /** @hide */
+    public static final class AzrWithId implements Parcelable {
+        public final String mId;
+        public final AutomaticZenRule mRule;
+
+        public AzrWithId(String id, AutomaticZenRule rule) {
+            mId = id;
+            mRule = rule;
+        }
+
+        public static final Creator<AzrWithId> CREATOR = new Creator<>() {
+            @Override
+            public AzrWithId createFromParcel(Parcel in) {
+                return new AzrWithId(
+                        in.readString8(),
+                        in.readParcelable(AutomaticZenRule.class.getClassLoader(),
+                                AutomaticZenRule.class));
+            }
+
+            @Override
+            public AzrWithId[] newArray(int size) {
+                return new AzrWithId[size];
+            }
+        };
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeString8(mId);
+            dest.writeParcelable(mRule, flags);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7e5c0fb..99a2763 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2473,11 +2473,9 @@
     @Override
     public int getPermissionRequestState(String permission) {
         Objects.requireNonNull(permission, "Permission name can't be null");
-        int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
-                permission);
         PermissionManager permissionManager = getSystemService(PermissionManager.class);
         return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
-                deviceId);
+                getDeviceId());
     }
 
     @Override
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index ad01ad5..6cdfb97 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -403,8 +403,8 @@
     void setPackageScreenCompatMode(in String packageName, int mode);
     @UnsupportedAppUsage
     boolean switchUser(int userid);
-    String getSwitchingFromUserMessage();
-    String getSwitchingToUserMessage();
+    String getSwitchingFromUserMessage(int userId);
+    String getSwitchingToUserMessage(int userId);
     @UnsupportedAppUsage
     void setStopUserOnSwitch(int value);
     boolean removeTask(int taskId);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 00df724..1f0cd39 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -224,7 +224,7 @@
     void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted);
     ZenPolicy getDefaultZenPolicy();
     AutomaticZenRule getAutomaticZenRule(String id);
-    Map<String, AutomaticZenRule> getAutomaticZenRules();
+    ParceledListSlice getAutomaticZenRules();
     String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg, boolean fromUser);
     boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule, boolean fromUser);
     boolean removeAutomaticZenRule(String id, boolean fromUser);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 8af5b1b..19fecb9 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -101,14 +101,20 @@
      */
     public static final String REPORT_KEY_STREAMRESULT = "stream";
 
-    static final String TAG = "Instrumentation";
+    /**
+     * @hide
+     */
+    public static final String TAG = "Instrumentation";
 
     private static final long CONNECT_TIMEOUT_MILLIS = 60_000;
 
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
-    // If set, will print the stack trace for activity starts within the process
-    static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
+    /**
+     * If set, will print the stack trace for activity starts within the process
+     * @hide
+     */
+    public static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
             SystemProperties.getBoolean("persist.wm.debug.start_activity", false);
     static final boolean DEBUG_FINISH_ACTIVITY = Build.IS_DEBUGGABLE &&
             SystemProperties.getBoolean("persist.wm.debug.finish_activity", false);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index cc72d8f..7c293cb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3266,7 +3266,6 @@
         final Class<? extends Style> notificationStyle = getNotificationStyle();
 
         return notificationStyle == null
-                || BigPictureStyle.class.equals(notificationStyle)
                 || BigTextStyle.class.equals(notificationStyle)
                 || CallStyle.class.equals(notificationStyle)
                 || ProgressStyle.class.equals(notificationStyle);
@@ -6144,6 +6143,20 @@
                 result.mTitleMarginSet.applyToView(contentView, p.mTextViewId);
                 contentView.setInt(p.mTextViewId, "setNumIndentLines", p.hasTitle() ? 0 : 1);
             }
+            // The expand button uses paddings rather than margins, so we'll adjust it
+            // separately.
+            adjustExpandButtonPadding(contentView, result.mRightIconVisible);
+        }
+
+        private void adjustExpandButtonPadding(RemoteViews contentView, boolean rightIconVisible) {
+            if (notificationsRedesignTemplates()) {
+                final Resources res = mContext.getResources();
+                int normalPadding = res.getDimensionPixelSize(R.dimen.notification_2025_margin);
+                int iconSpacing = res.getDimensionPixelSize(
+                        R.dimen.notification_2025_expand_button_right_icon_spacing);
+                contentView.setInt(R.id.expand_button, "setStartPadding",
+                        rightIconVisible ? iconSpacing : normalPadding);
+            }
         }
 
         // This code is executed on behalf of other apps' notifications, sometimes even by 3p apps,
@@ -6155,12 +6168,21 @@
                 @NonNull TemplateBindResult result) {
             final Resources resources = mContext.getResources();
             final float density = resources.getDisplayMetrics().density;
-            final float iconMarginDp = resources.getDimension(
-                    R.dimen.notification_right_icon_content_margin) / density;
+            int iconMarginId = notificationsRedesignTemplates()
+                    ? R.dimen.notification_2025_right_icon_content_margin
+                    : R.dimen.notification_right_icon_content_margin;
+            final float iconMarginDp = resources.getDimension(iconMarginId) / density;
             final float contentMarginDp = resources.getDimension(
                     R.dimen.notification_content_margin_end) / density;
-            final float expanderSizeDp = resources.getDimension(
-                    R.dimen.notification_header_expand_icon_size) / density - contentMarginDp;
+            float spaceForExpanderDp;
+            if (notificationsRedesignTemplates()) {
+                spaceForExpanderDp = resources.getDimension(
+                        R.dimen.notification_2025_right_icon_expanded_margin_end) / density
+                        - contentMarginDp;
+            } else {
+                spaceForExpanderDp = resources.getDimension(
+                        R.dimen.notification_header_expand_icon_size) / density - contentMarginDp;
+            }
             final float viewHeightDp = resources.getDimension(
                     R.dimen.notification_right_icon_size) / density;
             float viewWidthDp = viewHeightDp;  // icons are 1:1 by default
@@ -6177,9 +6199,10 @@
                     }
                 }
             }
+            // Margin needed for the header to accommodate the icon when shown
             final float extraMarginEndDpIfVisible = viewWidthDp + iconMarginDp;
             result.setRightIconState(rightIcon != null /* visible */, viewWidthDp,
-                    viewHeightDp, extraMarginEndDpIfVisible, expanderSizeDp);
+                    viewHeightDp, extraMarginEndDpIfVisible, spaceForExpanderDp);
         }
 
         /**
@@ -6430,45 +6453,49 @@
             return mN.showsTime() || mN.showsChronometer();
         }
 
-        private void resetStandardTemplateWithActions(RemoteViews big) {
+        private void resetStandardTemplateWithActions(RemoteViews contentView) {
             // actions_container is only reset when there are no actions to avoid focus issues with
             // remote inputs.
-            big.setViewVisibility(R.id.actions, View.GONE);
-            big.removeAllViews(R.id.actions);
+            contentView.setViewVisibility(R.id.actions, View.GONE);
+            contentView.removeAllViews(R.id.actions);
 
-            big.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
-            big.setTextViewText(R.id.notification_material_reply_text_1, null);
-            big.setViewVisibility(R.id.notification_material_reply_text_1_container, View.GONE);
-            big.setViewVisibility(R.id.notification_material_reply_progress, View.GONE);
+            contentView.setViewVisibility(R.id.notification_material_reply_container, View.GONE);
+            contentView.setTextViewText(R.id.notification_material_reply_text_1, null);
+            contentView.setViewVisibility(R.id.notification_material_reply_text_1_container,
+                    View.GONE);
+            contentView.setViewVisibility(R.id.notification_material_reply_progress, View.GONE);
 
-            big.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
-            big.setTextViewText(R.id.notification_material_reply_text_2, null);
-            big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
-            big.setTextViewText(R.id.notification_material_reply_text_3, null);
+            contentView.setViewVisibility(R.id.notification_material_reply_text_2, View.GONE);
+            contentView.setTextViewText(R.id.notification_material_reply_text_2, null);
+            contentView.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
+            contentView.setTextViewText(R.id.notification_material_reply_text_3, null);
 
-            // This may get erased by bindSnoozeAction
-            big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
-                    RemoteViews.MARGIN_BOTTOM, R.dimen.notification_content_margin);
+            if (!notificationsRedesignTemplates()) {
+                // This may get erased by bindSnoozeAction, or if we're showing the bubble icon
+                contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+                        RemoteViews.MARGIN_BOTTOM, R.dimen.notification_content_margin);
+            }
         }
 
-        private void bindSnoozeAction(RemoteViews big, StandardTemplateParams p) {
+        private boolean bindSnoozeAction(RemoteViews contentView, StandardTemplateParams p) {
             boolean hideSnoozeButton = mN.isFgsOrUij()
                     || mN.fullScreenIntent != null
                     || isBackgroundColorized(p)
                     || p.mViewType != StandardTemplateParams.VIEW_TYPE_EXPANDED;
-            big.setBoolean(R.id.snooze_button, "setEnabled", !hideSnoozeButton);
+            contentView.setBoolean(R.id.snooze_button, "setEnabled", !hideSnoozeButton);
             if (hideSnoozeButton) {
                 // Only hide; NotificationContentView will show it when it adds the click listener
-                big.setViewVisibility(R.id.snooze_button, View.GONE);
+                contentView.setViewVisibility(R.id.snooze_button, View.GONE);
             }
 
             final boolean snoozeEnabled = !hideSnoozeButton
                     && mContext.getContentResolver() != null
                     && isSnoozeSettingEnabled();
-            if (snoozeEnabled) {
-                big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+            if (!notificationsRedesignTemplates() && snoozeEnabled) {
+                contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
                         RemoteViews.MARGIN_BOTTOM, 0);
             }
+            return snoozeEnabled;
         }
 
         private boolean isSnoozeSettingEnabled() {
@@ -6503,16 +6530,14 @@
 
         private RemoteViews applyStandardTemplateWithActions(int layoutId,
                 StandardTemplateParams p, TemplateBindResult result) {
-            RemoteViews big = applyStandardTemplate(layoutId, p, result);
+            RemoteViews contentView = applyStandardTemplate(layoutId, p, result);
 
-            resetStandardTemplateWithActions(big);
-            bindSnoozeAction(big, p);
+            resetStandardTemplateWithActions(contentView);
+            boolean snoozeEnabled = bindSnoozeAction(contentView, p);
             // color the snooze and bubble actions with the theme color
             ColorStateList actionColor = ColorStateList.valueOf(getStandardActionColor(p));
-            big.setColorStateList(R.id.snooze_button, "setImageTintList", actionColor);
-            big.setColorStateList(R.id.bubble_button, "setImageTintList", actionColor);
-
-            boolean validRemoteInput = false;
+            contentView.setColorStateList(R.id.snooze_button, "setImageTintList", actionColor);
+            contentView.setColorStateList(R.id.bubble_button, "setImageTintList", actionColor);
 
             // In the UI, contextual actions appear separately from the standard actions, so we
             // filter them out here.
@@ -6526,47 +6551,38 @@
             if (p.mCallStyleActions) {
                 // Clear view padding to allow buttons to start on the left edge.
                 // This must be done before 'setEmphasizedMode' which sets top/bottom margins.
-                big.setViewPadding(R.id.actions, 0, 0, 0, 0);
+                contentView.setViewPadding(R.id.actions, 0, 0, 0, 0);
                 if (!Flags.notificationsRedesignTemplates()) {
                     // Add an optional indent that will make buttons start at the correct column
                     // when there is enough space to do so (and fall back to the left edge if not).
                     // This is handled directly in NotificationActionListLayout in the new design.
-                    big.setInt(R.id.actions, "setCollapsibleIndentDimen",
+                    contentView.setInt(R.id.actions, "setCollapsibleIndentDimen",
                             R.dimen.call_notification_collapsible_indent);
                 }
                 if (evenlyDividedCallStyleActionLayout()) {
                     if (CallStyle.DEBUG_NEW_ACTION_LAYOUT) {
                         Log.d(TAG, "setting evenly divided mode on action list");
                     }
-                    big.setBoolean(R.id.actions, "setEvenlyDividedMode", true);
+                    contentView.setBoolean(R.id.actions, "setEvenlyDividedMode", true);
                 }
             }
-            big.setBoolean(R.id.actions, "setEmphasizedMode", emphasizedMode);
+            if (!notificationsRedesignTemplates()) {
+                contentView.setBoolean(R.id.actions, "setEmphasizedMode", emphasizedMode);
+            }
+
+            boolean validRemoteInput = false;
+            // With the new design, the actions_container should always be visible to act as padding
+            // when there are no actions. We're making its child GONE instead.
+            int actionsContainerForVisibilityChange = notificationsRedesignTemplates()
+                    ? R.id.actions_container_layout : R.id.actions_container;
             if (numActions > 0 && !p.mHideActions) {
-                big.setViewVisibility(R.id.actions_container, View.VISIBLE);
-                big.setViewVisibility(R.id.actions, View.VISIBLE);
-                big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
-                        RemoteViews.MARGIN_BOTTOM, 0);
-                for (int i = 0; i < numActions; i++) {
-                    Action action = nonContextualActions.get(i);
-
-                    boolean actionHasValidInput = hasValidRemoteInput(action);
-                    validRemoteInput |= actionHasValidInput;
-
-                    final RemoteViews button = generateActionButton(action, emphasizedMode, p);
-                    if (actionHasValidInput && !emphasizedMode) {
-                        // Clear the drawable
-                        button.setInt(R.id.action0, "setBackgroundResource", 0);
-                    }
-                    if (emphasizedMode && i > 0) {
-                        // Clear start margin from non-first buttons to reduce the gap between them.
-                        //  (8dp remaining gap is from all buttons' standard 4dp inset).
-                        button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
-                    }
-                    big.addView(R.id.actions, button);
-                }
+                contentView.setViewVisibility(actionsContainerForVisibilityChange, View.VISIBLE);
+                contentView.setViewVisibility(R.id.actions, View.VISIBLE);
+                updateMarginsForActions(contentView, emphasizedMode);
+                validRemoteInput = populateActionsContainer(contentView, p, nonContextualActions,
+                        numActions, emphasizedMode);
             } else {
-                big.setViewVisibility(R.id.actions_container, View.GONE);
+                contentView.setViewVisibility(actionsContainerForVisibilityChange, View.GONE);
             }
 
             RemoteInputHistoryItem[] replyText = getParcelableArrayFromBundle(
@@ -6575,37 +6591,89 @@
                     && !TextUtils.isEmpty(replyText[0].getText())
                     && p.maxRemoteInputHistory > 0) {
                 boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER);
-                big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
-                big.setViewVisibility(R.id.notification_material_reply_text_1_container,
+                contentView.setViewVisibility(R.id.notification_material_reply_container,
                         View.VISIBLE);
-                big.setTextViewText(R.id.notification_material_reply_text_1,
+                contentView.setViewVisibility(R.id.notification_material_reply_text_1_container,
+                        View.VISIBLE);
+                contentView.setTextViewText(R.id.notification_material_reply_text_1,
                         ensureColorSpanContrastOrStripStyling(replyText[0].getText(), p));
-                setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p);
-                big.setViewVisibility(R.id.notification_material_reply_progress,
+                setTextViewColorSecondary(contentView, R.id.notification_material_reply_text_1, p);
+                contentView.setViewVisibility(R.id.notification_material_reply_progress,
                         showSpinner ? View.VISIBLE : View.GONE);
-                big.setProgressIndeterminateTintList(
+                contentView.setProgressIndeterminateTintList(
                         R.id.notification_material_reply_progress,
                         ColorStateList.valueOf(getPrimaryAccentColor(p)));
 
                 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1].getText())
                         && p.maxRemoteInputHistory > 1) {
-                    big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
-                    big.setTextViewText(R.id.notification_material_reply_text_2,
+                    contentView.setViewVisibility(R.id.notification_material_reply_text_2,
+                            View.VISIBLE);
+                    contentView.setTextViewText(R.id.notification_material_reply_text_2,
                             ensureColorSpanContrastOrStripStyling(replyText[1].getText(), p));
-                    setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p);
+                    setTextViewColorSecondary(contentView, R.id.notification_material_reply_text_2,
+                            p);
 
                     if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2].getText())
                             && p.maxRemoteInputHistory > 2) {
-                        big.setViewVisibility(
+                        contentView.setViewVisibility(
                                 R.id.notification_material_reply_text_3, View.VISIBLE);
-                        big.setTextViewText(R.id.notification_material_reply_text_3,
+                        contentView.setTextViewText(R.id.notification_material_reply_text_3,
                                 ensureColorSpanContrastOrStripStyling(replyText[2].getText(), p));
-                        setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p);
+                        setTextViewColorSecondary(contentView,
+                                R.id.notification_material_reply_text_3, p);
                     }
                 }
             }
 
-            return big;
+            return contentView;
+        }
+
+        private void updateMarginsForActions(RemoteViews contentView, boolean emphasizedMode) {
+            if (notificationsRedesignTemplates()) {
+                if (emphasizedMode) {
+                    // Emphasized actions look similar to smart replies, so let's use the same
+                    // margins.
+                    contentView.setViewLayoutMarginDimen(R.id.actions_container,
+                            RemoteViews.MARGIN_TOP,
+                            R.dimen.notification_2025_smart_reply_container_margin);
+                    contentView.setViewLayoutMarginDimen(R.id.actions_container,
+                            RemoteViews.MARGIN_BOTTOM,
+                            R.dimen.notification_2025_smart_reply_container_margin);
+                } else {
+                    contentView.setViewLayoutMarginDimen(R.id.actions_container,
+                            RemoteViews.MARGIN_TOP, 0);
+                    contentView.setViewLayoutMarginDimen(R.id.actions_container,
+                            RemoteViews.MARGIN_BOTTOM,
+                            R.dimen.notification_2025_action_list_margin_bottom);
+                }
+            } else {
+                contentView.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+                        RemoteViews.MARGIN_BOTTOM, 0);
+            }
+        }
+
+        private boolean populateActionsContainer(RemoteViews contentView, StandardTemplateParams p,
+                List<Action> nonContextualActions, int numActions, boolean emphasizedMode) {
+            boolean validRemoteInput = false;
+            for (int i = 0; i < numActions; i++) {
+                Action action = nonContextualActions.get(i);
+
+                boolean actionHasValidInput = hasValidRemoteInput(action);
+                validRemoteInput |= actionHasValidInput;
+
+                final RemoteViews button = generateActionButton(action, emphasizedMode, p);
+                if (actionHasValidInput && !emphasizedMode) {
+                    // Clear the drawable
+                    button.setInt(R.id.action0, "setBackgroundResource", 0);
+                }
+                if (emphasizedMode && i > 0) {
+                    // Clear start margin from non-first buttons to reduce the gap between them.
+                    //  (8dp remaining gap is from all buttons' standard 4dp inset).
+                    button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
+                }
+                contentView.addView(R.id.actions, button);
+            }
+            return validRemoteInput;
         }
 
         /**
@@ -14659,13 +14727,19 @@
         public final MarginSet mTitleMarginSet = new MarginSet();
 
         public void setRightIconState(boolean visible, float widthDp, float heightDp,
-                float marginEndDpIfVisible, float expanderSizeDp) {
+                float marginEndDpIfVisible, float spaceForExpanderDp) {
             mRightIconVisible = visible;
             mRightIconWidthDp = widthDp;
             mRightIconHeightDp = heightDp;
-            mHeadingExtraMarginSet.setValues(0, marginEndDpIfVisible);
-            mHeadingFullMarginSet.setValues(expanderSizeDp, marginEndDpIfVisible + expanderSizeDp);
-            mTitleMarginSet.setValues(0, marginEndDpIfVisible + expanderSizeDp);
+            mHeadingExtraMarginSet.setValues(
+                    /* valueIfGone = */ 0,
+                    /* valueIfVisible = */ marginEndDpIfVisible);
+            mHeadingFullMarginSet.setValues(
+                    /* valueIfGone = */ spaceForExpanderDp,
+                    /* valueIfVisible = */ marginEndDpIfVisible + spaceForExpanderDp);
+            mTitleMarginSet.setValues(
+                    /* valueIfGone = */ 0,
+                    /* valueIfVisible = */ marginEndDpIfVisible + spaceForExpanderDp);
         }
 
         /**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 050ef23..69e3ef9 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1747,7 +1747,15 @@
     public Map<String, AutomaticZenRule> getAutomaticZenRules() {
         INotificationManager service = service();
         try {
-            return service.getAutomaticZenRules();
+            Map<String, AutomaticZenRule> result = new HashMap<>();
+            ParceledListSlice<AutomaticZenRule.AzrWithId> parceledRules =
+                    service.getAutomaticZenRules();
+            if (parceledRules != null) {
+                for (AutomaticZenRule.AzrWithId rule : parceledRules.getList()) {
+                    result.put(rule.mId, rule.mRule);
+                }
+            }
+            return result;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index afe915e..dd87d28 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -594,7 +594,7 @@
      * @see PictureInPictureParams.Builder#setSeamlessResizeEnabled(boolean)
      */
     public boolean isSeamlessResizeEnabled() {
-        return mSeamlessResizeEnabled == null ? true : mSeamlessResizeEnabled;
+        return mSeamlessResizeEnabled == null ? false : mSeamlessResizeEnabled;
     }
 
     /**
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 6936ddc..5924793 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -655,8 +655,10 @@
                 + " effectiveUid=" + effectiveUid
                 + " displayId=" + displayId
                 + " isRunning=" + isRunning
-                + " baseIntent=" + baseIntent + " baseActivity=" + baseActivity
-                + " topActivity=" + topActivity + " origActivity=" + origActivity
+                + " baseIntent=" + baseIntent
+                + " baseActivity=" + baseActivity
+                + " topActivity=" + topActivity
+                + " origActivity=" + origActivity
                 + " realActivity=" + realActivity
                 + " numActivities=" + numActivities
                 + " lastActiveTime=" + lastActiveTime
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index cb2b8ad..8a8877f 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -558,6 +558,11 @@
     }
 
     public void dump(Printer pw, String prefix) {
+        pw.println("mVisible: " + mVisible);
+        pw.println("mUsesPolicies: " + mUsesPolicies);
+        pw.println("mSupportsTransferOwnership: " + mSupportsTransferOwnership);
+        pw.println("mHeadlessDeviceOwnerMode: " + mHeadlessDeviceOwnerMode);
+
         pw.println(prefix + "Receiver:");
         mActivityInfo.dump(pw, prefix + "  ");
     }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 5359ba4..c528db8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -224,7 +224,7 @@
  * <li>A <i id="deviceowner">Device Owner</i>, which only ever exists on the
  * {@link UserManager#isSystemUser System User} or Main User, is
  * the most powerful type of Device Policy Controller and can affect policy across the device.
- * <li>A <i id="profileowner">Profile Owner<i>, which can exist on any user, can
+ * <li>A <i id="profileowner">Profile Owner</i>, which can exist on any user, can
  * affect policy on the user it is on, and when it is running on
  * {@link UserManager#isProfile a profile} has
  * <a href="#profile-on-parent">limited</a> ability to affect policy on its parent.
@@ -6981,6 +6981,8 @@
      * <p>The caller must hold the
      * {@link android.Manifest.permission#TRIGGER_LOST_MODE} permission.
      *
+     * <p>This API accesses the device's location and will only be used when a device is lost.
+     *
      * <p>Register a broadcast receiver to receive lost mode location updates. This receiver should
      * subscribe to the {@link #ACTION_LOST_MODE_LOCATION_UPDATE} action and receive the location
      * from an intent extra {@link #EXTRA_LOST_MODE_LOCATION}.
@@ -8269,6 +8271,7 @@
      *
      * @throws SecurityException if the caller is not a device owner, a profile owner or
      *         delegated certificate chooser.
+     * @throws IllegalArgumentException if {@code alias} does not correspond to an existing key
      * @see #grantKeyPairToWifiAuth
      */
     public boolean isKeyPairGrantedToWifiAuth(@NonNull String alias) {
diff --git a/core/java/android/app/admin/StringSetIntersection.java b/core/java/android/app/admin/StringSetIntersection.java
new file mode 100644
index 0000000..5f2031e
--- /dev/null
+++ b/core/java/android/app/admin/StringSetIntersection.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.admin;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Set;
+
+/**
+ * Class to identify a intersection resolution mechanism for {@code Set<String>} policies, it's
+ * used to resolve the enforced policy when being set by multiple admins (see {@link
+ * PolicyState#getResolutionMechanism()}).
+ *
+ * @hide
+ */
+public final class StringSetIntersection extends ResolutionMechanism<Set<String>> {
+
+    /**
+     * Intersection resolution for policies represented {@code Set<String>} which resolves as the
+     * intersection of all sets.
+     */
+    @NonNull
+    public static final StringSetIntersection STRING_SET_INTERSECTION = new StringSetIntersection();
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        return o != null && getClass() == o.getClass();
+    }
+
+    @Override
+    public int hashCode() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "StringSetIntersection {}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {}
+
+    @NonNull
+    public static final Parcelable.Creator<StringSetIntersection> CREATOR =
+            new Parcelable.Creator<StringSetIntersection>() {
+                @Override
+                public StringSetIntersection createFromParcel(Parcel source) {
+                    return new StringSetIntersection();
+                }
+
+                @Override
+                public StringSetIntersection[] newArray(int size) {
+                    return new StringSetIntersection[size];
+                }
+            };
+}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 0ecd275..b87ef70 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -402,3 +402,23 @@
   description: "Add new API for secondary lockscreen"
   bug: "336297680"
 }
+
+flag {
+  name: "remove_managed_esim_on_work_profile_deletion"
+  namespace: "enterprise"
+  description: "Remove managed eSIM when work profile is deleted"
+  bug: "347925470"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "use_policy_intersection_for_permitted_input_methods"
+  namespace: "enterprise"
+  description: "When deciding on permitted input methods, use policy intersection instead of last recorded policy."
+  bug: "340914586"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
index 83abc04..e05ede5 100644
--- a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
+++ b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
@@ -31,6 +31,7 @@
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.GlobalSearchSession;
 import android.app.appsearch.JoinSpec;
+import android.app.appsearch.PropertyPath;
 import android.app.appsearch.SearchResult;
 import android.app.appsearch.SearchResults;
 import android.app.appsearch.SearchSpec;
@@ -141,6 +142,9 @@
                         .addFilterSchemas(
                                 AppFunctionStaticMetadataHelper.getStaticSchemaNameForPackage(
                                         targetPackage))
+                        .addProjectionPaths(
+                                SearchSpec.SCHEMA_TYPE_WILDCARD,
+                                List.of(new PropertyPath(STATIC_PROPERTY_ENABLED_BY_DEFAULT)))
                         .setJoinSpec(joinSpec)
                         .setVerbatimSearchEnabled(true)
                         .build();
diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java
index e741bc2..ca14fce 100644
--- a/core/java/android/app/backup/BackupManagerMonitor.java
+++ b/core/java/android/app/backup/BackupManagerMonitor.java
@@ -164,6 +164,15 @@
   public static final String EXTRA_LOG_V_TO_U_ALLOWLIST =
           "android.app.backup.extra.V_TO_U_ALLOWLIST";
 
+  /**
+   * An int indicating why a backup was cancelled. One of {@link
+   * com.android.server.backup.BackupRestoreTask.CancellationReason}.
+   *
+   * @hide
+   */
+  public static final String EXTRA_LOG_CANCELLATION_REASON =
+          "android.app.backup.extra.CANCELLATION_REASON";
+
   // TODO complete this list with all log messages. And document properly.
   public static final int LOG_EVENT_ID_FULL_BACKUP_CANCEL = 4;
   public static final int LOG_EVENT_ID_ILLEGAL_KEY = 5;
@@ -297,6 +306,9 @@
    @hide */
   public static final int LOG_EVENT_ID_FAILED_TO_READ_DATA_FROM_TRANSPORT = 81;
 
+  /** The pipe between the BackupAgent and the framework was broken during full backup. @hide */
+  public static final int LOG_EVENT_ID_FULL_BACKUP_AGENT_PIPE_BROKEN = 82;
+
   /**
    * This method will be called each time something important happens on BackupManager.
    *
diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java
index 7718d15..3c8b1a0 100644
--- a/core/java/android/app/jank/JankDataProcessor.java
+++ b/core/java/android/app/jank/JankDataProcessor.java
@@ -366,7 +366,7 @@
                 30, 40, 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
                 Integer.MAX_VALUE
         };
-        private final int[] mFrameOverrunBuckets = new int[sFrameOverrunHistogramBounds.length];
+        private final int[] mFrameOverrunBuckets = new int[sFrameOverrunHistogramBounds.length - 1];
 
         // Histogram of frame duration overruns encoded in predetermined buckets.
         public PendingJankStat() {
@@ -511,7 +511,7 @@
             if (overrunTime <= 1000) {
                 return (overrunTime - 200) / 100 + 43;
             }
-            return sFrameOverrunHistogramBounds.length - 1;
+            return mFrameOverrunBuckets.length - 1;
         }
 
     }
diff --git a/core/java/android/app/jank/TEST_MAPPING b/core/java/android/app/jank/TEST_MAPPING
new file mode 100644
index 0000000..271eb4332
--- /dev/null
+++ b/core/java/android/app/jank/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "postsubmit": [
+    {
+      "name": "CoreAppJankTestCases"
+    },
+    {
+      "name": "CtsAppJankTestCases"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 5c267c9..6f0eafe 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -63,6 +63,16 @@
 }
 
 flag {
+  name: "modes_ui_dnd_tile"
+  namespace: "systemui"
+  description: "Shows a dedicated tile for the DND mode; dependent on modes_ui"
+  bug: "401217520"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "modes_hsum"
   namespace: "systemui"
   description: "Fixes for modes (and DND/Zen in general) with HSUM or secondary users"
@@ -259,7 +269,26 @@
   namespace: "systemui"
   description: "enables metrics when redacting notifications on the lockscreen"
   bug: "343631648"
-    metadata {
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "expanding_public_view"
+  namespace: "systemui"
+  description: "enables user expanding the public view of a notification"
+  bug: "398853084"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+ }
+flag {
+  name: "notif_entry_creation_time_use_elapsed_realtime"
+  namespace: "systemui"
+  description: "makes the notification entry expect its creation time to be elapsedRealtime, not uptimeMillis"
+  bug: "343631648"
+  metadata {
     purpose: PURPOSE_BUGFIX
   }
 }
@@ -347,6 +376,7 @@
   namespace: "systemui"
   description: "Allows the NAS to summarize notifications"
   bug: "390417189"
+  is_exported: true
 }
 
 flag {
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 8217ca1..172ed23 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -97,6 +97,8 @@
      *
      * @hide
      */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_SUPERVISION_MANAGER_APIS)
     @RequiresPermission(anyOf = {MANAGE_USERS, QUERY_USERS})
     @Nullable
     public Intent createConfirmSupervisionCredentialsIntent() {
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
index a13af7f..d7d6262 100644
--- a/core/java/android/app/wallpaper/WallpaperDescription.java
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -168,6 +168,12 @@
         return mSampleSize;
     }
 
+    @Override
+    public String toString() {
+        String component = (mComponent != null) ? mComponent.toString() : "{null}";
+        return  component + ":" + mId;
+    }
+
     ////// Comparison overrides
 
     @Override
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index f8ac27d..db77ade 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -24,6 +24,7 @@
 import android.companion.virtual.audio.IAudioConfigChangedCallback;
 import android.companion.virtual.audio.IAudioRoutingCallback;
 import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.companion.virtual.camera.VirtualCameraConfig;
@@ -251,6 +252,11 @@
     boolean sendSensorEvent(IBinder token, in VirtualSensorEvent event);
 
     /**
+     * Sends additional information about the virtual sensor corresponding to the given token.
+     */
+    boolean sendSensorAdditionalInfo(IBinder token, in VirtualSensorAdditionalInfo info);
+
+    /**
      * Launches a pending intent on the given display that is owned by this virtual device.
      */
     void launchPendingIntent(int displayId, in PendingIntent pendingIntent,
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 4fb3982..615a6df 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -158,3 +158,11 @@
     bug: "370720522"
     is_exported: true
 }
+
+flag {
+    name: "virtual_sensor_additional_info"
+    namespace: "virtual_devices"
+    description: "API for injecting SensorAdditionalInfo for VirtualSensor"
+    bug: "393517834"
+    is_exported: true
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index 934a1a8..8d4acfc 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -16,12 +16,15 @@
 
 package android.companion.virtual.sensor;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtualdevice.flags.Flags;
 import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -37,20 +40,33 @@
  */
 @SystemApi
 public final class VirtualSensor implements Parcelable {
+
     private final int mHandle;
     private final int mType;
     private final String mName;
+    private final int mFlags;
     private final IVirtualDevice mVirtualDevice;
     private final IBinder mToken;
+    // Only one additional info frame set at a time.
+    private final Object mAdditionalInfoLock = new Object();
 
     /**
      * @hide
      */
     public VirtualSensor(int handle, int type, String name, IVirtualDevice virtualDevice,
             IBinder token) {
+        this(handle, type, name, /*flags=*/0, virtualDevice, token);
+    }
+
+    /**
+     * @hide
+     */
+    public VirtualSensor(int handle, int type, String name, int flags, IVirtualDevice virtualDevice,
+            IBinder token) {
         mHandle = handle;
         mType = type;
         mName = name;
+        mFlags = flags;
         mVirtualDevice = virtualDevice;
         mToken = token;
     }
@@ -61,13 +77,14 @@
     @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
     @TestApi
     public VirtualSensor(int handle, int type, @NonNull String name) {
-        this(handle, type, name, /*virtualDevice=*/null, /*token=*/null);
+        this(handle, type, name, /*flags=*/0, /*virtualDevice=*/null, /*token=*/null);
     }
 
     private VirtualSensor(Parcel parcel) {
         mHandle = parcel.readInt();
         mType = parcel.readInt();
         mName = parcel.readString8();
+        mFlags = parcel.readInt();
         mVirtualDevice = IVirtualDevice.Stub.asInterface(parcel.readStrongBinder());
         mToken = parcel.readStrongBinder();
     }
@@ -123,6 +140,7 @@
         parcel.writeInt(mHandle);
         parcel.writeInt(mType);
         parcel.writeString8(mName);
+        parcel.writeInt(mFlags);
         parcel.writeStrongBinder(mVirtualDevice.asBinder());
         parcel.writeStrongBinder(mToken);
     }
@@ -143,6 +161,33 @@
         }
     }
 
+    /**
+     * Send additional information about the sensor to the system.
+     *
+     * @param info the additional sensor information to send.
+     * @throws UnsupportedOperationException if the sensor does not support sending additional info.
+     * @see Sensor#isAdditionalInfoSupported()
+     * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean)
+     * @see SensorAdditionalInfo
+     * @see VirtualSensorAdditionalInfo
+     */
+    @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+    public void sendAdditionalInfo(@NonNull VirtualSensorAdditionalInfo info) {
+        if (!Flags.virtualSensorAdditionalInfo()) {
+            return;
+        }
+        if ((mFlags & VirtualSensorConfig.ADDITIONAL_INFO_MASK) == 0) {
+            throw new UnsupportedOperationException("Sensor additional info not supported.");
+        }
+        try {
+            synchronized (mAdditionalInfoLock) {
+                mVirtualDevice.sendSensorAdditionalInfo(mToken, info);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     @NonNull
     public static final Parcelable.Creator<VirtualSensor> CREATOR =
             new Parcelable.Creator<VirtualSensor>() {
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
similarity index 80%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
index 5d14631..7267be8 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package android.companion.virtual.sensor;
 
-parcelable PictureProfileHandle;
+parcelable VirtualSensorAdditionalInfo;
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
new file mode 100644
index 0000000..a4fca50
--- /dev/null
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.companion.virtual.sensor;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.hardware.SensorAdditionalInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An additional information frame for a {@link VirtualSensor}, which is reported through listener
+ * callback {@link android.hardware.SensorEventCallback#onSensorAdditionalInfo}.
+ *
+ * @see SensorAdditionalInfo
+ * @see VirtualSensorConfig.Builder#setAdditionalInfoSupported(boolean)
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+@SystemApi
+public final class VirtualSensorAdditionalInfo implements Parcelable {
+
+    private final int mType;
+    @NonNull
+    private final List<float[]> mValues;
+
+    /** @hide */
+    @IntDef(prefix = "TYPE_", value = {
+            SensorAdditionalInfo.TYPE_UNTRACKED_DELAY,
+            SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE,
+            SensorAdditionalInfo.TYPE_VEC3_CALIBRATION,
+            SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT,
+            SensorAdditionalInfo.TYPE_SAMPLING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    private VirtualSensorAdditionalInfo(int type, @NonNull List<float[]> values) {
+        mType = type;
+        mValues = values;
+    }
+
+    private VirtualSensorAdditionalInfo(@NonNull Parcel parcel) {
+        mType = parcel.readInt();
+        final int valuesLength = parcel.readInt();
+        mValues = new ArrayList<>(valuesLength);
+        for (int i = 0; i < valuesLength; ++i) {
+            mValues.add(parcel.createFloatArray());
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+        parcel.writeInt(mType);
+        parcel.writeInt(mValues.size());
+        for (int i = 0; i < mValues.size(); ++i) {
+            parcel.writeFloatArray(mValues.get(i));
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Returns the type of this information frame.
+     *
+     * @see SensorAdditionalInfo#type
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the float values of this information frame, if any.
+     *
+     * @see SensorAdditionalInfo#floatValues
+     */
+    @NonNull
+    public List<float[]> getValues() {
+        return mValues;
+    }
+
+    /**
+     * Builder for {@link VirtualSensorAdditionalInfo}.
+     */
+    public static final class Builder {
+
+        @VirtualSensorAdditionalInfo.Type
+        private final int mType;
+        @NonNull
+        private final ArrayList<float[]> mValues = new ArrayList<>();
+
+        /**
+         * Creates a new builder.
+         *
+         * @param type type of this additional info frame.
+         * @see SensorAdditionalInfo
+         */
+        public Builder(@VirtualSensorAdditionalInfo.Type int type) {
+            switch (type) {
+                case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+                case SensorAdditionalInfo.TYPE_SAMPLING:
+                case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
+                case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+                case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported type " + type);
+            }
+            mType = type;
+        }
+
+        /**
+         * Additional info payload data represented in float values. Depending on the type of
+         * information, this may be null.
+         *
+         * @see SensorAdditionalInfo#floatValues
+         */
+        @NonNull
+        public Builder addValues(@NonNull float[] values) {
+            if (values.length > 14) {
+                throw new IllegalArgumentException("Maximum payload value size is 14.");
+            }
+            if (mValues.isEmpty()) {
+                switch (mType) {
+                    case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+                    case SensorAdditionalInfo.TYPE_SAMPLING:
+                        assertValuesLength(values, 2);
+                        break;
+                    case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
+                        assertValuesLength(values, 1);
+                        break;
+                    case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+                    case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
+                        assertValuesLength(values, 11);
+                        break;
+                }
+            } else if (values.length != mValues.getFirst().length) {
+                throw new IllegalArgumentException("All payload values must have the same length");
+            }
+
+            mValues.add(values);
+            return this;
+        }
+
+        private void assertValuesLength(float[] values, int expected) {
+            if (values.length != expected) {
+                throw new IllegalArgumentException(
+                        "Payload values must have size " + expected + " for type " + mType);
+            }
+        }
+
+        /**
+         * Creates a new {@link VirtualSensorAdditionalInfo}.
+         *
+         * @throws IllegalArgumentException if the payload doesn't match the expectation for the
+         *   given type, as documented in {@link SensorAdditionalInfo}.
+         */
+        @NonNull
+        public VirtualSensorAdditionalInfo build() {
+            if (mValues.isEmpty()) {
+                throw new IllegalArgumentException("Payload is required");
+            }
+            return new VirtualSensorAdditionalInfo(mType, mValues);
+        }
+    }
+
+    public static final @NonNull Creator<VirtualSensorAdditionalInfo> CREATOR =
+            new Creator<>() {
+                public VirtualSensorAdditionalInfo createFromParcel(Parcel source) {
+                    return new VirtualSensorAdditionalInfo(source);
+                }
+
+                public VirtualSensorAdditionalInfo[] newArray(int size) {
+                    return new VirtualSensorAdditionalInfo[size];
+                }
+            };
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 68bc9bc..be8974e 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -59,10 +59,14 @@
     private static final int REPORTING_MODE_MASK = 0xE;
     private static final int REPORTING_MODE_SHIFT = 1;
 
+    // Mask for indication bit of sensor additional information support, bit 6.
+    static final int ADDITIONAL_INFO_MASK = 0x40;
+
     // Mask for direct mode highest rate level, bit 7, 8, 9.
     private static final int DIRECT_REPORT_MASK = 0x380;
     private static final int DIRECT_REPORT_SHIFT = 7;
 
+
     // Mask for supported direct channel, bit 10, 11
     private static final int DIRECT_CHANNEL_SHIFT = 10;
 
@@ -253,6 +257,18 @@
     }
 
     /**
+     * Returns whether the sensor supports additional information.
+     *
+     * @see Builder#setAdditionalInfoSupported(boolean)
+     * @see Sensor#isAdditionalInfoSupported()
+     * @see android.hardware.SensorAdditionalInfo
+     */
+    @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+    public boolean isAdditionalInfoSupported() {
+        return (mFlags & ADDITIONAL_INFO_MASK) > 0;
+    }
+
+    /**
      * Returns the reporting mode of this sensor.
      *
      * @see Builder#setReportingMode(int)
@@ -450,6 +466,25 @@
         }
 
         /**
+         * Sets whether this sensor supports sensor additional information.
+         *
+         * @see Sensor#isAdditionalInfoSupported()
+         * @see android.hardware.SensorAdditionalInfo
+         * @see VirtualSensorAdditionalInfo
+         */
+        @FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
+        @NonNull
+        public VirtualSensorConfig.Builder setAdditionalInfoSupported(
+                boolean additionalInfoSupported) {
+            if (additionalInfoSupported) {
+                mFlags |= ADDITIONAL_INFO_MASK;
+            } else {
+                mFlags &= ~ADDITIONAL_INFO_MASK;
+            }
+            return this;
+        }
+
+        /**
          * Sets the reporting mode of this sensor.
          *
          * @throws IllegalArgumentException if the reporting mode is not one of
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 55d78f9b..cc288b1 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -744,15 +744,22 @@
      */
     public static final long BIND_MATCH_QUARANTINED_COMPONENTS = 0x2_0000_0000L;
 
+    /**
+     * Flag for {@link #bindService} that allows the bound app to be frozen if it is eligible.
+     *
+     * @hide
+     */
+    public static final long BIND_ALLOW_FREEZE = 0x4_0000_0000L;
 
     /**
      * These bind flags reduce the strength of the binding such that we shouldn't
      * consider it as pulling the process up to the level of the one that is bound to it.
      * @hide
      */
-    public static final int BIND_REDUCTION_FLAGS =
+    public static final long BIND_REDUCTION_FLAGS =
             Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_WAIVE_PRIORITY
-                    | Context.BIND_NOT_PERCEPTIBLE | Context.BIND_NOT_VISIBLE;
+                    | Context.BIND_NOT_PERCEPTIBLE | Context.BIND_NOT_VISIBLE
+                    | Context.BIND_ALLOW_FREEZE;
 
     /** @hide */
     @IntDef(flag = true, prefix = { "RECEIVER_VISIBLE" }, value = {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0387561..bb62ac3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -12426,6 +12426,8 @@
     }
 
     private void collectNestedIntentKeysRecur(Set<Intent> visited, boolean forceUnparcel) {
+        // if forceUnparcel is false, do not unparcel the mExtras bundle.
+        // forceUnparcel will only be true when this method is called from system server.
         if (mExtras != null && (forceUnparcel || !mExtras.isParcelled()) && !mExtras.isEmpty()) {
             addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
             for (String key : mExtras.keySet()) {
@@ -12440,6 +12442,7 @@
                         value = mExtras.get(key);
                     } else {
                         value = null;
+                        removeExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
                     }
                 } catch (BadParcelableException e) {
                     // This may still happen if the keys are collected on the system server side, in
@@ -12459,6 +12462,13 @@
             }
         }
 
+        // if there is no extras in the bundle, we also mark the intent as keys are collected.
+        // isDefinitelyEmpty() will not unparceled the mExtras. This is the best we can do without
+        // unparceling the extra bundle.
+        if (mExtras == null ||  mExtras.isDefinitelyEmpty()) {
+            addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
+        }
+
         if (mClipData != null) {
             for (int i = 0; i < mClipData.getItemCount(); i++) {
                 Intent intent = mClipData.getItemAt(i).mIntent;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 49fd634..53966b8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5964,7 +5964,39 @@
      *
      * @see #getLaunchIntentSenderForPackage(String)
      */
-    public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
+     public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
+
+    /**
+     * Returns a "good" intent to launch a front-door activity in a package.
+     * This is used, for example, to implement an "open" button when browsing
+     * through packages.  The current implementation looks first for a main
+     * activity in the category {@link Intent#CATEGORY_INFO}, and next for a
+     * main activity in the category {@link Intent#CATEGORY_LAUNCHER}. Returns
+     * <code>null</code> if neither are found.
+     *
+     * <p>Consider using {@link #getLaunchIntentSenderForPackage(String)} if
+     * the caller is not allowed to query for the <code>packageName</code>.
+     *
+     * @param packageName The name of the package to inspect.
+     * @param includeDirectBootUnaware When {@code true}, activities that are direct-boot-unaware
+     *    will be considered even if the device hasn't been unlocked (i.e. querying will be done
+     *    with {@code MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE}).
+     *
+     * @return A fully-qualified {@link Intent} that can be used to launch the
+     * main activity in the package. Returns <code>null</code> if the package
+     * does not contain such an activity, or if <em>packageName</em> is not
+     * recognized.
+     *
+     * @see #getLaunchIntentSenderForPackage(String)
+     *
+     * @hide
+     */
+    public @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName,
+            boolean includeDirectBootUnaware) {
+        throw new UnsupportedOperationException(
+                "getLaunchIntentForPackage(packageName, includeDirectBootUnaware) not implemented"
+                        + " in subclass");
+    }
 
     /**
      * Return a "good" intent to launch a front-door Leanback activity in a
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 74da62c..ded35b2 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -31,13 +31,13 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.AttributeSet;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseArrayMap;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -98,15 +98,16 @@
     @GuardedBy("mServicesLock")
     private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
 
-    @GuardedBy("mServiceInfoCaches")
-    private final ArrayMap<ComponentName, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>();
+    @GuardedBy("mUserIdToServiceInfoCaches")
+    private final SparseArrayMap<ComponentName, ServiceInfo<V>> mUserIdToServiceInfoCaches =
+            new SparseArrayMap<>();
 
     private final Handler mBackgroundHandler;
 
     private final Runnable mClearServiceInfoCachesRunnable = new Runnable() {
         public void run() {
-            synchronized (mServiceInfoCaches) {
-                mServiceInfoCaches.clear();
+            synchronized (mUserIdToServiceInfoCaches) {
+                mUserIdToServiceInfoCaches.clear();
             }
         }
     };
@@ -166,7 +167,15 @@
     @UnsupportedAppUsage
     public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
             String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
-        mContext = context;
+        this(new Injector<V>(context), interfaceName, metaDataName, attributeName,
+                serializerAndParser);
+    }
+
+    /** Provides the basic functionality for unit tests. */
+    @VisibleForTesting
+    public RegisteredServicesCache(Injector<V> injector, String interfaceName, String metaDataName,
+            String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
+        mContext = injector.getContext();
         mInterfaceName = interfaceName;
         mMetaDataName = metaDataName;
         mAttributesName = attributeName;
@@ -184,7 +193,7 @@
         if (isCore) {
             intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         }
-        mBackgroundHandler = BackgroundThread.getHandler();
+        mBackgroundHandler = injector.getBackgroundHandler();
         mContext.registerReceiverAsUser(
                 mPackageReceiver, UserHandle.ALL, intentFilter, null, mBackgroundHandler);
 
@@ -529,8 +538,8 @@
                     Slog.d(TAG, "Fail to get the PackageInfo in generateServicesMap: " + e);
                 }
                 if (lastUpdateTime >= 0) {
-                    ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(componentName,
-                            lastUpdateTime);
+                    ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(userId,
+                            componentName, lastUpdateTime);
                     if (serviceInfo != null) {
                         serviceInfos.add(serviceInfo);
                         continue;
@@ -545,8 +554,8 @@
                 }
                 serviceInfos.add(info);
                 if (Flags.optimizeParsingInRegisteredServicesCache()) {
-                    synchronized (mServiceInfoCaches) {
-                        mServiceInfoCaches.put(componentName, info);
+                    synchronized (mUserIdToServiceInfoCaches) {
+                        mUserIdToServiceInfoCaches.add(userId, componentName, info);
                     }
                 }
             } catch (XmlPullParserException | IOException e) {
@@ -555,8 +564,8 @@
         }
 
         if (Flags.optimizeParsingInRegisteredServicesCache()) {
-            synchronized (mServiceInfoCaches) {
-                if (!mServiceInfoCaches.isEmpty()) {
+            synchronized (mUserIdToServiceInfoCaches) {
+                if (mUserIdToServiceInfoCaches.numMaps() > 0) {
                     mBackgroundHandler.removeCallbacks(mClearServiceInfoCachesRunnable);
                     mBackgroundHandler.postDelayed(mClearServiceInfoCachesRunnable,
                             SERVICE_INFO_CACHES_TIMEOUT_MILLIS);
@@ -865,6 +874,11 @@
         synchronized (mServicesLock) {
             mUserServices.remove(userId);
         }
+        if (Flags.optimizeParsingInRegisteredServicesCache()) {
+            synchronized (mUserIdToServiceInfoCaches) {
+                mUserIdToServiceInfoCaches.delete(userId);
+            }
+        }
     }
 
     @VisibleForTesting
@@ -908,14 +922,35 @@
         mContext.unregisterReceiver(mUserRemovedReceiver);
     }
 
-    private ServiceInfo<V> getServiceInfoFromServiceCache(@NonNull ComponentName componentName,
-            long lastUpdateTime) {
-        synchronized (mServiceInfoCaches) {
-            ServiceInfo<V> serviceCache = mServiceInfoCaches.get(componentName);
+    private ServiceInfo<V> getServiceInfoFromServiceCache(int userId,
+            @NonNull ComponentName componentName, long lastUpdateTime) {
+        synchronized (mUserIdToServiceInfoCaches) {
+            ServiceInfo<V> serviceCache = mUserIdToServiceInfoCaches.get(userId, componentName);
             if (serviceCache != null && serviceCache.lastUpdateTime == lastUpdateTime) {
                 return serviceCache;
             }
             return null;
         }
     }
+
+    /**
+     * Point of injection for test dependencies.
+     * @param <V> The type of the value.
+     */
+    @VisibleForTesting
+    public static class Injector<V> {
+        private final Context mContext;
+
+        public Injector(Context context) {
+            mContext = context;
+        }
+
+        public Context getContext() {
+            return mContext;
+        }
+
+        public Handler getBackgroundHandler() {
+            return BackgroundThread.getHandler();
+        }
+    }
 }
diff --git a/core/java/android/content/pm/SystemFeaturesCache.java b/core/java/android/content/pm/SystemFeaturesCache.java
index b3d70fa..57dd1e6 100644
--- a/core/java/android/content/pm/SystemFeaturesCache.java
+++ b/core/java/android/content/pm/SystemFeaturesCache.java
@@ -74,6 +74,11 @@
         return instance;
     }
 
+    /** Checks for existence of the process-global instance. */
+    public static boolean hasInstance() {
+        return sInstance != null;
+    }
+
     /** Clears the process-global cache instance for testing. */
     @VisibleForTesting
     public static void clearInstance() {
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 23f1ff8..92ae15a 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -136,6 +136,28 @@
             ]
         },
         {
+          "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+          "options":[
+              {
+                  "exclude-annotation":"androidx.test.filters.FlakyTest"
+              },
+              {
+                  "exclude-annotation":"org.junit.Ignore"
+              }
+          ]
+        },
+        {
+          "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+          "options":[
+              {
+                  "exclude-annotation":"androidx.test.filters.FlakyTest"
+              },
+              {
+                  "exclude-annotation":"org.junit.Ignore"
+              }
+          ]
+        },
+        {
             "name": "CtsPackageInstallerCUJMultiUsersTestCases",
             "options":[
                {
@@ -261,6 +283,28 @@
             ]
         },
         {
+            "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
+        },
+        {
+            "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
+        },
+        {
             "name": "CtsPackageInstallerCUJMultiUsersTestCases",
             "options":[
                {
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 582a1a9..53203eb 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -410,6 +410,11 @@
         return UserManager.isUserTypePrivateProfile(userType);
     }
 
+    @FlaggedApi(android.multiuser.Flags.FLAG_ALLOW_SUPERVISING_PROFILE)
+    public boolean isSupervisingProfile() {
+        return UserManager.isUserTypeSupervisingProfile(userType);
+    }
+
     /** See {@link #FLAG_DISABLED}*/
     @UnsupportedAppUsage
     public boolean isEnabled() {
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 5c904c1..3411a48 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -617,8 +617,8 @@
 }
 
 flag {
-     namespace: "multi_user"
      name: "logout_user_api"
+     namespace: "multiuser"
      description: "Add API to logout user"
      bug: "350045389"
 }
@@ -629,3 +629,20 @@
     description: "Enable moving content into the Private Space"
     bug: "360066001"
 }
+
+flag {
+    name: "allow_supervising_profile"
+    namespace: "supervision"
+    description: "Enables support for new supervising user type"
+    bug: "389712089"
+}
+
+flag {
+    name: "use_unified_resources"
+    namespace: "multiuser"
+    description: "Use same resources"
+    bug: "392972139"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index e43a5fc..040dcfd 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -183,11 +183,6 @@
     private static native long nativeChanges(long connectionPtr);
     private static native long nativeTotalChanges(long connectionPtr);
 
-    // This method is deprecated and should be removed when it is no longer needed by the
-    // robolectric tests.  It should not be called from any frameworks java code.
-    @Deprecated
-    private static native void nativeClose(long connectionPtr);
-
     private SQLiteConnection(SQLiteConnectionPool pool,
             SQLiteDatabaseConfiguration configuration,
             int connectionId, boolean primaryConnection) {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index bcb7ebf..6e9dcf5 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1714,7 +1714,7 @@
                     final TaskInfo taskInfo = appTask.getTaskInfo();
                     final int freeformCameraCompatMode = taskInfo.appCompatTaskInfo
                             .cameraCompatTaskInfo.freeformCameraCompatMode;
-                    if (freeformCameraCompatMode != 0
+                    if (isInCameraCompatMode(freeformCameraCompatMode)
                             && taskInfo.topActivity != null
                             && taskInfo.topActivity.getPackageName().equals(packageName)) {
                         // WindowManager has requested rotation override.
@@ -1741,6 +1741,12 @@
                 : ICameraService.ROTATION_OVERRIDE_NONE;
     }
 
+    private static boolean isInCameraCompatMode(@CameraCompatTaskInfo.FreeformCameraCompatMode int
+            freeformCameraCompatMode) {
+        return (freeformCameraCompatMode != CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_UNSPECIFIED)
+                && (freeformCameraCompatMode != CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE);
+    }
+
     private static int getRotationOverrideForCompatFreeform(
             @CameraCompatTaskInfo.FreeformCameraCompatMode int freeformCameraCompatMode) {
         // Only rotate-and-crop if the app and device orientations do not match.
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
index fdde205..de3bafb 100644
--- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableParcelable.java
@@ -75,7 +75,6 @@
             }
 
             Parcel parcel = Parcel.obtain();
-            byte[] parcelContents;
 
             try {
                 value.writeToParcel(parcel, /*flags*/0);
@@ -85,17 +84,15 @@
                             "Parcelable " + value + " must not have file descriptors");
                 }
 
-                parcelContents = parcel.marshall();
+                final int position = buffer.position();
+                parcel.marshall(buffer);
+                if (buffer.position() == position) {
+                    throw new AssertionError("No data marshaled for " + value);
+                }
             }
             finally {
                 parcel.recycle();
             }
-
-            if (parcelContents.length == 0) {
-                throw new AssertionError("No data marshaled for " + value);
-            }
-
-            buffer.put(parcelContents);
         }
 
         @Override
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 92a56fc..8216130 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -433,8 +433,9 @@
     public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
 
     /**
-     * Virtual display flag: Indicates that the display should support system decorations. Virtual
-     * displays without this flag shouldn't show home, navigation bar or wallpaper.
+     * Virtual display flag: Indicates that the display supports and should always show system
+     * decorations. Virtual displays without this flag shouldn't show home, navigation bar or
+     * wallpaper.
      * <p>This flag doesn't work without {@link #VIRTUAL_DISPLAY_FLAG_TRUSTED}</p>
      *
      * @see #createVirtualDisplay
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index 4ed0fc0..c3c8c3d 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -600,8 +600,7 @@
 
     private void addDisplay(int displayId, float width, float height, boolean shouldLog) {
         if (findDisplay(displayId, mRoot) != null) {
-            throw new IllegalArgumentException(
-                    "DisplayTopology: attempting to add a display that already exists");
+            return;
         }
         if (mRoot == null) {
             mRoot = new TreeNode(displayId, width, height, POSITION_LEFT, /* offset= */ 0);
diff --git a/core/java/android/hardware/input/IKeyGestureHandler.aidl b/core/java/android/hardware/input/IKeyGestureHandler.aidl
index 509b948..4da991e 100644
--- a/core/java/android/hardware/input/IKeyGestureHandler.aidl
+++ b/core/java/android/hardware/input/IKeyGestureHandler.aidl
@@ -28,15 +28,4 @@
      * to that gesture.
      */
     boolean handleKeyGesture(in AidlKeyGestureEvent event, in IBinder focusedToken);
-
-    /**
-     * Called to know if a particular gesture type is supported by the handler.
-     *
-     * TODO(b/358569822): Remove this call to reduce the binder calls to single call for
-     *  handleKeyGesture. For this we need to remove dependency of multi-key gestures to identify if
-     *  a key gesture is supported on first relevant key down.
-     *  Also, for now we prioritize handlers in the system server process above external handlers to
-     *  reduce IPC binder calls.
-     */
-    boolean isKeyGestureSupported(int gestureType);
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 49db54d..d6419af 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1758,13 +1758,6 @@
          */
         boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
                 @Nullable IBinder focusedToken);
-
-        /**
-         * Called to identify if a particular gesture is of interest to a handler.
-         *
-         * NOTE: If no active handler supports certain gestures, the gestures will not be captured.
-         */
-        boolean isKeyGestureSupported(@KeyGestureEvent.KeyGestureType int gestureType);
     }
 
     /** @hide */
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index a9a45ae..c4b4831 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -1193,23 +1193,6 @@
             }
             return false;
         }
-
-        @Override
-        public boolean isKeyGestureSupported(@KeyGestureEvent.KeyGestureType int gestureType) {
-            synchronized (mKeyGestureEventHandlerLock) {
-                if (mKeyGestureEventHandlers == null) {
-                    return false;
-                }
-                final int numHandlers = mKeyGestureEventHandlers.size();
-                for (int i = 0; i < numHandlers; i++) {
-                    KeyGestureEventHandler handler = mKeyGestureEventHandlers.get(i);
-                    if (handler.isKeyGestureSupported(gestureType)) {
-                        return true;
-                    }
-                }
-            }
-            return false;
-        }
     }
 
     /**
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 1a712d2..9dd1fed 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -108,7 +108,8 @@
     public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD = 55;
     public static final int KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD = 56;
     public static final int KEY_GESTURE_TYPE_GLOBAL_ACTIONS = 57;
-    public static final int KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD = 58;
+    @Deprecated
+    public static final int DEPRECATED_KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD = 58;
     public static final int KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT = 59;
     public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT = 60;
     public static final int KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS = 61;
@@ -200,7 +201,6 @@
             KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
             KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD,
             KEY_GESTURE_TYPE_GLOBAL_ACTIONS,
-            KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
             KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT,
             KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
             KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
@@ -777,8 +777,6 @@
                 return "KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD";
             case KEY_GESTURE_TYPE_GLOBAL_ACTIONS:
                 return "KEY_GESTURE_TYPE_GLOBAL_ACTIONS";
-            case KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD:
-                return "KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD";
             case KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT:
                 return "KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT";
             case KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
diff --git a/core/java/android/hardware/serial/OWNERS b/core/java/android/hardware/serial/OWNERS
index bc2c66a..13f6774 100644
--- a/core/java/android/hardware/serial/OWNERS
+++ b/core/java/android/hardware/serial/OWNERS
@@ -3,4 +3,5 @@
 chominskib@google.com
 wzwonarz@google.com
 gstepniewski@google.com
-xutan@google.com
\ No newline at end of file
+xutan@google.com
+ovn@google.com
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 894b068..2e7bc6d 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -428,6 +428,9 @@
      */
     @AnyThread
     public static boolean canImeRenderGesturalNavButtons() {
+        if (Flags.disallowDisablingImeNavigationBar()) {
+            return true;
+        }
         return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, true);
     }
 
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 0964cde..c3ec96d 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -145,8 +145,17 @@
         }
 
         if (Flags.forceConcurrentMessageQueue()) {
-            sIsProcessAllowedToUseConcurrent = true;
-            return;
+            // b/379472827: Robolectric tests use reflection to access MessageQueue.mMessages.
+            // This is a hack to allow Robolectric tests to use the legacy implementation.
+            try {
+                Class.forName("org.robolectric.Robolectric");
+            } catch (ClassNotFoundException e) {
+                // This is not a Robolectric test.
+                sIsProcessAllowedToUseConcurrent = true;
+                return;
+            }
+            // This is a Robolectric test.
+            // Continue to the following checks.
         }
 
         final String processName = Process.myProcessName();
diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
index 132bdd1..1cf57de 100644
--- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java
+++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java
@@ -786,8 +786,8 @@
     }
 
     /**
-     * Get the timestamp of the next executable message in our priority queue.
-     * Returns null if there are no messages ready for delivery.
+     * Get the timestamp of the next message in our priority queue.
+     * Returns null if there are no messages in the queue.
      *
      * Caller must ensure that this doesn't race 'next' from the Looper thread.
      */
@@ -799,8 +799,8 @@
     }
 
     /**
-     * Return the next executable message in our priority queue.
-     * Returns null if there are no messages ready for delivery
+     * Return the next message in our priority queue.
+     * Returns null if there are no messages in the queue.
      *
      * Caller must ensure that this doesn't race 'next' from the Looper thread.
      */
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 49d3f06..4a99285 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -20,6 +20,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,6 +28,7 @@
 import android.annotation.TestApi;
 import android.app.AppOpsManager;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Flags;
 import android.ravenwood.annotation.RavenwoodClassLoadHook;
 import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.ravenwood.annotation.RavenwoodReplace;
@@ -52,6 +54,8 @@
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
+import java.nio.BufferOverflowException;
+import java.nio.ReadOnlyBufferException;
 import libcore.util.SneakyThrow;
 
 import java.io.ByteArrayInputStream;
@@ -62,6 +66,7 @@
 import java.io.ObjectOutputStream;
 import java.io.ObjectStreamClass;
 import java.io.Serializable;
+import java.nio.ByteBuffer;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.Array;
@@ -457,8 +462,15 @@
     private static native void nativeDestroy(long nativePtr);
 
     private static native byte[] nativeMarshall(long nativePtr);
+    private static native int nativeMarshallArray(
+            long nativePtr, byte[] data, int offset, int length);
+    private static native int nativeMarshallBuffer(
+            long nativePtr, ByteBuffer buffer, int offset, int length);
     private static native void nativeUnmarshall(
             long nativePtr, byte[] data, int offset, int length);
+    private static native void nativeUnmarshallBuffer(
+            long nativePtr, ByteBuffer buffer, int offset, int length);
+
     private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
     private static native boolean nativeCompareDataInRange(
             long ptrA, int offsetA, long ptrB, int offsetB, int length);
@@ -814,12 +826,78 @@
     }
 
     /**
+     * Writes the raw bytes of the parcel to a buffer.
+     *
+     * <p class="note">The data you retrieve here <strong>must not</strong>
+     * be placed in any kind of persistent storage (on local disk, across
+     * a network, etc).  For that, you should use standard serialization
+     * or another kind of general serialization mechanism.  The Parcel
+     * marshalled representation is highly optimized for local IPC, and as
+     * such does not attempt to maintain compatibility with data created
+     * in different versions of the platform.
+     *
+     * @param buffer The ByteBuffer to write the data to.
+     * @throws ReadOnlyBufferException if the buffer is read-only.
+     * @throws BufferOverflowException if the buffer is too small.
+     */
+    @FlaggedApi(Flags.FLAG_PARCEL_MARSHALL_BYTEBUFFER)
+    public final void marshall(@NonNull ByteBuffer buffer) {
+        if (buffer == null) {
+            throw new NullPointerException();
+        }
+        if (buffer.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+
+        final int position = buffer.position();
+        final int remaining = buffer.remaining();
+
+        int marshalledSize = 0;
+        if (buffer.isDirect()) {
+            marshalledSize = nativeMarshallBuffer(mNativePtr, buffer, position, remaining);
+        } else if (buffer.hasArray()) {
+            marshalledSize = nativeMarshallArray(
+                    mNativePtr, buffer.array(), buffer.arrayOffset() + position, remaining);
+        } else {
+            throw new IllegalArgumentException();
+        }
+
+        buffer.position(position + marshalledSize);
+    }
+
+    /**
      * Fills the raw bytes of this Parcel with the supplied data.
      */
     public final void unmarshall(@NonNull byte[] data, int offset, int length) {
         nativeUnmarshall(mNativePtr, data, offset, length);
     }
 
+    /**
+     * Fills the raw bytes of this Parcel with data from the supplied buffer.
+     *
+     * @param buffer will read buffer.remaining() bytes from the buffer.
+     */
+    @FlaggedApi(Flags.FLAG_PARCEL_MARSHALL_BYTEBUFFER)
+    public final void unmarshall(@NonNull ByteBuffer buffer) {
+        if (buffer == null) {
+            throw new NullPointerException();
+        }
+
+        final int position = buffer.position();
+        final int remaining = buffer.remaining();
+
+        if (buffer.isDirect()) {
+            nativeUnmarshallBuffer(mNativePtr, buffer, position, remaining);
+        } else if (buffer.hasArray()) {
+            nativeUnmarshall(
+                    mNativePtr, buffer.array(), buffer.arrayOffset() + position, remaining);
+        } else {
+            throw new IllegalArgumentException();
+        }
+
+        buffer.position(position + remaining);
+    }
+
     public final void appendFrom(Parcel parcel, int offset, int length) {
         nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
     }
diff --git a/core/java/android/os/TestLooperManager.java b/core/java/android/os/TestLooperManager.java
index 1a54f4d..204e344 100644
--- a/core/java/android/os/TestLooperManager.java
+++ b/core/java/android/os/TestLooperManager.java
@@ -96,8 +96,8 @@
     }
 
     /**
-     * Retrieves and removes the next message that should be executed by this queue.
-     * If the queue is empty or no messages are deliverable, returns null.
+     * Retrieves and removes the next message in this queue.
+     * If the queue is empty, returns null.
      * This method never blocks.
      *
      * <p>Callers should always call {@link #recycle(Message)} on the message when all interactions
@@ -112,9 +112,9 @@
     }
 
     /**
-     * Retrieves, but does not remove, the values of {@link Message#when} of next message that
-     * should be executed by this queue.
-     * If the queue is empty or no messages are deliverable, returns null.
+     * Retrieves, but does not remove, the values of {@link Message#when} of next message in the
+     * queue.
+     * If the queue is empty, returns null.
      * This method never blocks.
      */
     @FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 767019d..c01c3cd 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -209,6 +209,23 @@
     public static final String USER_TYPE_PROFILE_COMMUNAL = "android.os.usertype.profile.COMMUNAL";
 
     /**
+     * User type representing a user who manages supervision on the device.
+     * When any full user on the device is supervised, the credentials for this profile will be
+     * required in order to perform certain actions for that user (i.e. those controlled by
+     * {@link android.app.supervision.SupervisionManager} or the
+     * {@link android.app.role.RoleManager#ROLE_SYSTEM_SUPERVISION supervision role holder}).
+     * There can only be one supervising profile per device, and the credentials set for that
+     * profile will be used to authorize actions for any supervised user on the device. This is
+     * distinct from a managed profile in that it functions only to authorize certain supervised
+     * actions; it does not represent the user to which restriction or management is applied.
+     * @hide
+     */
+    @FlaggedApi(android.multiuser.Flags.FLAG_ALLOW_SUPERVISING_PROFILE)
+    @SystemApi
+    public static final String USER_TYPE_PROFILE_SUPERVISING =
+            "android.os.usertype.profile.SUPERVISING";
+
+    /**
      * User type representing a {@link UserHandle#USER_SYSTEM system} user that is <b>not</b> a
      * human user.
      * This type of user cannot be created; it can only pre-exist on first boot.
@@ -3226,6 +3243,18 @@
     }
 
     /**
+     * Returns whether the user type is a
+     * {@link UserManager#USER_TYPE_PROFILE_SUPERVISING supervising profile}.
+     *
+     * @hide
+     */
+    @FlaggedApi(android.multiuser.Flags.FLAG_ALLOW_SUPERVISING_PROFILE)
+    @android.ravenwood.annotation.RavenwoodKeep
+    public static boolean isUserTypeSupervisingProfile(@Nullable String userType) {
+        return USER_TYPE_PROFILE_SUPERVISING.equals(userType);
+    }
+
+    /**
      * @hide
      * @deprecated Use {@link #isRestrictedProfile()}
      */
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 86acb2b..0150d17 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -354,6 +354,15 @@
 
 flag {
      namespace: "system_performance"
+     name: "parcel_marshall_bytebuffer"
+     is_exported: true
+     description: "Parcel marshal/unmarshall APIs that use ByteBuffer."
+     is_fixed_read_only: true
+     bug: "401362825"
+}
+
+flag {
+     namespace: "system_performance"
      name: "perfetto_sdk_tracing"
      description: "Tracing using Perfetto SDK."
      bug: "303199244"
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index a8a22f6..b82f278 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -216,7 +216,7 @@
     /**
      * Gets the maximum number of TIDs this device supports for getting CPU headroom.
      * <p>
-     * See {@link CpuHeadroomParams#setTids(int...)}.
+     * See {@link CpuHeadroomParams.Builder#setTids(int...)}.
      *
      * @return the maximum size of TIDs supported
      * @throws UnsupportedOperationException if the CPU headroom API is unsupported.
@@ -288,9 +288,7 @@
     /**
      * Gets the range of the calculation window size for CPU headroom.
      * <p>
-     * In API version 36, the range will be a superset of [50, 10000].
-     * <p>
-     * See {@link CpuHeadroomParams#setCalculationWindowMillis(int)}.
+     * See {@link CpuHeadroomParams.Builder#setCalculationWindowMillis(int)}.
      *
      * @return the range of the calculation window size supported in milliseconds.
      * @throws UnsupportedOperationException if the CPU headroom API is unsupported.
@@ -310,9 +308,7 @@
     /**
      * Gets the range of the calculation window size for GPU headroom.
      * <p>
-     * In API version 36, the range will be a superset of [50, 10000].
-     * <p>
-     * See {@link GpuHeadroomParams#setCalculationWindowMillis(int)}.
+     * See {@link GpuHeadroomParams.Builder#setCalculationWindowMillis(int)}.
      *
      * @return the range of the calculation window size supported in milliseconds.
      * @throws UnsupportedOperationException if the GPU headroom API is unsupported.
diff --git a/core/java/android/os/instrumentation/MethodDescriptorParser.java b/core/java/android/os/instrumentation/MethodDescriptorParser.java
index 57fc44f..3264c04 100644
--- a/core/java/android/os/instrumentation/MethodDescriptorParser.java
+++ b/core/java/android/os/instrumentation/MethodDescriptorParser.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 
-import java.lang.reflect.Method;
+import java.lang.reflect.Executable;
 
 /**
  * A utility class for dynamic instrumentation / uprobestats.
@@ -28,9 +28,9 @@
 public final class MethodDescriptorParser {
 
     /**
-     * Parses a {@link MethodDescriptor} (in string representation) into a {@link Method}.
+     * Parses a {@link MethodDescriptor} (in string representation) into a {@link Executable}.
      */
-    public static Method parseMethodDescriptor(ClassLoader classLoader,
+    public static Executable parseMethodDescriptor(ClassLoader classLoader,
             @NonNull MethodDescriptor descriptor) {
         try {
             Class<?> javaClass = classLoader.loadClass(descriptor.fullyQualifiedClassName);
@@ -72,6 +72,10 @@
                 }
             }
 
+            if (com.android.art.flags.Flags.executableMethodFileOffsetsV2()
+                    && descriptor.methodName.equals("<init>")) {
+                return javaClass.getDeclaredConstructor(parameters);
+            }
             return javaClass.getDeclaredMethod(descriptor.methodName, parameters);
         } catch (ClassNotFoundException | NoSuchMethodException e) {
             throw new IllegalArgumentException(
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 414f2749..176c0c8 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -165,3 +165,14 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    namespace: "haptics"
+    name: "remove_hidl_support"
+    description: "Remove framework code to support HIDL vibrator HALs."
+    bug: "308452413"
+    is_fixed_read_only: true
+    metadata {
+      purpose: PURPOSE_FEATURE
+    }
+}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 561a2c9..17033d1 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1907,8 +1907,9 @@
     @Context.PermissionRequestState
     public int getPermissionRequestState(@NonNull String packageName, @NonNull String permission,
             int deviceId) {
+        int resolvedDeviceId = resolveDeviceIdForPermissionCheck(mContext, deviceId, permission);
         return sPermissionRequestStateCache.query(
-                new PermissionRequestStateQuery(packageName, permission, deviceId));
+                new PermissionRequestStateQuery(packageName, permission, resolvedDeviceId));
     }
 
     /**
@@ -2035,7 +2036,8 @@
      */
     public int checkPackageNamePermission(String permName, String pkgName,
             int deviceId, @UserIdInt int userId) {
-        String persistentDeviceId = getPersistentDeviceId(deviceId);
+        int resolvedDeviceId = resolveDeviceIdForPermissionCheck(mContext, deviceId, permName);
+        String persistentDeviceId = getPersistentDeviceId(resolvedDeviceId);
         return sPackageNamePermissionCache.query(
                 new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId));
     }
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 0476f62..34272b1 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -71,6 +71,19 @@
 }
 
 flag {
+    name: "unknown_call_setting_blocked_logging_enabled"
+    is_exported: true
+    is_fixed_read_only: true
+    namespace: "permissions"
+    description: "enable the metrics when blocking certain app installs during an unknown call"
+    bug: "364535720"
+
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "op_enable_mobile_data_by_user"
     is_exported: true
     namespace: "permissions"
@@ -372,6 +385,15 @@
 }
 
 flag {
+    name: "record_all_runtime_appops_sqlite"
+    is_fixed_read_only: true
+    is_exported: true
+    namespace: "permissions"
+    description: "Enables recording of all runtime app ops into SQlite"
+    bug: "377584611"
+}
+
+flag {
     name: "ranging_permission_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 b97c9b5..b7e2962 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4184,7 +4184,6 @@
             MOVED_TO_SECURE.add(Secure.PARENTAL_CONTROL_REDIRECT_URL);
             MOVED_TO_SECURE.add(Secure.SETTINGS_CLASSNAME);
             MOVED_TO_SECURE.add(Secure.USE_GOOGLE_MAIL);
-            MOVED_TO_SECURE.add(Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
             MOVED_TO_SECURE.add(Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY);
             MOVED_TO_SECURE.add(Secure.WIFI_NUM_OPEN_NETWORKS_KEPT);
             MOVED_TO_SECURE.add(Secure.WIFI_ON);
@@ -4223,6 +4222,7 @@
             MOVED_TO_SECURE_THEN_GLOBAL.add(Global.USB_MASS_STORAGE_ENABLED);
             MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS);
             MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_MAX_DHCP_RETRY_COUNT);
+            MOVED_TO_SECURE_THEN_GLOBAL.add(Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
 
             // these are moving directly from system to global
             MOVED_TO_GLOBAL.add(Settings.Global.AIRPLANE_MODE_ON);
@@ -6484,6 +6484,14 @@
         public static final String SCREEN_FLASH_NOTIFICATION = "screen_flash_notification";
 
         /**
+         * Setting to enable CV (proprietary)
+         *
+         * @hide
+         */
+        public static final String CV_ENABLED =
+                "cv_enabled";
+
+        /**
          * Integer property that specifes the color for screen flash notification as a
          * packed 32-bit color.
          *
@@ -8651,6 +8659,34 @@
         public static final String DOCKED_CLOCK_FACE = "docked_clock_face";
 
         /**
+         * Setting to indicate that content filters should be enabled on web browsers.
+         *
+         * <ul>
+         *   <li>0 = Allow all sites
+         *   <li>1 = Try to block explicit sites
+         * </ul>
+         *
+         * @hide
+         */
+        @Readable
+        public static final String BROWSER_CONTENT_FILTERS_ENABLED =
+                "browser_content_filters_enabled";
+
+        /**
+         * Setting to indicate that content filters should be enabled in web search engines.
+         *
+         * <ul>
+         *   <li>0 = Off
+         *   <li>1 = Filter
+         * </ul>
+         *
+         * @hide
+         */
+        @Readable
+        public static final String SEARCH_CONTENT_FILTERS_ENABLED =
+                "search_content_filters_enabled";
+
+        /**
          * Set by the system to track if the user needs to see the call to action for
          * the lockscreen notification policy.
          * @hide
@@ -10589,6 +10625,57 @@
         public static final String GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled";
 
         /**
+         * Indicates that glanceable hub should never be started automatically.
+         *
+         * @hide
+         */
+        public static final int GLANCEABLE_HUB_START_NEVER = 0;
+
+        /**
+         * Indicates that glanceable hub should be started when charging.
+         *
+         * @hide
+         */
+        public static final int GLANCEABLE_HUB_START_CHARGING = 1;
+
+        /**
+         * Indicates that glanceable hub should be started when charging and upright.
+         *
+         * @hide
+         */
+        public static final int GLANCEABLE_HUB_START_CHARGING_UPRIGHT = 2;
+
+        /**
+         * Indicates that glanceable hub should be started when docked.
+         *
+         * @hide
+         */
+        public static final int GLANCEABLE_HUB_START_DOCKED = 3;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({
+                GLANCEABLE_HUB_START_NEVER,
+                GLANCEABLE_HUB_START_CHARGING,
+                GLANCEABLE_HUB_START_CHARGING_UPRIGHT,
+                GLANCEABLE_HUB_START_DOCKED,
+        })
+        public @interface WhenToStartGlanceableHub {
+        }
+
+        /**
+         * Indicates when to start glanceable hub. Possible values are:
+         * 0: Never
+         * 1: While charging always
+         * 2: While upright and charging
+         * 3: While docked
+         *
+         * @hide
+         */
+        public static final String WHEN_TO_START_GLANCEABLE_HUB =
+                "when_to_start_glanceable_hub";
+
+        /**
          * Whether home controls are enabled to be shown over the screensaver by the user.
          *
          * @hide
@@ -11132,6 +11219,12 @@
         public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake";
 
         /**
+         * Controls whether double tap to sleep is enabled.
+         * @hide
+         */
+        public static final String DOUBLE_TAP_TO_SLEEP = "double_tap_to_sleep";
+
+        /**
          * The current assistant component. It could be a voice interaction service,
          * or an activity that handles ACTION_ASSIST, or empty which means using the default
          * handling.
@@ -12499,6 +12592,48 @@
                 "accessibility_magnification_always_on_enabled";
 
         /**
+         * Controls how the magnification follows the cursor.
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE =
+                "accessibility_magnification_cursor_following_mode";
+
+        /**
+         * Magnification cursor following mode value for the continuous mode.
+         *
+         * @hide
+         */
+        public static final int ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CONTINUOUS = 0;
+
+        /**
+         * Magnification cursor following mode value for the center mode.
+         *
+         * @hide
+         */
+        public static final int ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CENTER = 1;
+
+        /**
+         * Magnification cursor following mode value for the edge mode.
+         *
+         * @hide
+         */
+        public static final int ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_EDGE = 2;
+
+        /**
+         * Different cursor following settings that can be used as values with
+         * {@link #ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE}.
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = { "ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_" },
+                value = {
+                        ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CONTINUOUS,
+                        ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CENTER,
+                        ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_EDGE})
+        public @interface AccessibilityMagnificationCursorFollowingMode {}
+
+        /**
          * Whether the following typing focus feature for magnification is enabled.
          * @hide
          */
@@ -12695,6 +12830,22 @@
         public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled";
 
         /**
+         * Whether the Adaptive wifi scorer switch is enabled.
+         *
+         * @hide
+         */
+        public static final String ADAPTIVE_CONNECTIVITY_WIFI_ENABLED =
+                "adaptive_connectivity_wifi_enabled";
+
+        /**
+         * Whether the Adaptive 5G PM switch is enabled.
+         *
+         * @hide
+         */
+        public static final String ADAPTIVE_CONNECTIVITY_MOBILE_NETWORK_ENABLED =
+                "adaptive_connectivity_mobile_network_enabled";
+
+        /**
          * Controls the 'Sunlight boost' toggle in wearable devices (high brightness mode).
          *
          * Valid values for this key are: '0' (disabled) or '1' (enabled).
@@ -15371,7 +15522,8 @@
          * <ul>
          * <li><pre>secure</pre>: creates a secure display</li>
          * <li><pre>own_content_only</pre>: only shows this display's own content</li>
-         * <li><pre>should_show_system_decorations</pre>: supports system decorations</li>
+         * <li><pre>should_show_system_decorations</pre>: always shows system decorations</li>
+         * <li><pre>fixed_content_mode</pre>: does not allow the content mode switch</li>
          * </ul>
          * </p><p>
          * Example:
@@ -19663,6 +19815,14 @@
         public static final String REPAIR_MODE_ACTIVE = "repair_mode_active";
 
         /**
+         * Whether the notification manager service should redact notifications that contain otps
+         * from untrusted listeners. Defaults to 1/true.
+         * @hide
+         */
+        public static final String REDACT_OTP_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS =
+                "redact_otp_notifications_from_untrusted_listeners";
+
+        /**
          * Settings migrated from Wear OS settings provider.
          * @hide
          */
@@ -20672,6 +20832,24 @@
             @Readable
             public static final String WEAR_LAUNCHER_UI_MODE = "wear_launcher_ui_mode";
 
+            /**
+             * Setting indicating whether the primary gesture input action has been enabled by the
+             * user.
+             *
+             * @hide
+             */
+            public static final String GESTURE_PRIMARY_ACTION_USER_PREFERENCE =
+                    "gesture_primary_action_user_preference";
+
+            /**
+             * Setting indicating whether the dismiss gesture input action has been enabled by the
+             * user.
+             *
+             * @hide
+             */
+            public static final String GESTURE_DISMISS_ACTION_USER_PREFERENCE =
+                    "gesture_dismiss_action_user_preference";
+
             /** Whether Wear Power Anomaly Service is enabled.
              *
              * (0 = false, 1 = true)
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 770e234..62b2bcf 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -57,6 +57,7 @@
 @SystemService(Context.ADVANCED_PROTECTION_SERVICE)
 public final class AdvancedProtectionManager {
     private static final String TAG = "AdvancedProtectionMgr";
+    private static final String PKG_SETTINGS = "com.android.settings";
 
     //TODO(b/378931989): Switch to android.app.admin.DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY
     //when the appropriate flag is launched.
@@ -123,6 +124,18 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface FeatureId {}
 
+    /** @hide */
+    public static String featureIdToString(@FeatureId int featureId) {
+        return switch(featureId) {
+            case FEATURE_ID_DISALLOW_CELLULAR_2G -> "DISALLOW_CELLULAR_2G";
+            case FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES -> "DISALLOW_INSTALL_UNKNOWN_SOURCES";
+            case FEATURE_ID_DISALLOW_USB -> "DISALLOW_USB";
+            case FEATURE_ID_DISALLOW_WEP -> "DISALLOW_WEP";
+            case FEATURE_ID_ENABLE_MTE -> "ENABLE_MTE";
+            default -> "UNKNOWN";
+        };
+    }
+
     private static final Set<Integer> ALL_FEATURE_IDS = Set.of(
             FEATURE_ID_DISALLOW_CELLULAR_2G,
             FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
@@ -146,7 +159,7 @@
             "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
 
     /**
-     * A string extra used with {@link #createSupportIntent} to identify the feature that needs to
+     * An int extra used with {@link #createSupportIntent} to identify the feature that needs to
      * show a support dialog explaining it was disabled by advanced protection.
      *
      * @hide */
@@ -155,7 +168,7 @@
             "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
 
     /**
-     * A string extra used with {@link #createSupportIntent} to identify the type of the action that
+     * An int extra used with {@link #createSupportIntent} to identify the type of the action that
      * needs to be explained in the support dialog.
      *
      * @hide */
@@ -193,6 +206,16 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface SupportDialogType {}
 
+    /** @hide */
+    public static String supportDialogTypeToString(@SupportDialogType int type) {
+        return switch(type) {
+            case SUPPORT_DIALOG_TYPE_UNKNOWN -> "UNKNOWN";
+            case SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION -> "BLOCKED_INTERACTION";
+            case SUPPORT_DIALOG_TYPE_DISABLED_SETTING -> "DISABLED_SETTING";
+            default -> "UNKNOWN";
+        };
+    }
+
     private static final Set<Integer> ALL_SUPPORT_DIALOG_TYPES = Set.of(
             SUPPORT_DIALOG_TYPE_UNKNOWN,
             SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
@@ -343,6 +366,7 @@
         }
 
         Intent intent = new Intent(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG);
+        intent.setPackage(PKG_SETTINGS);
         intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
         intent.putExtra(EXTRA_SUPPORT_DIALOG_FEATURE, featureId);
         intent.putExtra(EXTRA_SUPPORT_DIALOG_TYPE, type);
@@ -370,6 +394,17 @@
         return createSupportIntent(featureId, type);
     }
 
+    /** @hide */
+    @RequiresPermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
+    public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
+            boolean learnMoreClicked) {
+        try {
+            mService.logDialogShown(featureId, type, learnMoreClicked);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * A callback class for monitoring changes to Advanced Protection state
      *
diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
index 1939f82..0fc0fbe 100644
--- a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
+++ b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
@@ -35,4 +35,6 @@
     void setAdvancedProtectionEnabled(boolean enabled);
     @EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE")
     List<AdvancedProtectionFeature> getAdvancedProtectionFeatures();
+    @EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE")
+    void logDialogShown(int featureId, int type, boolean learnMoreClicked);
 }
\ No newline at end of file
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 7013f7d..9fd4618 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -83,13 +83,6 @@
 }
 
 flag {
-    name: "report_primary_auth_attempts"
-    namespace: "biometrics"
-    description: "Report primary auth attempts from LockSettingsService"
-    bug: "285053096"
-}
-
-flag {
     name: "dump_attestation_verifications"
     namespace: "hardware_backed_security"
     description: "Add a dump capability for attestation_verification service"
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index ae0b56e..45a21be 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -44,6 +44,16 @@
 }
 
 flag {
+  name: "notify_single_item_change_on_icon_load"
+  namespace: "intentresolver"
+  description: "ChooserGridAdapter to notify specific items change when the target icon is loaded (instead of all-item change)."
+  bug: "298193161"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "fix_resolver_memory_leak"
   is_exported: true
   namespace: "intentresolver"
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index ce31e1e..df3b8ba 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -455,7 +455,7 @@
 
                         // Simply wake up in the case the device is not locked.
                         if (!keyguardManager.isKeyguardLocked()) {
-                            wakeUp();
+                            wakeUp(false);
                             return true;
                         }
 
@@ -477,11 +477,11 @@
 
         if (!mInteractive) {
             if (mDebug) Slog.v(mTag, "Waking up on keyEvent");
-            wakeUp();
+            wakeUp(false);
             return true;
         } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
             if (mDebug) Slog.v(mTag, "Waking up on back key");
-            wakeUp();
+            wakeUp(false);
             return true;
         }
         return mWindow.superDispatchKeyEvent(event);
@@ -492,7 +492,7 @@
     public boolean dispatchKeyShortcutEvent(KeyEvent event) {
         if (!mInteractive) {
             if (mDebug) Slog.v(mTag, "Waking up on keyShortcutEvent");
-            wakeUp();
+            wakeUp(false);
             return true;
         }
         return mWindow.superDispatchKeyShortcutEvent(event);
@@ -505,7 +505,7 @@
         // but finish()es on any other kind of activity
         if (!mInteractive && event.getActionMasked() == MotionEvent.ACTION_UP) {
             if (mDebug) Slog.v(mTag, "Waking up on touchEvent");
-            wakeUp();
+            wakeUp(false);
             return true;
         }
         return mWindow.superDispatchTouchEvent(event);
@@ -516,7 +516,7 @@
     public boolean dispatchTrackballEvent(MotionEvent event) {
         if (!mInteractive) {
             if (mDebug) Slog.v(mTag, "Waking up on trackballEvent");
-            wakeUp();
+            wakeUp(false);
             return true;
         }
         return mWindow.superDispatchTrackballEvent(event);
@@ -527,7 +527,7 @@
     public boolean dispatchGenericMotionEvent(MotionEvent event) {
         if (!mInteractive) {
             if (mDebug) Slog.v(mTag, "Waking up on genericMotionEvent");
-            wakeUp();
+            wakeUp(false);
             return true;
         }
         return mWindow.superDispatchGenericMotionEvent(event);
@@ -925,32 +925,37 @@
         }
     }
 
-    private synchronized void updateDoze() {
-        if (mDreamToken == null) {
-            Slog.w(mTag, "Updating doze without a dream token.");
-            return;
-        }
-
-        if (mDozing) {
-            try {
-                Slog.v(mTag, "UpdateDoze mDozeScreenState=" + mDozeScreenState
-                        + " mDozeScreenBrightness=" + mDozeScreenBrightness
-                        + " mDozeScreenBrightnessFloat=" + mDozeScreenBrightnessFloat);
-                if (startAndStopDozingInBackground()) {
-                    mDreamManager.startDozingOneway(
-                            mDreamToken, mDozeScreenState, mDozeScreenStateReason,
-                            mDozeScreenBrightnessFloat, mDozeScreenBrightness,
-                            mUseNormalBrightnessForDoze);
-                } else {
-                    mDreamManager.startDozing(
-                            mDreamToken, mDozeScreenState, mDozeScreenStateReason,
-                            mDozeScreenBrightnessFloat, mDozeScreenBrightness,
-                            mUseNormalBrightnessForDoze);
-                }
-            } catch (RemoteException ex) {
-                // system server died
+    /**
+     * Updates doze state. Note that this must be called on the mHandler.
+     */
+    private void updateDoze() {
+        mHandler.post(() -> {
+            if (mDreamToken == null) {
+                Slog.w(mTag, "Updating doze without a dream token.");
+                return;
             }
-        }
+
+            if (mDozing) {
+                try {
+                    Slog.v(mTag, "UpdateDoze mDozeScreenState=" + mDozeScreenState
+                            + " mDozeScreenBrightness=" + mDozeScreenBrightness
+                            + " mDozeScreenBrightnessFloat=" + mDozeScreenBrightnessFloat);
+                    if (startAndStopDozingInBackground()) {
+                        mDreamManager.startDozingOneway(
+                                mDreamToken, mDozeScreenState, mDozeScreenStateReason,
+                                mDozeScreenBrightnessFloat, mDozeScreenBrightness,
+                                mUseNormalBrightnessForDoze);
+                    } else {
+                        mDreamManager.startDozing(
+                                mDreamToken, mDozeScreenState, mDozeScreenStateReason,
+                                mDozeScreenBrightnessFloat, mDozeScreenBrightness,
+                                mUseNormalBrightnessForDoze);
+                    }
+                } catch (RemoteException ex) {
+                    // system server died
+                }
+            }
+        });
     }
 
     /**
@@ -966,14 +971,20 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void stopDozing() {
-        if (mDozing) {
-            mDozing = false;
-            try {
-                mDreamManager.stopDozing(mDreamToken);
-            } catch (RemoteException ex) {
-                // system server died
+        mHandler.post(() -> {
+            if (mDreamToken == null) {
+                return;
             }
-        }
+
+            if (mDozing) {
+                mDozing = false;
+                try {
+                    mDreamManager.stopDozing(mDreamToken);
+                } catch (RemoteException ex) {
+                    // system server died
+                }
+            }
+        });
     }
 
     /**
@@ -1201,7 +1212,7 @@
             @Override
             public void onExitRequested() {
                 // Simply finish dream when exit is requested.
-                mHandler.post(() -> finish());
+                mHandler.post(() -> finishInternal());
             }
 
             @Override
@@ -1299,9 +1310,13 @@
      * </p>
      */
     public final void finish() {
+        mHandler.post(this::finishInternal);
+    }
+
+    private void finishInternal() {
         // If there is an active overlay connection, signal that the dream is ending before
-        // continuing. Note that the overlay cannot rely on the unbound state, since another dream
-        // might have bound to it in the meantime.
+        // continuing. Note that the overlay cannot rely on the unbound state, since another
+        // dream might have bound to it in the meantime.
         if (mOverlayConnection != null) {
             mOverlayConnection.addConsumer(overlay -> {
                 try {
@@ -1357,7 +1372,7 @@
      * </p>
      */
     public final void wakeUp() {
-        wakeUp(false);
+        mHandler.post(()-> wakeUp(false));
     }
 
     /**
@@ -1559,7 +1574,7 @@
         if (mActivity != null && !mActivity.isFinishing()) {
             mActivity.finishAndRemoveTask();
         } else {
-            finish();
+            finishInternal();
         }
 
         mDreamToken = null;
@@ -1719,7 +1734,7 @@
                             // the window reference in order to fully release the DreamActivity.
                             mWindow = null;
                             mActivity = null;
-                            finish();
+                            finishInternal();
                         }
 
                         if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) {
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 7660ed9..815444d 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -219,7 +219,7 @@
                 // Gets a read/write buffer mapping the entire shared memory region.
                 buffer = mRankingMapFd.mapReadWrite();
                 // Puts the ranking map into the shared memory region buffer.
-                buffer.put(mapParcel.marshall(), 0, mapSize);
+                mapParcel.marshall(buffer);
                 // Protects the region from being written to, by setting it to be read-only.
                 mRankingMapFd.setProtect(OsConstants.PROT_READ);
                 // Puts the SharedMemory object in the parcel.
diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING
index dc7129cd..ea7ee4a 100644
--- a/core/java/android/service/notification/TEST_MAPPING
+++ b/core/java/android/service/notification/TEST_MAPPING
@@ -4,7 +4,10 @@
       "name": "CtsNotificationTestCases_notification"
     },
     {
-      "name": "FrameworksUiServicesTests_notification"
+      "name": "FrameworksUiServicesNotificationTests"
+    },
+    {
+      "name": "FrameworksUiServicesZenTests"
     }
   ],
   "postsubmit": [
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1cf43d4..fce2df1 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -62,6 +62,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Build;
@@ -460,14 +461,21 @@
     }
 
     private static void readRulesFromParcel(ArrayMap<String, ZenRule> ruleMap, Parcel source) {
-        final int len = source.readInt();
+        int len = source.readInt();
         if (len > 0) {
             final String[] ids = new String[len];
-            final ZenRule[] rules = new ZenRule[len];
-            source.readStringArray(ids);
-            source.readTypedArray(rules, ZenRule.CREATOR);
+            source.readString8Array(ids);
+            ParceledListSlice<?> parceledRules = source.readParcelable(
+                    ZenRule.class.getClassLoader(), ParceledListSlice.class);
+            List<?> rules = parceledRules != null ? parceledRules.getList() : new ArrayList<>();
+            if (rules.size() != len) {
+                Slog.wtf(TAG, String.format(
+                        "Unexpected parceled rules count (%s != %s), throwing them out",
+                        rules.size(), len));
+                len = 0;
+            }
             for (int i = 0; i < len; i++) {
-                ruleMap.put(ids[i], rules[i]);
+                ruleMap.put(ids[i], (ZenRule) rules.get(i));
             }
         }
     }
@@ -485,8 +493,8 @@
         }
         dest.writeInt(user);
         dest.writeParcelable(manualRule, 0);
-        writeRulesToParcel(automaticRules, dest);
-        writeRulesToParcel(deletedRules, dest);
+        writeRulesToParcel(automaticRules, dest, flags);
+        writeRulesToParcel(deletedRules, dest, flags);
         if (!Flags.modesUi()) {
             dest.writeInt(allowAlarms ? 1 : 0);
             dest.writeInt(allowMedia ? 1 : 0);
@@ -501,18 +509,19 @@
         }
     }
 
-    private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest) {
+    private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest,
+            int flags) {
         if (!ruleMap.isEmpty()) {
             final int len = ruleMap.size();
             final String[] ids = new String[len];
-            final ZenRule[] rules = new ZenRule[len];
+            final ArrayList<ZenRule> rules = new ArrayList<>();
             for (int i = 0; i < len; i++) {
                 ids[i] = ruleMap.keyAt(i);
-                rules[i] = ruleMap.valueAt(i);
+                rules.add(ruleMap.valueAt(i));
             }
             dest.writeInt(len);
-            dest.writeStringArray(ids);
-            dest.writeTypedArray(rules, 0);
+            dest.writeString8Array(ids);
+            dest.writeParcelable(new ParceledListSlice<>(rules), flags);
         } else {
             dest.writeInt(0);
         }
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 6ed8c6d..929e39f 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -421,7 +421,12 @@
         Intent intent = new Intent(SERVICE_INTERFACE);
         intent.setComponent(serviceInfo.getComponentName());
         int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
-        mLifecycleExecutor.execute(() -> mContext.bindService(intent, this, flags));
+        if (mServiceInfo == null) {
+            mLifecycleExecutor.execute(() -> mContext.bindService(intent, this, flags));
+        } else {
+            mLifecycleExecutor.execute(() -> mContext.bindServiceAsUser(intent, this, flags,
+                    UserHandle.of(mServiceInfo.getUserId())));
+        }
         resetServiceConnectionTimeout();
     }
 
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
index e1dc6f6..e9ddfc3 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -96,6 +96,10 @@
             defaultAppPackageName = defaultPaymentApp.getPackageName();
         }
 
+        if (defaultAppPackageName == null || defaultAppUser < 0) {
+            return null;
+        }
+
         ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultAppPackageName,
                 defaultAppUser);
         if (serviceInfo == null) {
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 937aecc..0ace808 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -230,8 +230,8 @@
         }
 
         @Override
-        public void ping(IRemoteCallback callback) throws RemoteException {
-            callback.sendResult(null);
+        public void ping(IPingMe callback) throws RemoteException {
+            callback.onPing();
         }
 
         @Override
diff --git a/core/java/android/service/voice/ISandboxedDetectionService.aidl b/core/java/android/service/voice/ISandboxedDetectionService.aidl
index c76ac28..5c4503c 100644
--- a/core/java/android/service/voice/ISandboxedDetectionService.aidl
+++ b/core/java/android/service/voice/ISandboxedDetectionService.aidl
@@ -65,11 +65,15 @@
     void updateRecognitionServiceManager(
         in IRecognitionServiceManager recognitionServiceManager);
 
+    interface IPingMe {
+        void onPing();
+    }
+
     /**
      * Simply requests the service to trigger the callback, so that the system can check its
      * identity.
      */
-    void ping(in IRemoteCallback callback);
+    void ping(in IPingMe callback);
 
     void stopDetection();
 
diff --git a/core/java/android/service/voice/VisualQueryDetectionService.java b/core/java/android/service/voice/VisualQueryDetectionService.java
index 8c9731d..5dcaa3e 100644
--- a/core/java/android/service/voice/VisualQueryDetectionService.java
+++ b/core/java/android/service/voice/VisualQueryDetectionService.java
@@ -122,8 +122,8 @@
         }
 
         @Override
-        public void ping(IRemoteCallback callback) throws RemoteException {
-            callback.sendResult(null);
+        public void ping(IPingMe callback) throws RemoteException {
+            callback.onPing();
         }
 
         @Override
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 44c3f9a..0152c52 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -75,9 +75,12 @@
     // These should match the constants in framework/base/libs/hwui/hwui/DrawTextFunctor.h
     private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 0f;
     private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0f;
-    private static final float HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP = 5f;
     // since we're not using soft light yet, this needs to be much lower than the spec'd 0.8
     private static final float HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE = 0.7f;
+    @VisibleForTesting
+    static final float HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_MIN_DP = 5f;
+    @VisibleForTesting
+    static final float HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_FACTOR = 0.5f;
 
     /** @hide */
     @IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
@@ -1030,7 +1033,9 @@
 
         var padding = Math.max(HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX,
                 mPaint.getTextSize() * HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR);
-        var cornerRadius = mPaint.density * HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP;
+        var cornerRadius = Math.max(
+                mPaint.density * HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_MIN_DP,
+                mPaint.getTextSize() * HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_FACTOR);
 
         // We set the alpha on the color itself instead of Paint.setAlpha(), because that function
         // actually mutates the color in... *ehem* very strange ways. Also the color might get reset
diff --git a/core/java/android/util/MapCollections.java b/core/java/android/util/MapCollections.java
index cce3a0e..e7ceaae 100644
--- a/core/java/android/util/MapCollections.java
+++ b/core/java/android/util/MapCollections.java
@@ -355,7 +355,12 @@
             }
             return result;
         }
-    };
+
+        @Override
+        public String toString() {
+            return toStringHelper(0, this, "KeySet");
+        }
+    }
 
     final class ValuesCollection implements Collection<V> {
 
@@ -456,7 +461,12 @@
         public <T> T[] toArray(T[] array) {
             return toArrayHelper(array, 1);
         }
-    };
+
+        @Override
+        public String toString() {
+            return toStringHelper(1, this, "ValuesCollection");
+        }
+    }
 
     public static <K, V> boolean containsAllHelper(Map<K, V> map, Collection<?> collection) {
         Iterator<?> it = collection.iterator();
@@ -513,6 +523,29 @@
         return array;
     }
 
+    private String toStringHelper(int offset, Object thing, String thingName) {
+        int size = colGetSize();
+        if (size == 0) {
+            return "[]";
+        }
+
+        StringBuilder buffer = new StringBuilder(size * 14);
+        buffer.append('[');
+        for (int i = 0; i < size; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            Object entry = colGetEntry(i, offset);
+            if (entry != thing) {
+                buffer.append(entry);
+            } else {
+                buffer.append("(this ").append(thingName).append(")");
+            }
+        }
+        buffer.append(']');
+        return buffer.toString();
+    }
+
     public static <T> boolean equalsSetHelper(Set<T> set, Object object) {
         if (set == object) {
             return true;
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 7c1e497..3a2ec91 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -967,8 +967,11 @@
             DisplayEventReceiver.VsyncEventData vsyncEventData) {
         final long startNanos;
         final long frameIntervalNanos = vsyncEventData.frameInterval;
-        boolean resynced = false;
+        // Original intended vsync time that is not adjusted by jitter
+        // or buffer stuffing recovery. Reported for jank tracking.
+        final long intendedFrameTimeNanos = frameTimeNanos;
         long offsetFrameTimeNanos = frameTimeNanos;
+        boolean resynced = false;
 
         // Evaluate if buffer stuffing recovery needs to start or end, and
         // what actions need to be taken for recovery.
@@ -1012,7 +1015,6 @@
                             + ((offsetFrameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
                 }
 
-                long intendedFrameTimeNanos = offsetFrameTimeNanos;
                 startNanos = System.nanoTime();
                 // Calculating jitter involves using the original frame time without
                 // adjustments from buffer stuffing
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 3f45e29..5c81654 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -286,7 +286,7 @@
     /**
      * Display flag: Indicates that the display should show system decorations.
      * <p>
-     * This flag identifies secondary displays that should show system decorations, such as
+     * This flag identifies secondary displays that should always show system decorations, such as
      * navigation bar, home activity or wallpaper.
      * </p>
      * <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
@@ -401,6 +401,18 @@
     public static final int FLAG_ROTATES_WITH_CONTENT = 1 << 14;
 
     /**
+     * Display flag: Indicates that the display is allowed to switch the content mode between
+     * projected/extended and mirroring. This allows the display to dynamically add or remove the
+     * home and system decorations.
+     *
+     * Note that this flag shouldn't be enabled with {@link #FLAG_PRIVATE} or
+     * {@link #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} at the same time; otherwise it will be ignored.
+     *
+     * @hide
+     */
+    public static final int FLAG_ALLOWS_CONTENT_MODE_SWITCH = 1 << 15;
+
+    /**
      * Display flag: Indicates that the contents of the display should not be scaled
      * to fit the physical screen dimensions.  Used for development only to emulate
      * devices with smaller physicals screens while preserving density.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index d880072..bf000d5 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -1125,6 +1125,9 @@
         if ((flags & Display.FLAG_REAR) != 0) {
             result.append(", FLAG_REAR_DISPLAY");
         }
+        if ((flags & Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0) {
+            result.append(", FLAG_ALLOWS_CONTENT_MODE_SWITCH");
+        }
         return result.toString();
     }
 }
diff --git a/core/java/android/view/ISurfaceControlViewHost.aidl b/core/java/android/view/ISurfaceControlViewHost.aidl
index fd4b329..83aceb3 100644
--- a/core/java/android/view/ISurfaceControlViewHost.aidl
+++ b/core/java/android/view/ISurfaceControlViewHost.aidl
@@ -21,6 +21,7 @@
 import android.view.InsetsState;
 import android.view.ISurfaceControlViewHostParent;
 import android.window.ISurfaceSyncGroup;
+import android.window.InputTransferToken;
 
 /**
  * API from content embedder back to embedded content in SurfaceControlViewHost
@@ -32,6 +33,7 @@
      * APIs that are blocking
      */
     oneway void onConfigurationChanged(in Configuration newConfig);
+    oneway void onDispatchAttachedToWindow(in InputTransferToken token);
     oneway void onDispatchDetachedFromWindow();
     oneway void onInsetsChanged(in InsetsState state, in Rect insetFrame);
     ISurfaceSyncGroup getSurfaceSyncGroup();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 4fc894c..237d8f9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -699,7 +699,7 @@
     /**
      * Indicates the display should show system decors.
      * <p>
-     * System decors include status bar, navigation bar, launcher.
+     * System decors include status bar, navigation bar, launcher, and wallpaper.
      * </p>
      *
      * @param displayId The id of the display.
@@ -719,6 +719,23 @@
     void setShouldShowSystemDecors(int displayId, boolean shouldShow);
 
     /**
+     * Indicates that the display is eligible for the desktop mode from WindowManager's perspective.
+     * This includes:
+     * - The default display;
+     * - Any display that is allowed to switch the content mode between extended and mirroring
+     * (which means it can dynamically add or remove system decors), and it is now in extended mode
+     * (should currently show system decors).
+     * <p>
+     * System decors include status bar, navigation bar, launcher, and wallpaper.
+     * </p>
+     *
+     * @param displayId The id of the display.
+     * @return {@code true} if the display is eligible for the desktop mode from WindowManager's
+     * perspective.
+     */
+    boolean isEligibleForDesktopMode(int displayId);
+
+    /**
      * Indicates the policy for how the display should show IME.
      *
      * @param displayId The id of the display.
@@ -792,7 +809,8 @@
      * Updates the currently animating insets types of a remote process.
      */
     @EnforcePermission("MANAGE_APP_TOKENS")
-    void updateDisplayWindowAnimatingTypes(int displayId, int animatingTypes);
+    void updateDisplayWindowAnimatingTypes(int displayId, int animatingTypes,
+            in @nullable ImeTracker.Token statsToken);
 
     /**
      * Called to get the expected window insets.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7d6d5a2..a029303 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -277,8 +277,10 @@
      *
      * @param window The window that is insets animaiton is running.
      * @param animatingTypes Indicates the currently animating insets types.
+     * @param imeStatsToken the token tracking the current IME request or {@code null} otherwise.
      */
-    oneway void updateAnimatingTypes(IWindow window, int animatingTypes);
+    oneway void updateAnimatingTypes(IWindow window, int animatingTypes,
+            in @nullable ImeTracker.Token imeStatsToken);
 
     /**
      * Called when the system gesture exclusion has changed.
@@ -311,8 +313,9 @@
     /**
      * Update the flags on an input channel associated with a particular surface.
      */
-    oneway void updateInputChannel(in IBinder channelToken, int displayId,
-            in SurfaceControl surface, int flags, int privateFlags, int inputFeatures,
+    oneway void updateInputChannel(in IBinder channelToken,
+            in @nullable InputTransferToken hostInputTransferToken,
+            int displayId, in SurfaceControl surface, int flags, int privateFlags, int inputFeatures,
             in Region region);
 
     /**
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 6f346bd..6b7b818 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -215,8 +215,11 @@
          * contain all types, which have an ongoing animation.
          *
          * @param animatingTypes the {@link InsetsType}s that are currently animating
+         * @param statsToken the token tracking the current IME request or {@code null} otherwise.
          */
-        default void updateAnimatingTypes(@InsetsType int animatingTypes) {}
+        default void updateAnimatingTypes(@InsetsType int animatingTypes,
+                @Nullable ImeTracker.Token statsToken) {
+        }
 
         /** @see ViewRootImpl#isHandlingPointerEvent */
         default boolean isHandlingPointerEvent() {
@@ -748,7 +751,9 @@
                             mFrame, mFromState, mToState, RESIZE_INTERPOLATOR,
                             ANIMATION_DURATION_RESIZE, mTypes, InsetsController.this);
                     if (mRunningAnimations.isEmpty()) {
-                        mHost.updateAnimatingTypes(runner.getTypes());
+                        mHost.updateAnimatingTypes(runner.getTypes(),
+                                runner.getAnimationType() == ANIMATION_TYPE_HIDE
+                                        ? runner.getStatsToken() : null);
                     }
                     mRunningAnimations.add(new RunningAnimation(runner, runner.getAnimationType()));
                     mAnimatingTypes |= runner.getTypes();
@@ -1421,6 +1426,7 @@
             if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked");
             Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
             Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0);
+            ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION);
             return;
         }
         if (DEBUG) Log.d(TAG, "controlAnimation types: " + types);
@@ -1569,7 +1575,7 @@
         }
         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_ANIMATION_RUNNING);
         mAnimatingTypes |= runner.getTypes();
-        mHost.updateAnimatingTypes(mAnimatingTypes);
+        mHost.updateAnimatingTypes(mAnimatingTypes, null /* statsToken */);
         mRunningAnimations.add(new RunningAnimation(runner, animationType));
         if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
                 + useInsetsAnimationThread);
@@ -1778,7 +1784,7 @@
                 ImeTracker.forLogging().onHidden(statsToken);
             }
         }
-        reportRequestedVisibleTypes(shown ? null : runner.getStatsToken());
+        reportRequestedVisibleTypes(null /* statsToken */);
     }
 
     @Override
@@ -1835,7 +1841,8 @@
                             if (mHost != null) {
                                 // if the (hide) animation is cancelled, the
                                 // requestedVisibleTypes should be reported at this point.
-                                reportRequestedVisibleTypes(control.getStatsToken());
+                                reportRequestedVisibleTypes(!Flags.reportAnimatingInsetsTypes()
+                                        ? control.getStatsToken() : null);
                                 mHost.getInputMethodManager().removeImeSurface(
                                         mHost.getWindowToken());
                             }
@@ -1847,7 +1854,13 @@
         }
         if (removedTypes > 0) {
             mAnimatingTypes &= ~removedTypes;
-            mHost.updateAnimatingTypes(mAnimatingTypes);
+            if (mHost != null) {
+                final boolean dispatchStatsToken =
+                        Flags.reportAnimatingInsetsTypes() && (removedTypes & ime()) != 0
+                                && control.getAnimationType() == ANIMATION_TYPE_HIDE;
+                mHost.updateAnimatingTypes(mAnimatingTypes,
+                        dispatchStatsToken ? control.getStatsToken() : null);
+            }
         }
 
         onAnimationStateChanged(removedTypes, false /* running */);
@@ -1986,7 +1999,11 @@
             // report its requested visibility at the end of the animation, otherwise we would
             // lose the leash, and it would disappear during the animation
             // TODO(b/326377046) revisit this part and see if we can make it more general
-            typesToReport = mRequestedVisibleTypes | (mAnimatingTypes & ime());
+            if (Flags.reportAnimatingInsetsTypes()) {
+                typesToReport = mRequestedVisibleTypes;
+            } else {
+                typesToReport = mRequestedVisibleTypes | (mAnimatingTypes & ime());
+            }
         } else {
             typesToReport = mRequestedVisibleTypes;
         }
@@ -1999,6 +2016,11 @@
             if (Flags.refactorInsetsController()) {
                 ImeTracker.forLogging().onProgress(statsToken,
                         ImeTracker.PHASE_CLIENT_REPORT_REQUESTED_VISIBLE_TYPES);
+                if (Flags.reportAnimatingInsetsTypes() && (typesToReport & ime()) == 0) {
+                    // The IME hide animating flow should not be followed from here, but after
+                    // the hide animation has finished and Host.updateAnimatingTypes is called.
+                    statsToken = null;
+                }
             }
             mReportedRequestedVisibleTypes = mRequestedVisibleTypes;
             mHost.updateRequestedVisibleTypes(mReportedRequestedVisibleTypes, statsToken);
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index aad3bf2..901fc02 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -516,8 +516,9 @@
             mConstructorArgs[0] = inflaterContext;
             View result = root;
 
-            if (root != null && root.getViewRootImpl() != null) {
-                root.getViewRootImpl().notifyRendererOfExpensiveFrame();
+            ViewRootImpl viewRootImpl = root != null ? root.getViewRootImpl() : null;
+            if (viewRootImpl != null) {
+                viewRootImpl.notifyRendererOfExpensiveFrame();
             }
 
             try {
diff --git a/core/java/android/view/ListenerWrapper.java b/core/java/android/view/ListenerWrapper.java
new file mode 100644
index 0000000..fcf3fdb
--- /dev/null
+++ b/core/java/android/view/ListenerWrapper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * A utilty class to bundle a {@link Consumer} and an {@link Executor}
+ * @param <T> the type of value to be reported.
+ * @hide
+ */
+public class ListenerWrapper<T> {
+
+    @NonNull
+    private final Consumer<T> mConsumer;
+    @NonNull
+    private final Executor mExecutor;
+
+    public ListenerWrapper(@NonNull Executor executor, @NonNull Consumer<T> consumer) {
+        mExecutor = Objects.requireNonNull(executor);
+        mConsumer = Objects.requireNonNull(consumer);
+    }
+
+    /**
+     * Relays the new value to the {@link Consumer} using the {@link  Executor}
+     */
+    public void accept(@NonNull T value) {
+        mExecutor.execute(() -> mConsumer.accept(value));
+    }
+
+    /**
+     * Returns {@code true} if the consumer matches the one provided in the constructor,
+     * {@code false} otherwise.
+     */
+    public boolean isConsumerSame(@NonNull Consumer<T> consumer) {
+        return mConsumer.equals(consumer);
+    }
+}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 04ec4d0..ecbf1f6 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -79,6 +79,20 @@
         }
 
         @Override
+        public void onDispatchAttachedToWindow(InputTransferToken hostInputTransferToken) {
+            boolean hostInputTransferTokenChanged =
+                    !Objects.equals(hostInputTransferToken, mWm.mHostInputTransferToken);
+            if (!hostInputTransferTokenChanged) {
+                return;
+            }
+
+            mWm.setHostInputTransferToken(hostInputTransferToken);
+            if (mViewRoot != null && mViewRoot.mView != null) {
+                mWm.updateInputChannel(getWindowToken().asBinder());
+            }
+        }
+
+        @Override
         public void onDispatchDetachedFromWindow() {
             if (mViewRoot == null) {
                 return;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 4f6c730..1b57b00 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+
 import static android.view.flags.Flags.FLAG_DEPRECATE_SURFACE_VIEW_Z_ORDER_APIS;
 import static android.view.flags.Flags.FLAG_SURFACE_VIEW_GET_SURFACE_PACKAGE;
 import static android.view.flags.Flags.FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER;
@@ -23,6 +24,8 @@
 import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
 import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER;
 import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER;
+import static android.view.flags.Flags.FLAG_SURFACE_VIEW_GET_SURFACE_PACKAGE;
+import static android.view.flags.Flags.FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER;
 
 import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
@@ -59,6 +62,7 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityEmbeddedConnection;
+import android.window.InputTransferToken;
 import android.window.SurfaceSyncGroup;
 
 import com.android.graphics.hwui.flags.Flags;
@@ -347,7 +351,7 @@
                     sv.mSurfacePackage.getRemoteInterface().attachParentInterface(this);
                     mSurfaceView = sv;
                 } catch (RemoteException e) {
-                    Log.d(TAG, "Failed to attach parent interface to SCVH. Likely SCVH is alraedy "
+                    Log.d(TAG, "Failed to attach parent interface to SCVH. Likely SCVH is already "
                             + "dead.");
                 }
             }
@@ -492,10 +496,37 @@
         mTag = "SV[" + System.identityHashCode(this) + windowName + "]";
     }
 
+    private void dispatchScvhAttachedToHost() {
+        final ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot == null) {
+            return;
+        }
+
+        IBinder inputToken = viewRoot.getInputToken();
+        if (inputToken == null) {
+            // We don't have an input channel so we can't transfer focus or active
+            // touch gestures to embedded.
+            return;
+        }
+
+        try {
+            mSurfacePackage
+                    .getRemoteInterface()
+                    .onDispatchAttachedToWindow(new InputTransferToken(inputToken));
+        } catch (RemoteException e) {
+            Log.d(TAG,
+                    "Failed to onDispatchAttachedToWindow to SCVH. Likely SCVH is already "
+                            + "dead.");
+        }
+    }
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         setTag();
+        if (mSurfacePackage != null) {
+            dispatchScvhAttachedToHost();
+        }
         getViewRootImpl().addSurfaceChangedCallback(this);
         mWindowStopped = false;
         mViewVisibility = getVisibility() == VISIBLE;
@@ -2189,6 +2220,7 @@
             applyTransactionOnVriDraw(transaction);
         }
         mSurfacePackage = p;
+        dispatchScvhAttachedToHost();
         mSurfaceControlViewHostParent.attach(this);
 
         if (isFocused()) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f50d77e..f32ce6f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -34271,6 +34271,12 @@
                 frameRateToSet = velocityFrameRate;
             }
             viewRootImpl.votePreferredFrameRate(frameRateToSet, compatibility);
+
+            if (Trace.isTagEnabled(TRACE_TAG_VIEW)) {
+                Trace.instant(TRACE_TAG_VIEW,
+                        getClass().getSimpleName()
+                            + " - votePreferredFrameRate: " + frameRateToSet);
+            }
         }
 
         if (viewRootImpl.shouldCheckFrameRateCategory()) {
@@ -34306,6 +34312,13 @@
                                 | FRAME_RATE_CATEGORY_REASON_INVALID;
                     }
                 }
+
+                if (Trace.isTagEnabled(TRACE_TAG_VIEW)) {
+                    Trace.instant(TRACE_TAG_VIEW,
+                            getClass().getSimpleName() + " - votePreferredFrameRate: "
+                                + viewRootImpl.categoryToString(
+                                    frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK));
+                }
             } else {
                 // Category doesn't control it. It is directly controlled by frame rate
                 frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7dc96f2..4e3ff90 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -37,8 +37,8 @@
 import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
-import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
 import static android.view.Surface.FRAME_RATE_COMPATIBILITY_AT_LEAST;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_BOOST;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_CONFLICTED;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
@@ -555,8 +555,6 @@
     @UiContext
     public final Context mContext;
 
-    private UiModeManager mUiModeManager;
-
     @UnsupportedAppUsage
     final IWindowSession mWindowSession;
     @NonNull Display mDisplay;
@@ -2079,8 +2077,7 @@
                 // We also ignore dark theme, since the app developer can override the user's
                 // preference for dark mode in configuration.uiMode. Instead, we assume that both
                 // force invert and the system's dark theme are enabled.
-                if (getUiModeManager().getForceInvertState() ==
-                        UiModeManager.FORCE_INVERT_TYPE_DARK) {
+                if (shouldApplyForceInvertDark()) {
                     final boolean isLightTheme =
                         a.getBoolean(R.styleable.Theme_isLightTheme, false);
                     // TODO: b/372558459 - Also check the background ColorDrawable color lightness
@@ -2108,6 +2105,14 @@
         }
     }
 
+    private boolean shouldApplyForceInvertDark() {
+        final UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
+        if (uiModeManager == null) {
+            return false;
+        }
+        return uiModeManager.getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK;
+    }
+
     private void updateForceDarkMode() {
         if (mAttachInfo.mThreadedRenderer == null) return;
         if (mAttachInfo.mThreadedRenderer.setForceDark(determineForceDarkType())) {
@@ -2550,9 +2555,11 @@
 
     /**
      * Notify the when the animating insets types have changed.
+     *
+     * @hide
      */
-    @VisibleForTesting
-    public void updateAnimatingTypes(@InsetsType int animatingTypes) {
+    public void updateAnimatingTypes(@InsetsType int animatingTypes,
+            @Nullable ImeTracker.Token statsToken) {
         if (sToolkitSetFrameRateReadOnlyFlagValue) {
             boolean running = animatingTypes != 0;
             if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
@@ -2562,7 +2569,7 @@
             }
             mInsetsAnimationRunning = running;
             try {
-                mWindowSession.updateAnimatingTypes(mWindow, animatingTypes);
+                mWindowSession.updateAnimatingTypes(mWindow, animatingTypes, statsToken);
             } catch (RemoteException e) {
             }
         }
@@ -2676,7 +2683,8 @@
             mStopped = stopped;
             final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer;
             if (renderer != null) {
-                if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
+                if (DEBUG_DRAW)
+                    Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
                 renderer.setStopped(mStopped);
             }
             if (!mStopped) {
@@ -3474,6 +3482,9 @@
      * TODO(b/260382739): Apply this to all windows.
      */
     private static boolean shouldOptimizeMeasure(final WindowManager.LayoutParams lp) {
+        if (com.android.window.flags.Flags.reduceUnnecessaryMeasure()) {
+            return true;
+        }
         return (lp.privateFlags & PRIVATE_FLAG_OPTIMIZE_MEASURE) != 0;
     }
 
@@ -9410,13 +9421,6 @@
         return mAudioManager;
     }
 
-    private UiModeManager getUiModeManager() {
-        if (mUiModeManager == null) {
-            mUiModeManager = mContext.getSystemService(UiModeManager.class);
-        }
-        return mUiModeManager;
-    }
-
     private Vibrator getSystemVibrator() {
         if (mVibrator == null) {
             mVibrator = mContext.getSystemService(Vibrator.class);
@@ -13215,7 +13219,7 @@
         }
     }
 
-    private static String categoryToString(int frameRateCategory) {
+    static String categoryToString(int frameRateCategory) {
         String category;
         switch (frameRateCategory) {
             case FRAME_RATE_CATEGORY_NO_PREFERENCE -> category = "no preference";
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index 8954df6..20fa77b 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -171,9 +171,15 @@
     }
 
     @Override
-    public void updateAnimatingTypes(@WindowInsets.Type.InsetsType int animatingTypes) {
+    public void updateAnimatingTypes(@WindowInsets.Type.InsetsType int animatingTypes,
+            @Nullable ImeTracker.Token statsToken) {
         if (mViewRoot != null) {
-            mViewRoot.updateAnimatingTypes(animatingTypes);
+            ImeTracker.forLogging().onProgress(statsToken,
+                    ImeTracker.PHASE_CLIENT_UPDATE_ANIMATING_TYPES);
+            mViewRoot.updateAnimatingTypes(animatingTypes, statsToken);
+        } else {
+            ImeTracker.forLogging().onFailed(statsToken,
+                    ImeTracker.PHASE_CLIENT_UPDATE_ANIMATING_TYPES);
         }
     }
 
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 3b444c4..fc66e49 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -16,6 +16,9 @@
 
 package android.view;
 
+import static android.view.flags.Flags.FLAG_ENABLE_DISPATCH_ON_SCROLL_CHANGED;
+
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -1258,8 +1261,9 @@
     /**
      * Notifies registered listeners that something has scrolled.
      */
+    @FlaggedApi(FLAG_ENABLE_DISPATCH_ON_SCROLL_CHANGED)
     @UnsupportedAppUsage
-    final void dispatchOnScrollChanged() {
+    public final void dispatchOnScrollChanged() {
         // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
         // perform the dispatching. The iterator is a safe guard against listeners that
         // could mutate the list by calling the various add/remove methods. This prevents
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 3953334..9097849 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1840,6 +1840,16 @@
     @NonNull
     public abstract LayoutInflater getLayoutInflater();
 
+    /**
+     * Sets a user-facing title for the window.
+     * <p>
+     * This title may be shown to the user in the window's title or action bar
+     * if the {@link #requestFeature requested features} provide such a bar.
+     * It is also exposed through {@link
+     * android.view.accessibility.AccessibilityWindowInfo#getTitle}.
+     *
+     * @see WindowManager.LayoutParams#setTitle
+     */
     public abstract void setTitle(CharSequence title);
 
     @Deprecated
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 83dc79b..9d21f1a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1514,6 +1514,44 @@
             "android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY";
 
     /**
+     * Application or Activity level
+     * {@link android.content.pm.PackageManager.Property PackageManager.Property} that specifies
+     * whether this package or activity wants to allow safe region letterboxing. A safe
+     * region policy may be applied by the system to improve the user experience by ensuring that
+     * the activity does not have any content that is occluded and has the correct current
+     * window metrics.
+     *
+     * <p>Not setting the property at all defaults it to {@code true}. In such a case, the activity
+     * will be letterboxed in the safe region.
+     *
+     * <p>To not allow the safe region letterboxing, add this property to your app
+     * manifest and set the value to {@code false}. An app should ignore safe region
+     * letterboxing if it can handle bounds and insets from all four directions correctly when a
+     * request to go immersive is denied by the system. If the application does not allow safe
+     * region letterboxing, the system will not override this behavior.
+     *
+     * <p><b>Syntax:</b>
+     * <pre>
+     * &lt;application&gt;
+     *   &lt;property
+     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING"
+     *     android:value="false"/&gt;
+     * &lt;/application&gt;
+     * </pre>or
+     * <pre>
+     * &lt;activity&gt;
+     *   &lt;property
+     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING"
+     *     android:value="false"/&gt;
+     * &lt;/activity&gt;
+     * </pre>
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    String PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING =
+            "android.window.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING";
+
+    /**
      * @hide
      */
     public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
@@ -5057,6 +5095,15 @@
             format = _format;
         }
 
+        /**
+         * Sets a title for the window.
+         * <p>
+         * This title will be used primarily for debugging, and may be exposed via {@link
+         * android.view.accessibility.AccessibilityWindowInfo#getTitle} if no {@link Window#setTitle
+         * user-facing title} has been set.
+         *
+         * @see Window#setTitle
+         */
         public final void setTitle(CharSequence title) {
             if (null == title)
                 title = "";
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index a5da0c3..6242167 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -44,6 +44,7 @@
 import android.util.Pair;
 import android.util.SparseArray;
 import android.view.inputmethod.InputMethodManager;
+import android.view.translation.ListenerGroup;
 import android.window.ITrustedPresentationListener;
 import android.window.InputTransferToken;
 import android.window.TrustedPresentationThresholds;
@@ -58,6 +59,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.WeakHashMap;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -147,6 +149,12 @@
 
     @UnsupportedAppUsage
     private final ArrayList<View> mViews = new ArrayList<View>();
+    /**
+     * The {@link ListenerGroup} that is associated to {@link #mViews}.
+     * @hide
+     */
+    @GuardedBy("mLock")
+    private final ListenerGroup<List<View>> mWindowViewsListenerGroup = new ListenerGroup<>();
     @UnsupportedAppUsage
     private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
     @UnsupportedAppUsage
@@ -319,6 +327,29 @@
         }
     }
 
+    /**
+     * Adds a listener that will be notified whenever {@link #getWindowViews()} changes. The
+     * current value is provided immediately. If it was registered previously then this is ano op.
+     */
+    public void addWindowViewsListener(@NonNull Executor executor,
+            @NonNull Consumer<List<View>> consumer) {
+        synchronized (mLock) {
+            mWindowViewsListenerGroup.addListener(executor, consumer);
+            mWindowViewsListenerGroup.accept(getWindowViews());
+        }
+    }
+
+    /**
+     * Removes a listener that was registered in
+     * {@link #addWindowViewsListener(Executor, Consumer)}. If it was not registered previously,
+     * then this is a no op.
+     */
+    public void removeWindowViewsListener(@NonNull Consumer<List<View>> consumer) {
+        synchronized (mLock) {
+            mWindowViewsListenerGroup.removeListener(consumer);
+        }
+    }
+
     public View getWindowView(IBinder windowToken) {
         synchronized (mLock) {
             final int numViews = mViews.size();
@@ -454,6 +485,7 @@
             // do this last because it fires off messages to start doing things
             try {
                 root.setView(view, wparams, panelParentView, userId);
+                mWindowViewsListenerGroup.accept(getWindowViews());
             } catch (RuntimeException e) {
                 Log.e(TAG, "Couldn't add view: " + view, e);
                 final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
@@ -575,6 +607,7 @@
                 mDyingViews.remove(view);
             }
             allViewsRemoved = mRoots.isEmpty();
+            mWindowViewsListenerGroup.accept(getWindowViews());
         }
 
         // If we don't have any views anymore in our process, we no longer need the
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 0a86ff8..5af4abd 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -89,7 +89,7 @@
     protected final SurfaceControl mRootSurface;
     private final Configuration mConfiguration;
     private final IWindowSession mRealWm;
-    final InputTransferToken mHostInputTransferToken;
+    InputTransferToken mHostInputTransferToken;
     private final InputTransferToken mInputTransferToken = new InputTransferToken();
     private InsetsState mInsetsState;
     private final ClientWindowFrames mTmpFrames = new ClientWindowFrames();
@@ -128,9 +128,11 @@
         return null;
     }
 
-    /**
-     * Utility API.
-     */
+    void setHostInputTransferToken(InputTransferToken token) {
+        mHostInputTransferToken = token;
+    }
+
+    /** Utility API. */
     void setCompletionCallback(IBinder window, ResizeCompleteCallback callback) {
         if (mResizeCompletionForWindow.get(window) != null) {
             Log.w(TAG, "Unsupported overlapping resizes");
@@ -151,11 +153,25 @@
                 return;
             }
             state.mInputRegion = region != null ? new Region(region) : null;
+            updateInputChannel(window);
+        }
+    }
+
+    protected void updateInputChannel(IBinder window) {
+        State state;
+        synchronized (this) {
+            // Do everything while locked so that we synchronize with relayout. This should be a
+            // very infrequent operation.
+            state = mStateForWindow.get(window);
+            if (state == null) {
+                return;
+            }
             if (state.mInputChannelToken != null) {
                 try {
-                    mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId,
-                            state.mSurfaceControl, state.mParams.flags, state.mParams.privateFlags,
-                            state.mParams.inputFeatures, state.mInputRegion);
+                    mRealWm.updateInputChannel(state.mInputChannelToken, mHostInputTransferToken,
+                            state.mDisplayId, state.mSurfaceControl, state.mParams.flags,
+                            state.mParams.privateFlags, state.mParams.inputFeatures,
+                            state.mInputRegion);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to update surface input channel: ", e);
                 }
@@ -174,9 +190,7 @@
         }
     }
 
-    /**
-     * IWindowSession implementation.
-     */
+    /** IWindowSession implementation. */
     @Override
     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, @InsetsType int requestedVisibleTypes,
@@ -437,14 +451,15 @@
         if ((attrChanges & inputChangeMask) != 0 && state.mInputChannelToken != null) {
             try {
                 if (mRealWm instanceof IWindowSession.Stub) {
-                    mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId,
+                    mRealWm.updateInputChannel(state.mInputChannelToken, mHostInputTransferToken,
+                            state.mDisplayId,
                             new SurfaceControl(sc, "WindowlessWindowManager.relayout"),
                             attrs.flags, attrs.privateFlags, attrs.inputFeatures,
                             state.mInputRegion);
                 } else {
-                    mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc,
-                            attrs.flags, attrs.privateFlags, attrs.inputFeatures,
-                            state.mInputRegion);
+                    mRealWm.updateInputChannel(state.mInputChannelToken, mHostInputTransferToken,
+                            state.mDisplayId, sc, attrs.flags, attrs.privateFlags,
+                            attrs.inputFeatures, state.mInputRegion);
                 }
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to update surface input channel: ", e);
@@ -597,7 +612,8 @@
     }
 
     @Override
-    public void updateAnimatingTypes(IWindow window, @InsetsType int animatingTypes) {
+    public void updateAnimatingTypes(IWindow window, @InsetsType int animatingTypes,
+            @Nullable ImeTracker.Token statsToken) {
         // NO-OP
     }
 
@@ -623,8 +639,9 @@
     }
 
     @Override
-    public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
-            int flags, int privateFlags, int inputFeatures, Region region) {
+    public void updateInputChannel(IBinder channelToken, InputTransferToken hostInputToken,
+            int displayId, SurfaceControl surface, int flags, int privateFlags, int inputFeatures,
+            Region region) {
     }
 
     @Override
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index e43fb48..c97c4ac 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -866,31 +866,18 @@
     }
 
     /**
-     * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services
-     * for a given feedback type.
-     *
-     * @param feedbackTypeFlags The feedback type flags.
-     * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
-     *
-     * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
-     * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
-     * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
-     * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
-     * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
-     * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
+     * @see #getEnabledAccessibilityServiceList(int)
+     * @hide
      */
     public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
-            int feedbackTypeFlags) {
+            int feedbackTypeFlags, int userId) {
         final IAccessibilityManager service;
-        final int userId;
         synchronized (mLock) {
             service = getServiceLocked();
             if (service == null) {
                 return Collections.emptyList();
             }
-            userId = mUserId;
         }
-
         List<AccessibilityServiceInfo> services = null;
         try {
             services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
@@ -912,6 +899,29 @@
     }
 
     /**
+     * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services
+     * for a given feedback type.
+     *
+     * @param feedbackTypeFlags The feedback type flags.
+     * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
+     *
+     * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
+     * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
+     * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
+     * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
+     * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
+     * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
+     */
+    public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+            int feedbackTypeFlags) {
+        final int userId;
+        synchronized (mLock) {
+            userId = mUserId;
+        }
+        return getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
+    }
+
+    /**
      * Returns whether the user must be shown the AccessibilityService warning dialog
      * before the AccessibilityService (or any shortcut for the service) can be enabled.
      * @hide
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 7b6e070..c17002e 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -160,6 +160,9 @@
 
     /**
      * Gets the title of the window.
+     * <p>
+     * This is taken from the {@link android.view.Window}'s title, or its {@link
+     * android.view.WindowManager.LayoutParams} if that is unset.
      *
      * @return The title of the window, or {@code null} if none is available.
      */
diff --git a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
index 8c98fa4..3780db3 100644
--- a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
+++ b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
@@ -21,6 +21,7 @@
     namespace: "pixel_state_server"
     description: "Feature flag to send a flush event after each frame"
     bug: "380381249"
+    is_exported: true
     is_fixed_read_only: true
     metadata {
         purpose: PURPOSE_BUGFIX
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index d06f885..d973104 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -159,3 +159,11 @@
     bug: "364653005"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "root_view_changed_listener"
+    namespace: "windowing_sdk"
+    description: "Implement listener pattern for WindowInspector#getGlobalWindowViews."
+    bug: "394397033"
+    is_fixed_read_only: false
+}
diff --git a/core/java/android/view/flags/view_tree_observer_flags.aconfig b/core/java/android/view/flags/view_tree_observer_flags.aconfig
new file mode 100644
index 0000000..82f3300
--- /dev/null
+++ b/core/java/android/view/flags/view_tree_observer_flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.view.flags"
+container: "system"
+
+flag {
+    name: "enable_dispatch_on_scroll_changed"
+    namespace: "toolkit"
+    description: "Feature flag for enabling the dispatchOnScrollChanged method in ViewTreeObserver."
+    bug: "238109286"
+}
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index eca798d..2908855 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -379,7 +379,7 @@
             @Nullable IRemoteInputConnection remoteInputConnection,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible) {
         final IInputMethodManager service = getService();
         if (service == null) {
             return InputBindResult.NULL;
@@ -388,7 +388,7 @@
             return service.startInputOrWindowGainedFocus(startInputReason, client, windowToken,
                     startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
                     remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
-                    imeDispatcher);
+                    imeDispatcher, imeRequestedVisible);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -408,7 +408,8 @@
             @Nullable IRemoteInputConnection remoteInputConnection,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean useAsyncShowHideMethod) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible,
+            boolean useAsyncShowHideMethod) {
         final IInputMethodManager service = getService();
         if (service == null) {
             return -1;
@@ -417,7 +418,8 @@
             service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken,
                     startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
                     remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
-                    imeDispatcher, advanceAngGetStartInputSequenceNumber(), useAsyncShowHideMethod);
+                    imeDispatcher, imeRequestedVisible, advanceAngGetStartInputSequenceNumber(),
+                    useAsyncShowHideMethod);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index 60178cd..5dadf32 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -226,6 +226,11 @@
             PHASE_WM_DISPLAY_IME_CONTROLLER_SET_IME_REQUESTED_VISIBLE,
             PHASE_WM_UPDATE_DISPLAY_WINDOW_REQUESTED_VISIBLE_TYPES,
             PHASE_WM_REQUESTED_VISIBLE_TYPES_NOT_CHANGED,
+            PHASE_CLIENT_UPDATE_ANIMATING_TYPES,
+            PHASE_WM_UPDATE_ANIMATING_TYPES,
+            PHASE_WM_WINDOW_ANIMATING_TYPES_CHANGED,
+            PHASE_WM_NOTIFY_HIDE_ANIMATION_FINISHED,
+            PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface Phase {}
@@ -449,6 +454,21 @@
     /** The requestedVisibleTypes have not been changed, so this request is not continued. */
     int PHASE_WM_REQUESTED_VISIBLE_TYPES_NOT_CHANGED =
             ImeProtoEnums.PHASE_WM_REQUESTED_VISIBLE_TYPES_NOT_CHANGED;
+    /** Updating the currently animating types on the client side. */
+    int PHASE_CLIENT_UPDATE_ANIMATING_TYPES =
+            ImeProtoEnums.PHASE_CLIENT_UPDATE_ANIMATING_TYPES;
+    /** Updating the animating types in the WindowState on the WindowManager side. */
+    int PHASE_WM_UPDATE_ANIMATING_TYPES =
+            ImeProtoEnums.PHASE_WM_UPDATE_ANIMATING_TYPES;
+    /** Animating types of the WindowState have changed, now sending them to state controller. */
+    int PHASE_WM_WINDOW_ANIMATING_TYPES_CHANGED =
+            ImeProtoEnums.PHASE_WM_WINDOW_ANIMATING_TYPES_CHANGED;
+    /** ImeInsetsSourceProvider got notified that the hide animation is finished. */
+    int PHASE_WM_NOTIFY_HIDE_ANIMATION_FINISHED =
+            ImeProtoEnums.PHASE_WM_NOTIFY_HIDE_ANIMATION_FINISHED;
+    /** The control target reported its animatingTypes back to WindowManagerService. */
+    int PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES =
+            ImeProtoEnums.PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES;
 
     /**
      * Called when an IME request is started.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0b34600..b3bd89c 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -871,6 +871,19 @@
         IInputMethodManagerGlobalInvoker.reportPerceptibleAsync(windowToken, perceptible);
     }
 
+    private static boolean hasViewImeRequestedVisible(View view) {
+        // before the refactor, the requestedVisibleTypes for the IME were not in sync with
+        // the state that was actually requested.
+        if (Flags.refactorInsetsController() && view != null) {
+            final var controller = view.getWindowInsetsController();
+            if (controller != null) {
+                return (view.getWindowInsetsController()
+                        .getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0;
+            }
+        }
+        return false;
+    }
+
     private final class DelegateImpl implements
             ImeFocusController.InputMethodManagerDelegate {
 
@@ -941,6 +954,9 @@
                     Log.v(TAG, "Reporting focus gain, without startInput");
                 }
 
+                final boolean imeRequestedVisible = hasViewImeRequestedVisible(
+                        mCurRootView.getView());
+
                 // ignore the result
                 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus");
                 IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
@@ -950,7 +966,7 @@
                         null,
                         null, null,
                         mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
-                        UserHandle.myUserId(), mImeDispatcher);
+                        UserHandle.myUserId(), mImeDispatcher, imeRequestedVisible);
                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             }
         }
@@ -2438,19 +2454,28 @@
                                 & WindowInsets.Type.ime()) == 0
                         || viewRootImpl.getInsetsController()
                                 .isPredictiveBackImeHideAnimInProgress())) {
+                    Handler vh = view.getHandler();
                     ImeTracker.forLogging().onProgress(statsToken,
                             ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION);
                     if (resultReceiver != null) {
-                        final boolean imeReqVisible =
-                                (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
-                                        & WindowInsets.Type.ime()) != 0;
+                        final boolean imeReqVisible = hasViewImeRequestedVisible(
+                                viewRootImpl.getView());
                         resultReceiver.send(
                                 imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
                                         : InputMethodManager.RESULT_SHOWN, null);
                     }
                     // TODO(b/322992891) handle case of SHOW_IMPLICIT
-                    viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
-                            false /* fromIme */, statsToken);
+                    if (vh.getLooper() != Looper.myLooper()) {
+                        // The view is running on a different thread than our own, so
+                        // we need to reschedule our work for over there.
+                        if (DEBUG) Log.v(TAG, "Show soft input: reschedule to view thread");
+                        final var finalStatsToken = statsToken;
+                        vh.post(() -> viewRootImpl.getInsetsController().show(
+                                WindowInsets.Type.ime(), false /* fromIme */, finalStatsToken));
+                    } else {
+                        viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
+                                false /* fromIme */, statsToken);
+                    }
                     return true;
                 }
                 ImeTracker.forLogging().onCancelled(statsToken,
@@ -2656,9 +2681,8 @@
                     ImeTracker.forLogging().onProgress(statsToken,
                             ImeTracker.PHASE_CLIENT_VIEW_HANDLER_AVAILABLE);
 
-                    final boolean imeReqVisible =
-                            (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
-                                    & WindowInsets.Type.ime()) != 0;
+                    final boolean imeReqVisible = hasViewImeRequestedVisible(
+                            viewRootImpl.getView());
                     if (resultReceiver != null) {
                         resultReceiver.send(
                                 !imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_HIDDEN
@@ -3412,6 +3436,7 @@
         final Handler icHandler;
         InputBindResult res = null;
         final boolean hasServedView;
+        final boolean imeRequestedVisible;
         synchronized (mH) {
             // Now that we are locked again, validate that our state hasn't
             // changed.
@@ -3479,10 +3504,13 @@
             }
             mServedInputConnection = servedInputConnection;
 
+            imeRequestedVisible = hasViewImeRequestedVisible(servedView);
+
             if (DEBUG) {
                 Log.v(TAG, "START INPUT: view=" + InputMethodDebug.dumpViewInfo(view)
                         + " ic=" + ic + " editorInfo=" + editorInfo + " startInputFlags="
-                        + InputMethodDebug.startInputFlagsToString(startInputFlags));
+                        + InputMethodDebug.startInputFlagsToString(startInputFlags)
+                        + " imeRequestedVisible=" + imeRequestedVisible);
             }
 
             // When we switch between non-editable views, do not call into the IMMS.
@@ -3513,7 +3541,7 @@
                         servedInputConnection == null ? null
                                 : servedInputConnection.asIRemoteAccessibilityInputConnection(),
                         view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
-                        mImeDispatcher, mAsyncShowHideMethodEnabled);
+                        mImeDispatcher, imeRequestedVisible, mAsyncShowHideMethodEnabled);
             } else {
                 res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
                         startInputReason, mClient, windowGainingFocus, startInputFlags,
@@ -3521,7 +3549,7 @@
                         servedInputConnection == null ? null
                                 : servedInputConnection.asIRemoteAccessibilityInputConnection(),
                         view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
-                        mImeDispatcher);
+                        mImeDispatcher, imeRequestedVisible);
             }
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             if (Flags.useZeroJankProxy()) {
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index 3557f16..ced27d6 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -28,7 +28,6 @@
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UiThread;
 import android.app.UriGrantsManager;
 import android.content.ContentProvider;
 import android.content.Intent;
@@ -38,6 +37,7 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.CancellationSignalBeamer;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -468,13 +468,27 @@
         });
     }
 
+    /**
+     * Returns {@code false} if there is a sessionId mismatch and logs the event.
+     */
+    private boolean checkSessionId(@NonNull InputConnectionCommandHeader header) {
+        if (header.mSessionId != mCurrentSessionId.get()) {
+            Log.w(TAG, "Session id mismatch header.sessionId: " + header.mSessionId
+                            + " currentSessionId: " + mCurrentSessionId.get() + " while calling "
+                    + Debug.getCaller());
+            //TODO(b/396066692): log metrics.
+            return false;  // cancelled
+        }
+        return true;
+    }
+
     @Dispatching(cancellable = true)
     @Override
     public void getTextAfterCursor(InputConnectionCommandHeader header, int length, int flags,
             AndroidFuture future /* T=CharSequence */) {
         dispatchWithTracing("getTextAfterCursor", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return null;  // cancelled
+            if (!checkSessionId(header)) {
+                return null;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -495,8 +509,8 @@
     public void getTextBeforeCursor(InputConnectionCommandHeader header, int length, int flags,
             AndroidFuture future /* T=CharSequence */) {
         dispatchWithTracing("getTextBeforeCursor", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return null;  // cancelled
+            if (!checkSessionId(header)) {
+                return null;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -517,8 +531,8 @@
     public void getSelectedText(InputConnectionCommandHeader header, int flags,
             AndroidFuture future /* T=CharSequence */) {
         dispatchWithTracing("getSelectedText", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return null;  // cancelled
+            if (!checkSessionId(header)) {
+                return null;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -539,8 +553,8 @@
     public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
             int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
         dispatchWithTracing("getSurroundingText", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return null;  // cancelled
+            if (!checkSessionId(header)) {
+                return null;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -567,8 +581,8 @@
     public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
             AndroidFuture future /* T=Integer */) {
         dispatchWithTracing("getCursorCapsMode", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return 0;  // cancelled
+            if (!checkSessionId(header)) {
+                return 0;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -584,8 +598,8 @@
     public void getExtractedText(InputConnectionCommandHeader header, ExtractedTextRequest request,
             int flags, AndroidFuture future /* T=ExtractedText */) {
         dispatchWithTracing("getExtractedText", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return null;  // cancelled
+            if (!checkSessionId(header)) {
+                return null;
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -601,8 +615,8 @@
     public void commitText(InputConnectionCommandHeader header, CharSequence text,
             int newCursorPosition) {
         dispatchWithTracing("commitText", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return;
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -618,8 +632,8 @@
     public void commitTextWithTextAttribute(InputConnectionCommandHeader header, CharSequence text,
             int newCursorPosition, @Nullable TextAttribute textAttribute) {
         dispatchWithTracing("commitTextWithTextAttribute", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -634,8 +648,8 @@
     @Override
     public void commitCompletion(InputConnectionCommandHeader header, CompletionInfo text) {
         dispatchWithTracing("commitCompletion", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -650,8 +664,8 @@
     @Override
     public void commitCorrection(InputConnectionCommandHeader header, CorrectionInfo info) {
         dispatchWithTracing("commitCorrection", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -670,8 +684,8 @@
     @Override
     public void setSelection(InputConnectionCommandHeader header, int start, int end) {
         dispatchWithTracing("setSelection", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -686,8 +700,8 @@
     @Override
     public void performEditorAction(InputConnectionCommandHeader header, int id) {
         dispatchWithTracing("performEditorAction", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -702,8 +716,8 @@
     @Override
     public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
         dispatchWithTracing("performContextMenuAction", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -718,8 +732,8 @@
     @Override
     public void setComposingRegion(InputConnectionCommandHeader header, int start, int end) {
         dispatchWithTracing("setComposingRegion", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -739,8 +753,8 @@
     public void setComposingRegionWithTextAttribute(InputConnectionCommandHeader header, int start,
             int end, @Nullable TextAttribute textAttribute) {
         dispatchWithTracing("setComposingRegionWithTextAttribute", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -756,8 +770,8 @@
     public void setComposingText(InputConnectionCommandHeader header, CharSequence text,
             int newCursorPosition) {
         dispatchWithTracing("setComposingText", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -773,8 +787,8 @@
     public void setComposingTextWithTextAttribute(InputConnectionCommandHeader header,
             CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute) {
         dispatchWithTracing("setComposingTextWithTextAttribute", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -826,8 +840,8 @@
                 }
                 return;
             }
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null && mDeactivateRequested.get()) {
@@ -842,8 +856,8 @@
     @Override
     public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
         dispatchWithTracing("sendKeyEvent", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -858,8 +872,8 @@
     @Override
     public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
         dispatchWithTracing("clearMetaKeyStates", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -875,8 +889,8 @@
     public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
             int afterLength) {
         dispatchWithTracing("deleteSurroundingText", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -892,8 +906,8 @@
     public void deleteSurroundingTextInCodePoints(InputConnectionCommandHeader header,
             int beforeLength, int afterLength) {
         dispatchWithTracing("deleteSurroundingTextInCodePoints", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -912,8 +926,8 @@
     @Override
     public void beginBatchEdit(InputConnectionCommandHeader header) {
         dispatchWithTracing("beginBatchEdit", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -928,8 +942,8 @@
     @Override
     public void endBatchEdit(InputConnectionCommandHeader header) {
         dispatchWithTracing("endBatchEdit", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -944,8 +958,8 @@
     @Override
     public void performSpellCheck(InputConnectionCommandHeader header) {
         dispatchWithTracing("performSpellCheck", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -961,8 +975,8 @@
     public void performPrivateCommand(InputConnectionCommandHeader header, String action,
             Bundle data) {
         dispatchWithTracing("performPrivateCommand", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -995,12 +1009,12 @@
             }
         }
         dispatchWithTracing("performHandwritingGesture", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
+            if (!checkSessionId(header)) {
                 if (resultReceiver != null) {
                     resultReceiver.send(
                             InputConnection.HANDWRITING_GESTURE_RESULT_CANCELLED, null);
                 }
-                return;  // cancelled
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -1038,9 +1052,9 @@
                 (PreviewableHandwritingGesture) gestureContainer.get();
 
         dispatchWithTracing("previewHandwritingGesture", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()
+            if (!checkSessionId(header)
                     || (cancellationSignal != null && cancellationSignal.isCanceled())) {
-                return;  // cancelled
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -1065,8 +1079,8 @@
     public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
             int imeDisplayId, AndroidFuture future /* T=Boolean */) {
         dispatchWithTracing("requestCursorUpdates", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return false;  // cancelled
+            if (!checkSessionId(header)) {
+                return false; // cancelled.
             }
             return requestCursorUpdatesInternal(
                     cursorUpdateMode, 0 /* cursorUpdateFilter */, imeDisplayId);
@@ -1079,8 +1093,8 @@
             int cursorUpdateMode, int cursorUpdateFilter, int imeDisplayId,
             AndroidFuture future /* T=Boolean */) {
         dispatchWithTracing("requestCursorUpdates", future, () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return false;  // cancelled
+            if (!checkSessionId(header)) {
+                return false; // cancelled.
             }
             return requestCursorUpdatesInternal(
                     cursorUpdateMode, cursorUpdateFilter, imeDisplayId);
@@ -1123,9 +1137,9 @@
             InputConnectionCommandHeader header, RectF bounds,
             @NonNull ResultReceiver resultReceiver) {
         dispatchWithTracing("requestTextBoundsInfo", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
+            if (!checkSessionId(header)) {
                 resultReceiver.send(TextBoundsInfoResult.CODE_CANCELLED, null);
-                return;  // cancelled
+                return; // cancelled
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -1168,8 +1182,8 @@
                 return false;
             }
 
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return false;  // cancelled
+            if (!checkSessionId(header)) {
+                return false; // cancelled.
             }
             final InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -1193,8 +1207,8 @@
     @Override
     public void setImeConsumesInput(InputConnectionCommandHeader header, boolean imeConsumesInput) {
         dispatchWithTracing("setImeConsumesInput", () -> {
-            if (header.mSessionId != mCurrentSessionId.get()) {
-                return;  // cancelled
+            if (!checkSessionId(header)) {
+                return; // cancelled.
             }
             InputConnection ic = getInputConnection();
             if (ic == null || mDeactivateRequested.get()) {
@@ -1217,8 +1231,8 @@
         dispatchWithTracing(
                 "replaceText",
                 () -> {
-                    if (header.mSessionId != mCurrentSessionId.get()) {
-                        return; // cancelled
+                    if (!checkSessionId(header)) {
+                        return; // cancelled.
                     }
                     InputConnection ic = getInputConnection();
                     if (ic == null || mDeactivateRequested.get()) {
@@ -1236,8 +1250,8 @@
         public void commitText(InputConnectionCommandHeader header, CharSequence text,
                 int newCursorPosition, @Nullable TextAttribute textAttribute) {
             dispatchWithTracing("commitTextFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1256,8 +1270,8 @@
         @Override
         public void setSelection(InputConnectionCommandHeader header, int start, int end) {
             dispatchWithTracing("setSelectionFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1273,8 +1287,8 @@
         public void getSurroundingText(InputConnectionCommandHeader header, int beforeLength,
                 int afterLength, int flags, AndroidFuture future /* T=SurroundingText */) {
             dispatchWithTracing("getSurroundingTextFromA11yIme", future, () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return null;  // cancelled
+                if (!checkSessionId(header)) {
+                    return null; // cancelled.
                 }
                 final InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1301,8 +1315,8 @@
         public void deleteSurroundingText(InputConnectionCommandHeader header, int beforeLength,
                 int afterLength) {
             dispatchWithTracing("deleteSurroundingTextFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1317,8 +1331,8 @@
         @Override
         public void sendKeyEvent(InputConnectionCommandHeader header, KeyEvent event) {
             dispatchWithTracing("sendKeyEventFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1333,8 +1347,8 @@
         @Override
         public void performEditorAction(InputConnectionCommandHeader header, int id) {
             dispatchWithTracing("performEditorActionFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1349,8 +1363,8 @@
         @Override
         public void performContextMenuAction(InputConnectionCommandHeader header, int id) {
             dispatchWithTracing("performContextMenuActionFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1366,8 +1380,8 @@
         public void getCursorCapsMode(InputConnectionCommandHeader header, int reqModes,
                 AndroidFuture future /* T=Integer */) {
             dispatchWithTracing("getCursorCapsModeFromA11yIme", future, () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return 0;  // cancelled
+                if (!checkSessionId(header)) {
+                    return 0; // cancelled.
                 }
                 final InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
@@ -1382,8 +1396,8 @@
         @Override
         public void clearMetaKeyStates(InputConnectionCommandHeader header, int states) {
             dispatchWithTracing("clearMetaKeyStatesFromA11yIme", () -> {
-                if (header.mSessionId != mCurrentSessionId.get()) {
-                    return;  // cancelled
+                if (!checkSessionId(header)) {
+                    return; // cancelled.
                 }
                 InputConnection ic = getInputConnection();
                 if (ic == null || mDeactivateRequested.get()) {
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index a4ea64e..cdca410 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -214,3 +214,24 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+  name: "invalidate_input_calls_restart"
+  namespace: "input_method"
+  description: "Feature flag to fix the race between invalidateInput and restartInput"
+  bug: "396066692"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+    name: "disallow_disabling_ime_navigation_bar"
+    namespace: "input_method"
+    description: "Disallows disabling the IME navigation bar through canImeRenderGesturalNavButtons"
+    bug: "402442590"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/view/inspector/WindowInspector.java b/core/java/android/view/inspector/WindowInspector.java
index 69d004e..3ebca3c 100644
--- a/core/java/android/view/inspector/WindowInspector.java
+++ b/core/java/android/view/inspector/WindowInspector.java
@@ -16,11 +16,14 @@
 
 package android.view.inspector;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.view.View;
 import android.view.WindowManagerGlobal;
 
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Provides access to window inspection information.
@@ -37,4 +40,25 @@
     public static List<View> getGlobalWindowViews() {
         return WindowManagerGlobal.getInstance().getWindowViews();
     }
+
+    /**
+     * Adds a listener that is notified whenever the list of global window views changes. If a
+     * {@link Consumer} is already registered this method is a no op.
+     * @see #getGlobalWindowViews()
+     */
+    @FlaggedApi(android.view.flags.Flags.FLAG_ROOT_VIEW_CHANGED_LISTENER)
+    public static void addGlobalWindowViewsListener(@NonNull Executor executor,
+            @NonNull Consumer<List<View>> consumer) {
+        WindowManagerGlobal.getInstance().addWindowViewsListener(executor, consumer);
+    }
+
+    /**
+     * Removes a listener from getting notifications of global window views changes. If the
+     * {@link Consumer} is not registered this method is a no op.
+     * @see #addGlobalWindowViewsListener(Executor, Consumer)
+     */
+    @FlaggedApi(android.view.flags.Flags.FLAG_ROOT_VIEW_CHANGED_LISTENER)
+    public static void removeGlobalWindowViewsListener(@NonNull Consumer<List<View>> consumer) {
+        WindowManagerGlobal.getInstance().removeWindowViewsListener(consumer);
+    }
 }
diff --git a/core/java/android/view/translation/ListenerGroup.java b/core/java/android/view/translation/ListenerGroup.java
new file mode 100644
index 0000000..bf50681
--- /dev/null
+++ b/core/java/android/view/translation/ListenerGroup.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.annotation.NonNull;
+import android.view.ListenerWrapper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * A utility class to manage a list of {@link ListenerWrapper}. This class is not thread safe.
+ * @param <T> the type of the value to be reported.
+ * @hide
+ */
+public class ListenerGroup<T> {
+    private final List<ListenerWrapper<T>> mListeners = new ArrayList<>();
+
+    /**
+     * Relays the value to all the registered {@link java.util.function.Consumer}
+     */
+    public void accept(@NonNull T value) {
+        Objects.requireNonNull(value);
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).accept(value);
+        }
+    }
+
+    /**
+     * Adds a {@link Consumer} to the group. If the {@link Consumer} is already present then this
+     * is a no op.
+     */
+    public void addListener(@NonNull Executor executor, @NonNull Consumer<T> consumer) {
+        if (isContained(consumer)) {
+            return;
+        }
+        mListeners.add(new ListenerWrapper<>(executor, consumer));
+    }
+
+    /**
+     * Removes a {@link Consumer} from the group. If the {@link Consumer} was not present then this
+     * is a no op.
+     */
+    public void removeListener(@NonNull Consumer<T> consumer) {
+        final int index = computeIndex(consumer);
+        if (index > -1) {
+            mListeners.remove(index);
+        }
+    }
+
+    /**
+     * Returns {@code true} if the {@link Consumer} is present in the list, {@code false}
+     * otherwise.
+     */
+    private boolean isContained(Consumer<T> consumer) {
+        return computeIndex(consumer) > -1;
+    }
+
+    /**
+     * Returns the index of the matching {@link ListenerWrapper} if present, {@code -1} otherwise.
+     */
+    private int computeIndex(Consumer<T> consumer) {
+        for (int i = 0; i < mListeners.size(); i++) {
+            if (mListeners.get(i).isConsumerSame(consumer)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 3f611c7..a328c78 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -72,7 +72,7 @@
 
 /**
  * <p>Displays a vertically-scrollable collection of views, where each view is positioned
- * immediatelybelow the previous view in the list.  For a more modern, flexible, and performant
+ * immediately below the previous view in the list.  For a more modern, flexible, and performant
  * approach to displaying lists, use {@link androidx.recyclerview.widget.RecyclerView}.</p>
  *
  * <p>To display a list, you can include a list view in your layout XML file:</p>
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 222a7b3..3dfbc25 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -9216,7 +9216,13 @@
         public static RemoteResponse fromFillInIntent(@NonNull Intent fillIntent) {
             RemoteResponse response = new RemoteResponse();
             response.mFillIntent = fillIntent;
-            fillIntent.collectExtraIntentKeys();
+            if (fillIntent != null) {
+                // Although the parameter is marked as @NonNull, it is nullable. The method that
+                // calls it (RemoteReviews.setOnClickFillInIntent()) passes its fillInIntent
+                // parameter to this method and it does not guarantee that the fillInIntent is
+                // non-null.
+                fillIntent.collectExtraIntentKeys();
+            }
             return response;
         }
 
diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java
index cc2afbc..d53c787 100644
--- a/core/java/android/window/BackMotionEvent.java
+++ b/core/java/android/window/BackMotionEvent.java
@@ -18,7 +18,6 @@
 
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.RemoteAnimationTarget;
@@ -39,8 +38,6 @@
 
     @BackEvent.SwipeEdge
     private final int mSwipeEdge;
-    @Nullable
-    private final RemoteAnimationTarget mDepartingAnimationTarget;
 
     /**
      * Creates a new {@link BackMotionEvent} instance.
@@ -53,8 +50,6 @@
      * @param progress Value between 0 and 1 on how far along the back gesture is.
      * @param triggerBack Indicates whether the back arrow is in the triggered state or not
      * @param swipeEdge Indicates which edge the swipe starts from.
-     * @param departingAnimationTarget The remote animation target of the departing
-     *                                 application window.
      */
     public BackMotionEvent(
             float touchX,
@@ -62,15 +57,13 @@
             long frameTimeMillis,
             float progress,
             boolean triggerBack,
-            @BackEvent.SwipeEdge int swipeEdge,
-            @Nullable RemoteAnimationTarget departingAnimationTarget) {
+            @BackEvent.SwipeEdge int swipeEdge) {
         mTouchX = touchX;
         mTouchY = touchY;
         mFrameTimeMillis = frameTimeMillis;
         mProgress = progress;
         mTriggerBack = triggerBack;
         mSwipeEdge = swipeEdge;
-        mDepartingAnimationTarget = departingAnimationTarget;
     }
 
     private BackMotionEvent(@NonNull Parcel in) {
@@ -79,7 +72,6 @@
         mProgress = in.readFloat();
         mTriggerBack = in.readBoolean();
         mSwipeEdge = in.readInt();
-        mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
         mFrameTimeMillis = in.readLong();
     }
 
@@ -108,7 +100,6 @@
         dest.writeFloat(mProgress);
         dest.writeBoolean(mTriggerBack);
         dest.writeInt(mSwipeEdge);
-        dest.writeTypedObject(mDepartingAnimationTarget, flags);
         dest.writeLong(mFrameTimeMillis);
     }
 
@@ -160,16 +151,6 @@
         return mFrameTimeMillis;
     }
 
-    /**
-     * Returns the {@link RemoteAnimationTarget} of the top departing application window,
-     * or {@code null} if the top window should not be moved for the current type of back
-     * destination.
-     */
-    @Nullable
-    public RemoteAnimationTarget getDepartingAnimationTarget() {
-        return mDepartingAnimationTarget;
-    }
-
     @Override
     public String toString() {
         return "BackMotionEvent{"
@@ -179,7 +160,6 @@
                 + ", mProgress=" + mProgress
                 + ", mTriggerBack=" + mTriggerBack
                 + ", mSwipeEdge=" + mSwipeEdge
-                + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget
                 + "}";
     }
 }
diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java
index 4908068..ea1b6406 100644
--- a/core/java/android/window/BackTouchTracker.java
+++ b/core/java/android/window/BackTouchTracker.java
@@ -20,7 +20,6 @@
 import android.os.SystemProperties;
 import android.util.MathUtils;
 import android.view.MotionEvent;
-import android.view.RemoteAnimationTarget;
 
 import java.io.PrintWriter;
 
@@ -147,15 +146,14 @@
     }
 
     /** Creates a start {@link BackMotionEvent}. */
-    public BackMotionEvent createStartEvent(RemoteAnimationTarget target) {
+    public BackMotionEvent createStartEvent() {
         return new BackMotionEvent(
                 /* touchX = */ mInitTouchX,
                 /* touchY = */ mInitTouchY,
                 /* frameTimeMillis = */ 0,
                 /* progress = */ 0,
                 /* triggerBack = */ mTriggerBack,
-                /* swipeEdge = */ mSwipeEdge,
-                /* departingAnimationTarget = */ target);
+                /* swipeEdge = */ mSwipeEdge);
     }
 
     /** Creates a progress {@link BackMotionEvent}. */
@@ -239,8 +237,7 @@
                 /* frameTimeMillis = */ 0,
                 /* progress = */ progress,
                 /* triggerBack = */ mTriggerBack,
-                /* swipeEdge = */ mSwipeEdge,
-                /* departingAnimationTarget = */ null);
+                /* swipeEdge = */ mSwipeEdge);
     }
 
     /** Sets the thresholds for computing progress. */
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
index cf58217..5e8ce5e 100644
--- a/core/java/android/window/DesktopExperienceFlags.java
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -47,17 +47,23 @@
             com.android.server.display.feature.flags.Flags::baseDensityForExternalDisplays, true),
     CONNECTED_DISPLAYS_CURSOR(com.android.input.flags.Flags::connectedDisplaysCursor, true),
     DISPLAY_TOPOLOGY(com.android.server.display.feature.flags.Flags::displayTopology, true),
-    ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY(Flags::enableBugFixesForSecondaryDisplay, false),
+    ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY(Flags::enableBugFixesForSecondaryDisplay, true),
     ENABLE_CONNECTED_DISPLAYS_DND(Flags::enableConnectedDisplaysDnd, false),
     ENABLE_CONNECTED_DISPLAYS_PIP(Flags::enableConnectedDisplaysPip, false),
-    ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG(Flags::enableConnectedDisplaysWindowDrag, false),
+    ENABLE_CONNECTED_DISPLAYS_WALLPAPER(
+            android.app.Flags::enableConnectedDisplaysWallpaper, false),
+    ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG(Flags::enableConnectedDisplaysWindowDrag, true),
     ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(
             com.android.server.display.feature.flags.Flags::enableDisplayContentModeManagement,
             true),
-    ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS(Flags::enableDisplayFocusInShellTransitions, false),
-    ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING(Flags::enableDisplayWindowingModeSwitching, false),
+    ENABLE_DISPLAY_DISCONNECT_INTERACTION(Flags::enableDisplayDisconnectInteraction, false),
+    ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS(Flags::enableDisplayFocusInShellTransitions, true),
+    ENABLE_DISPLAY_RECONNECT_INTERACTION(Flags::enableDisplayReconnectInteraction, false),
+    ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING(Flags::enableDisplayWindowingModeSwitching, true),
     ENABLE_DRAG_TO_MAXIMIZE(Flags::enableDragToMaximize, true),
-    ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT(Flags::enableMoveToNextDisplayShortcut, false),
+    ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX(Flags::enableDynamicRadiusComputationBugfix, false),
+    ENABLE_KEYBOARD_SHORTCUTS_TO_SWITCH_DESKS(Flags::keyboardShortcutsToSwitchDesks, false),
+    ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT(Flags::enableMoveToNextDisplayShortcut, true),
     ENABLE_MULTIPLE_DESKTOPS_BACKEND(Flags::enableMultipleDesktopsBackend, false),
     ENABLE_MULTIPLE_DESKTOPS_FRONTEND(Flags::enableMultipleDesktopsFrontend, false),
     ENABLE_PERSISTING_DISPLAY_SIZE_FOR_CONNECTED_DISPLAYS(
@@ -66,9 +72,11 @@
             false),
     ENABLE_PER_DISPLAY_PACKAGE_CONTEXT_CACHE_IN_STATUSBAR_NOTIF(
             Flags::enablePerDisplayPackageContextCacheInStatusbarNotif, false),
-    ENABLE_TASKBAR_CONNECTED_DISPLAYS(Flags::enableTaskbarConnectedDisplays, false),
+    ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE(Flags::enableProjectedDisplayDesktopMode, false),
+    ENABLE_TASKBAR_CONNECTED_DISPLAYS(Flags::enableTaskbarConnectedDisplays, true),
     ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAYS(Flags::enterDesktopByDefaultOnFreeformDisplays,
-            false),
+            true),
+    FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH(Flags::formFactorBasedDesktopFirstSwitch, false),
     REPARENT_WINDOW_TOKEN_API(Flags::reparentWindowTokenApi, true)
     // go/keep-sorted end
     ;
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 888d7e7..5b3044e 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -94,6 +94,7 @@
     ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES(
             Flags::enableDesktopWindowingMultiInstanceFeatures, true),
     ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, true),
+    ENABLE_DESKTOP_WINDOWING_PIP(Flags::enableDesktopWindowingPip, false),
     ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true),
     ENABLE_DESKTOP_WINDOWING_SCVH_CACHE(Flags::enableDesktopWindowingScvhCacheBugFix, true),
     ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
@@ -112,7 +113,7 @@
     ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true),
     ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS(Flags::enableModalsFullscreenWithPermission, false),
     ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS(
-            Flags::enableOpaqueBackgroundForTransparentWindows, false),
+            Flags::enableOpaqueBackgroundForTransparentWindows, true),
     ENABLE_QUICKSWITCH_DESKTOP_SPLIT_BUGFIX(Flags::enableQuickswitchDesktopSplitBugfix, true),
     ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
     ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE(
@@ -137,10 +138,16 @@
     ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
             Flags::enableWindowingTransitionHandlersObservers, false),
     EXCLUDE_CAPTION_FROM_APP_BOUNDS(Flags::excludeCaptionFromAppBounds, false),
+    FORCE_CLOSE_TOP_TRANSPARENT_FULLSCREEN_TASK(
+            Flags::forceCloseTopTransparentFullscreenTask, false),
     IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES(
             Flags::ignoreAspectRatioRestrictionsForResizeableFreeformActivities, true),
     INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
-            Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true)
+            Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true),
+    INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES(
+            Flags::inheritTaskBoundsForTrampolineTaskLaunches, false),
+    SKIP_DECOR_VIEW_RELAYOUT_WHEN_CLOSING_BUGFIX(
+            Flags::skipDecorViewRelayoutWhenClosingBugfix, false),
     // go/keep-sorted end
     ;
 
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 6f4dd4e..0b84070 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -66,17 +66,6 @@
     void startTransition(IBinder transitionToken, in @nullable WindowContainerTransaction t);
 
     /**
-     * Starts a legacy transition.
-     * @param type The transition type.
-     * @param adapter The animation to use.
-     * @param syncCallback A sync callback for the contents of `t`
-     * @param t Operations that are part of the transition.
-     * @return sync-id or -1 if this no-op'd because a transition is already running.
-     */
-    int startLegacyTransition(int type, in RemoteAnimationAdapter adapter,
-            in IWindowContainerTransactionCallback syncCallback, in WindowContainerTransaction t);
-
-    /**
      * Finishes a transition. This must be called for all created transitions.
      * @param transitionToken Which transition to finish
      * @param t Changes to make before finishing but in the same SF Transaction. Can be null.
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index d478108..69613a7 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -270,8 +270,7 @@
                 }
                 mIOnBackInvokedCallback.onBackStarted(
                         new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
-                                backEvent.getProgress(), false, backEvent.getSwipeEdge(),
-                                null));
+                                backEvent.getProgress(), false, backEvent.getSwipeEdge()));
             } catch (RemoteException e) {
                 Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
             }
@@ -286,8 +285,7 @@
                 }
                 mIOnBackInvokedCallback.onBackProgressed(
                         new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
-                                backEvent.getProgress(), false, backEvent.getSwipeEdge(),
-                                null));
+                                backEvent.getProgress(), false, backEvent.getSwipeEdge()));
             } catch (RemoteException e) {
                 Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
             }
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 1156503..8f7f941 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -16,6 +16,7 @@
 
 package android.window;
 
+import static android.app.Instrumentation.DEBUG_START_ACTIVITY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -32,6 +33,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
+import android.app.Instrumentation;
 import android.app.PendingIntent;
 import android.app.WindowConfiguration;
 import android.app.WindowConfiguration.WindowingMode;
@@ -45,6 +47,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.view.InsetsFrameProvider;
 import android.view.InsetsSource;
 import android.view.SurfaceControl;
@@ -445,6 +448,27 @@
         return this;
     }
 
+    /**
+     * Sets a given safe region {@code Rect} on the {@code container}. Set {@code null} to reset
+     * safe region bounds. When a safe region is set on a WindowContainer, the activities which
+     * need to be within a safe region will be letterboxed within the set safe region bounds.
+     * <p>Note that if the position of the WindowContainer changes, the caller needs to update the
+     * safe region bounds.
+     *
+     * @param container        The window container that the safe region bounds are set on
+     * @param safeRegionBounds The rect for the safe region bounds which are absolute in nature.
+     * @hide
+     */
+    @NonNull
+    @FlaggedApi(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public WindowContainerTransaction setSafeRegionBounds(
+            @NonNull WindowContainerToken container,
+            @Nullable Rect safeRegionBounds) {
+        mHierarchyOps.add(
+                HierarchyOp.createForSetSafeRegionBounds(container.asBinder(), safeRegionBounds));
+        return this;
+    }
+
     /*
      * ===========================================================================================
      * Hierarchy updates (create/destroy/reorder/reparent containers)
@@ -621,6 +645,10 @@
      */
     @NonNull
     public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(Instrumentation.TAG, "WCT.startTask: taskId=" + taskId
+                    + " options=" + options, new Throwable());
+        }
         mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
         return this;
     }
@@ -634,11 +662,15 @@
      */
     @NonNull
     public WindowContainerTransaction sendPendingIntent(@Nullable PendingIntent sender,
-            @Nullable Intent intent, @Nullable Bundle options) {
+            @Nullable Intent fillInIntent, @Nullable Bundle options) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(Instrumentation.TAG, "WCT.sendPendingIntent: sender=" + sender.getIntent()
+                    + " fillInIntent=" + fillInIntent + " options=" + options, new Throwable());
+        }
         mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
                 .setLaunchOptions(options)
                 .setPendingIntent(sender)
-                .setActivityIntent(intent)
+                .setActivityIntent(fillInIntent)
                 .build());
         return this;
     }
@@ -653,6 +685,10 @@
     @NonNull
     public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
             @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(Instrumentation.TAG, "WCT.startShortcut: shortcutInfo=" + shortcutInfo
+                    + " options=" + options, new Throwable());
+        }
         mHierarchyOps.add(HierarchyOp.createForStartShortcut(
                 callingPackage, shortcutInfo, options));
         return this;
@@ -684,16 +720,13 @@
      * organizer.
      * @param root1 the first root.
      * @param root2 the second root.
+     * @deprecated replace with {@link #setAdjacentRootSet}
      */
+    @SuppressWarnings("UnflaggedApi") // @TestApi without associated feature.
+    @Deprecated
     @NonNull
     public WindowContainerTransaction setAdjacentRoots(
             @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
-                    root1.asBinder(),
-                    root2.asBinder()));
-            return this;
-        }
         return setAdjacentRootSet(root1, root2);
     }
 
@@ -710,14 +743,10 @@
      *
      * @param roots the Tasks that should be adjacent to each other.
      * @throws IllegalArgumentException if roots have size < 2.
-     * @hide // TODO(b/373709676) Rename to setAdjacentRoots and update CTS.
+     * @hide // TODO(b/373709676) Rename to setAdjacentRoots and update CTS in 25Q4.
      */
     @NonNull
     public WindowContainerTransaction setAdjacentRootSet(@NonNull WindowContainerToken... roots) {
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            throw new IllegalArgumentException("allowMultipleAdjacentTaskFragments is not enabled."
-                    + " Use #setAdjacentRoots instead.");
-        }
         if (roots.length < 2) {
             throw new IllegalArgumentException("setAdjacentRootSet must have size >= 2");
         }
@@ -1013,7 +1042,7 @@
     /**
      * Sets to TaskFragments adjacent to each other. Containers below two visible adjacent
      * TaskFragments will be made invisible. This is similar to
-     * {@link #setAdjacentRoots(WindowContainerToken, WindowContainerToken)}, but can be used with
+     * {@link #setAdjacentRootSet(WindowContainerToken...)}, but can be used with
      * fragmentTokens when that TaskFragments haven't been created (but will be created in the same
      * {@link WindowContainerTransaction}).
      * @param fragmentToken1    client assigned unique token to create TaskFragment with specified
@@ -1604,6 +1633,7 @@
         public static final int HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT = 23;
         public static final int HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK = 24;
         public static final int HIERARCHY_OP_TYPE_APP_COMPAT_REACHABILITY = 25;
+        public static final int HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS = 26;
 
         @IntDef(prefix = {"HIERARCHY_OP_TYPE_"}, value = {
                 HIERARCHY_OP_TYPE_REPARENT,
@@ -1632,6 +1662,7 @@
                 HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT,
                 HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK,
                 HIERARCHY_OP_TYPE_APP_COMPAT_REACHABILITY,
+                HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS,
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface HierarchyOpType {
@@ -1717,6 +1748,9 @@
 
         private boolean mLaunchAdjacentDisabled;
 
+        @Nullable
+        private Rect mSafeRegionBounds;
+
         /** Creates a hierarchy operation for reparenting a container within the hierarchy. */
         @NonNull
         public static HierarchyOp createForReparent(
@@ -1880,6 +1914,17 @@
                     .build();
         }
 
+        /** Creates a hierarchy op for setting the safe region bounds. */
+        @NonNull
+        @FlaggedApi(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+        public static HierarchyOp createForSetSafeRegionBounds(@NonNull IBinder container,
+                @Nullable Rect safeRegionBounds) {
+            return new Builder(HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS)
+                    .setContainer(container)
+                    .setSafeRegionBounds(safeRegionBounds)
+                    .build();
+        }
+
         /** Only creates through {@link Builder}. */
         private HierarchyOp(@HierarchyOpType int type) {
             mType = type;
@@ -1910,6 +1955,7 @@
             mIsTrimmableFromRecents = copy.mIsTrimmableFromRecents;
             mExcludeInsetsTypes = copy.mExcludeInsetsTypes;
             mLaunchAdjacentDisabled = copy.mLaunchAdjacentDisabled;
+            mSafeRegionBounds = copy.mSafeRegionBounds;
         }
 
         private HierarchyOp(@NonNull Parcel in) {
@@ -1937,6 +1983,7 @@
             mIsTrimmableFromRecents = in.readBoolean();
             mExcludeInsetsTypes = in.readInt();
             mLaunchAdjacentDisabled = in.readBoolean();
+            mSafeRegionBounds = in.readTypedObject(Rect.CREATOR);
         }
 
         @HierarchyOpType
@@ -1973,13 +2020,6 @@
             return mContainers;
         }
 
-        /** @deprecated b/373709676 replace with {@link #getContainers()}. */
-        @Deprecated
-        @NonNull
-        public IBinder getAdjacentRoot() {
-            return mReparent;
-        }
-
         public boolean getToTop() {
             return mToTop;
         }
@@ -2065,6 +2105,12 @@
             return mLaunchAdjacentDisabled;
         }
 
+        /** Denotes the safe region bounds */
+        @Nullable
+        public Rect getSafeRegionBounds() {
+            return mSafeRegionBounds;
+        }
+
         /** Gets a string representation of a hierarchy-op type. */
         public static String hopToString(@HierarchyOpType int type) {
             switch (type) {
@@ -2098,6 +2144,7 @@
                 case HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION: return "restoreBackNav";
                 case HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES: return "setExcludeInsetsTypes";
                 case HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE: return "setKeyguardState";
+                case HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS: return "setSafeRegionBounds";
                 default: return "HOP(" + type + ")";
             }
         }
@@ -2127,17 +2174,12 @@
                     sb.append(mContainer).append(" to ").append(mToTop ? "top" : "bottom");
                     break;
                 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
-                    if (Flags.allowMultipleAdjacentTaskFragments()) {
-                        for (IBinder container : mContainers) {
-                            if (container == mContainers[0]) {
-                                sb.append("adjacentRoots=").append(container);
-                            } else {
-                                sb.append(", ").append(container);
-                            }
+                    for (IBinder container : mContainers) {
+                        if (container == mContainers[0]) {
+                            sb.append("adjacentRoots=").append(container);
+                        } else {
+                            sb.append(", ").append(container);
                         }
-                    } else {
-                        sb.append("container=").append(mContainer)
-                                .append(" adjacentRoot=").append(mReparent);
                     }
                     break;
                 case HIERARCHY_OP_TYPE_LAUNCH_TASK:
@@ -2203,6 +2245,11 @@
                     sb.append("container= ").append(mContainer)
                             .append(" isTrimmable= ")
                             .append(mIsTrimmableFromRecents);
+                    break;
+                case HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS:
+                    sb.append("container= ").append(mContainer)
+                            .append(" safeRegionBounds= ")
+                            .append(mSafeRegionBounds);
                 default:
                     sb.append("container=").append(mContainer)
                             .append(" reparent=").append(mReparent)
@@ -2239,6 +2286,7 @@
             dest.writeBoolean(mIsTrimmableFromRecents);
             dest.writeInt(mExcludeInsetsTypes);
             dest.writeBoolean(mLaunchAdjacentDisabled);
+            dest.writeTypedObject(mSafeRegionBounds, flags);
         }
 
         @Override
@@ -2324,6 +2372,9 @@
 
             private boolean mLaunchAdjacentDisabled;
 
+            @Nullable
+            private Rect mSafeRegionBounds;
+
             Builder(@HierarchyOpType int type) {
                 mType = type;
             }
@@ -2445,6 +2496,11 @@
                 return this;
             }
 
+            Builder setSafeRegionBounds(Rect safeRegionBounds) {
+                mSafeRegionBounds = safeRegionBounds;
+                return this;
+            }
+
             @NonNull
             HierarchyOp build() {
                 final HierarchyOp hierarchyOp = new HierarchyOp(mType);
@@ -2475,7 +2531,7 @@
                 hierarchyOp.mIsTrimmableFromRecents = mIsTrimmableFromRecents;
                 hierarchyOp.mExcludeInsetsTypes = mExcludeInsetsTypes;
                 hierarchyOp.mLaunchAdjacentDisabled = mLaunchAdjacentDisabled;
-
+                hierarchyOp.mSafeRegionBounds = mSafeRegionBounds;
                 return hierarchyOp;
             }
         }
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index 5c5da49..6e56b63 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -130,27 +130,6 @@
     }
 
     /**
-     * Start a legacy transition.
-     * @param type The type of the transition. This is ignored if a transitionToken is provided.
-     * @param adapter An existing transition to start. If null, a new transition is created.
-     * @param t The set of window operations that are part of this transition.
-     * @return true on success, false if a transition was already running.
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
-    @NonNull
-    public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
-            @NonNull WindowContainerTransactionCallback syncCallback,
-            @NonNull WindowContainerTransaction t) {
-        try {
-            return getWindowOrganizerController().startLegacyTransition(
-                    type, adapter, syncCallback.mInterface, t);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Register an ITransitionPlayer to handle transition animations.
      * @hide
      */
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index a42759e..cc07616 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -155,4 +155,12 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
+}
+
+flag {
+  name: "backup_and_restore_for_user_aspect_ratio_settings"
+  namespace: "large_screen_experiences_app_compat"
+  description: "Whether B&R for user aspect ratio settings is enabled"
+  bug: "396650383"
+  is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 1f710c1..e4142a1 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -27,6 +27,17 @@
 }
 
 flag {
+    name: "inherit_task_bounds_for_trampoline_task_launches"
+    namespace: "lse_desktop_experience"
+    description: "Forces trampoline task launches to inherit the bounds of the previous instance /n"
+                 "before is closes to prevent each task from cascading."
+    bug: "392815318"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+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"
@@ -50,6 +61,17 @@
 }
 
 flag {
+    name: "force_close_top_transparent_fullscreen_task"
+    namespace: "lse_desktop_experience"
+    description: "If a top transparent fullscreen task is on top of desktop mode, force it to /n"
+                 "close if another task is opened or brought to front."
+    bug: "395041610"
+    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"
@@ -573,6 +595,13 @@
 }
 
 flag {
+    name: "nested_tasks_with_independent_bounds"
+    namespace: "lse_desktop_experience"
+    description: "Allows tasks under a root task to be have independent (non-inherited) bounds"
+    bug: "402825303"
+}
+
+flag {
     name: "enable_multiple_desktops_backend"
     namespace: "lse_desktop_experience"
     description: "Enable multiple desktop sessions for desktop windowing (backend)."
@@ -580,6 +609,13 @@
 }
 
 flag {
+    name: "keyboard_shortcuts_to_switch_desks"
+    namespace: "lse_desktop_experience"
+    description: "Enable switching the active desk with keyboard shortcuts"
+    bug: "389957556"
+}
+
+flag {
     name: "enable_connected_displays_dnd"
     namespace: "lse_desktop_experience"
     description: "Enable drag-and-drop between connected displays."
@@ -904,6 +940,16 @@
 }
 
 flag {
+    name: "skip_decor_view_relayout_when_closing_bugfix"
+    namespace: "lse_desktop_experience"
+    description: "Enables bugfix to skip DecorView relayout when the corresponding window is closing."
+    bug: "394502142"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "enable_size_compat_mode_improvements_for_connected_displays"
     namespace: "lse_desktop_experience"
     description: "Enable some improvements in size compat mode for connected displays."
@@ -916,3 +962,27 @@
     description: "Enables the desktop-first mode switching logic based on its form factor."
     bug: "394736817"
 }
+
+flag {
+    name: "enable_restart_menu_for_connected_displays"
+    namespace: "lse_desktop_experience"
+    description: "Enable restart menu UI, which is shown when an app moves between displays."
+    bug: "397804287"
+}
+
+flag {
+    name: "enable_dynamic_radius_computation_bugfix"
+    namespace: "lse_desktop_experience"
+    description: "Enables bugfix to compute the corner/shadow radius of desktop windows dynamically with the current window context."
+    bug: "399630464"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "show_home_behind_desktop"
+    namespace: "lse_desktop_experience"
+    description: "Enables the home to be shown behind the desktop."
+    bug: "375644149"
+}
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index d866b0e..756288b 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -118,3 +118,15 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "update_host_input_transfer_token"
+    namespace: "window_surfaces"
+    description: "Update host InpuTransferToken on view attach"
+    is_fixed_read_only: true
+    is_exported: true
+    bug: "392965431"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ 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 b4fec41..8dd0457 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: "wait_for_transition_on_display_switch"
-    namespace: "windowing_frontend"
-    description: "Waits for Shell transition to start before unblocking the screen after display switch"
-    bug: "301420598"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "apply_lifecycle_on_pip_change"
     namespace: "windowing_frontend"
     description: "Make pip activity lifecyle change with windowing mode"
@@ -29,17 +19,6 @@
 }
 
 flag {
-  name: "respect_animation_clip"
-  namespace: "windowing_frontend"
-  description: "Fix missing clip transformation of animation"
-  bug: "376601866"
-  is_fixed_read_only: true
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "cache_window_style"
   namespace: "windowing_frontend"
   description: "Cache common window styles"
@@ -86,6 +65,14 @@
 }
 
 flag {
+  name: "action_mode_edge_to_edge"
+  namespace: "windowing_frontend"
+  description: "Make contextual action bar edge-to-edge"
+  bug: "379783298"
+  is_fixed_read_only: true
+}
+
+flag {
   name: "keyguard_going_away_timeout"
   namespace: "windowing_frontend"
   description: "Allow a maximum of 10 seconds with keyguardGoingAway=true before force-resetting"
@@ -289,6 +276,28 @@
 }
 
 flag {
+  name: "reduce_unnecessary_measure"
+  namespace: "windowing_frontend"
+  description: "Skip measuring view hierarchy if the size is known"
+  bug: "260382739"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "use_visible_requested_for_process_tracker"
+  namespace: "windowing_frontend"
+  description: "Do not count closing activity as visible process"
+  bug: "396653764"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "ensure_wallpaper_in_transitions"
   namespace: "windowing_frontend"
   description: "Ensure that wallpaper window tokens are always present/available for collection in transitions"
@@ -501,6 +510,17 @@
     description: "Sets Launch powermode for activity launches earlier"
     bug: "399380676"
     is_fixed_read_only: true
+        metadata {
+            purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "scramble_snapshot_file_name"
+    namespace: "windowing_frontend"
+    description: "Scramble the file name of task snapshot."
+    bug: "293139053"
+    is_fixed_read_only: true
     metadata {
         purpose: PURPOSE_BUGFIX
     }
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index cbeaeda..94e87c7 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -105,17 +105,6 @@
 
 flag {
     namespace: "windowing_sdk"
-    name: "allow_multiple_adjacent_task_fragments"
-    description: "Refactor to allow more than 2 adjacent TaskFragments"
-    bug: "373709676"
-    is_fixed_read_only: true
-    metadata {
-        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"
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 1281a78..24e8020 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -136,7 +136,8 @@
 
     private final Context mContext;
     private final Handler mHandler;
-    private final UserSetupCompleteObserver  mUserSetupCompleteObserver;
+    @VisibleForTesting
+    public final UserSetupCompleteObserver  mUserSetupCompleteObserver;
 
     private AlertDialog mAlertDialog;
     private boolean mIsShortcutEnabled;
@@ -471,7 +472,7 @@
         AccessibilityManager accessibilityManager =
                 mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext);
         return accessibilityManager.getEnabledAccessibilityServiceList(
-                FEEDBACK_ALL_MASK).contains(serviceInfo);
+                FEEDBACK_ALL_MASK, mUserId).contains(serviceInfo);
     }
 
     private boolean hasFeatureLeanback() {
@@ -676,7 +677,8 @@
         }
     }
 
-    private class UserSetupCompleteObserver extends ContentObserver {
+    @VisibleForTesting
+    public class UserSetupCompleteObserver extends ContentObserver {
 
         private boolean mIsRegistered = false;
         private int mUserId;
@@ -749,7 +751,8 @@
                     R.string.config_defaultAccessibilityService);
             final List<AccessibilityServiceInfo> enabledServices =
                     mFrameworkObjectProvider.getAccessibilityManagerInstance(
-                            mContext).getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
+                            mContext).getEnabledAccessibilityServiceList(
+                                    FEEDBACK_ALL_MASK, mUserId);
             for (int i = enabledServices.size() - 1; i >= 0; i--) {
                 if (TextUtils.equals(defaultShortcutTarget, enabledServices.get(i).getId())) {
                     return;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index c009fc3..9bc6671 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -23,6 +23,7 @@
 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
 import static android.content.ContentProvider.getUriWithoutUserId;
 import static android.content.ContentProvider.getUserIdFromUri;
+import static android.service.chooser.Flags.notifySingleItemChangeOnIconLoad;
 import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
 import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
 
@@ -163,9 +164,11 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
@@ -3212,6 +3215,8 @@
 
         private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20;
 
+        private final Set<ViewHolderBase> mBoundViewHolders = new HashSet<>();
+
         ChooserGridAdapter(ChooserListAdapter wrappedAdapter) {
             super();
             mChooserListAdapter = wrappedAdapter;
@@ -3232,6 +3237,31 @@
                     notifyDataSetChanged();
                 }
             });
+            if (notifySingleItemChangeOnIconLoad()) {
+                wrappedAdapter.setOnIconLoadedListener(this::onTargetIconLoaded);
+            }
+        }
+
+        private void onTargetIconLoaded(DisplayResolveInfo info) {
+            for (ViewHolderBase holder : mBoundViewHolders) {
+                switch (holder.getViewType()) {
+                    case VIEW_TYPE_NORMAL:
+                        TargetInfo itemInfo =
+                                mChooserListAdapter.getItem(
+                                        ((ItemViewHolder) holder).mListPosition);
+                        if (info == itemInfo) {
+                            notifyItemChanged(holder.getAdapterPosition());
+                        }
+                        break;
+                    case VIEW_TYPE_CALLER_AND_RANK:
+                        ItemGroupViewHolder groupHolder = (ItemGroupViewHolder) holder;
+                        if (suggestedAppsGroupContainsTarget(groupHolder, info)) {
+                            notifyItemChanged(holder.getAdapterPosition());
+                        }
+                        break;
+                }
+
+            }
         }
 
         public void setFooterHeight(int height) {
@@ -3382,6 +3412,9 @@
 
         @Override
         public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+            if (notifySingleItemChangeOnIconLoad()) {
+                mBoundViewHolders.add((ViewHolderBase) holder);
+            }
             int viewType = ((ViewHolderBase) holder).getViewType();
             switch (viewType) {
                 case VIEW_TYPE_DIRECT_SHARE:
@@ -3396,6 +3429,22 @@
         }
 
         @Override
+        public void onViewRecycled(RecyclerView.ViewHolder holder) {
+            if (notifySingleItemChangeOnIconLoad()) {
+                mBoundViewHolders.remove((ViewHolderBase) holder);
+            }
+            super.onViewRecycled(holder);
+        }
+
+        @Override
+        public boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) {
+            if (notifySingleItemChangeOnIconLoad()) {
+                mBoundViewHolders.remove((ViewHolderBase) holder);
+            }
+            return super.onFailedToRecycleView(holder);
+        }
+
+        @Override
         public int getItemViewType(int position) {
             int count;
 
@@ -3604,6 +3653,33 @@
             }
         }
 
+        /**
+         * Checks whether the suggested apps group, {@code holder}, contains the target,
+         * {@code info}.
+         */
+        private boolean suggestedAppsGroupContainsTarget(
+                ItemGroupViewHolder holder, DisplayResolveInfo info) {
+
+            int position = holder.getAdapterPosition();
+            int start = getListPosition(position);
+            int startType = getRowType(start);
+
+            int columnCount = holder.getColumnCount();
+            int end = start + columnCount - 1;
+            while (getRowType(end) != startType && end >= start) {
+                end--;
+            }
+
+            for (int i = 0; i < columnCount; i++) {
+                if (start + i <= end) {
+                    if (mChooserListAdapter.getItem(holder.getItemIndex(i)) == info) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
         int getListPosition(int position) {
             position -= getSystemRowCount() + getProfileRowCount();
 
diff --git a/core/java/com/android/internal/app/ChooserGridLayoutManager.java b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
index 69d2abc..6eb5e74 100644
--- a/core/java/com/android/internal/app/ChooserGridLayoutManager.java
+++ b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
@@ -25,7 +25,7 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
-import android.widget.GridView;
+import android.widget.ListView;
 import android.widget.TextView;
 
 import com.android.internal.R;
@@ -131,7 +131,7 @@
         super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info);
         if (announceShortcutsAndSuggestedAppsLegacy() && host instanceof ViewGroup) {
             if (host.getId() == R.id.shortcuts_container) {
-                info.setClassName(GridView.class.getName());
+                info.setClassName(ListView.class.getName());
                 info.setContainerTitle(mShortcutGroupTitle);
                 info.setCollectionInfo(createShortcutsA11yCollectionInfo((ViewGroup) host));
             } else if (host.getId() == R.id.chooser_row) {
@@ -140,7 +140,7 @@
                 ChooserListAdapter gridAdapter = adapter instanceof ChooserGridAdapter
                         ? ((ChooserGridAdapter) adapter).getListAdapter()
                         : null;
-                info.setClassName(GridView.class.getName());
+                info.setClassName(ListView.class.getName());
                 info.setCollectionInfo(createSuggestedAppsA11yCollectionInfo((ViewGroup) host));
                 if (gridAdapter == null || gridAdapter.getAlphaTargetCount() > 0) {
                     info.setContainerTitle(mSuggestedAppsGroupTitle);
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index d38689c..1b8c36d 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -16,9 +16,12 @@
 
 package com.android.internal.app;
 
+import static android.service.chooser.Flags.notifySingleItemChangeOnIconLoad;
+
 import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
 import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
 
+import android.annotation.Nullable;
 import android.app.prediction.AppPredictor;
 import android.content.ComponentName;
 import android.content.Context;
@@ -56,6 +59,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Consumer;
 
 public class ChooserListAdapter extends ResolverListAdapter {
     private static final String TAG = "ChooserListAdapter";
@@ -108,6 +112,9 @@
     // Represents the UserSpace in which the Initial Intents should be resolved.
     private final UserHandle mInitialIntentsUserSpace;
 
+    @Nullable
+    private Consumer<DisplayResolveInfo> mOnIconLoadedListener;
+
     // For pinned direct share labels, if the text spans multiple lines, the TextView will consume
     // the full width, even if the characters actually take up less than that. Measure the actual
     // line widths and constrain the View's width based upon that so that the pin doesn't end up
@@ -218,6 +225,10 @@
                 true);
     }
 
+    public void setOnIconLoadedListener(Consumer<DisplayResolveInfo> onIconLoadedListener) {
+        mOnIconLoadedListener = onIconLoadedListener;
+    }
+
     AppPredictor getAppPredictor() {
         return mAppPredictor;
     }
@@ -329,6 +340,15 @@
         }
     }
 
+    @Override
+    protected void onIconLoaded(DisplayResolveInfo info) {
+        if (notifySingleItemChangeOnIconLoad() && mOnIconLoadedListener != null) {
+            mOnIconLoadedListener.accept(info);
+        } else {
+            notifyDataSetChanged();
+        }
+    }
+
     private void loadDirectShareIcon(SelectableTargetInfo info) {
         LoadDirectShareIconTask task = (LoadDirectShareIconTask) mIconLoaders.get(info);
         if (task == null) {
diff --git a/core/java/com/android/internal/app/MediaRouteChooserContentManager.java b/core/java/com/android/internal/app/MediaRouteChooserContentManager.java
new file mode 100644
index 0000000..09c6f5e
--- /dev/null
+++ b/core/java/com/android/internal/app/MediaRouteChooserContentManager.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.app;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+
+import com.android.internal.R;
+
+public class MediaRouteChooserContentManager {
+    Context mContext;
+
+    private final boolean mShowProgressBarWhenEmpty;
+
+    public MediaRouteChooserContentManager(Context context, boolean showProgressBarWhenEmpty) {
+        mContext = context;
+        mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
+    }
+
+    /**
+     * Starts binding all the views (list view, empty view, etc.) using the
+     * given container view.
+     */
+    public void bindViews(View containerView) {
+        View emptyView = containerView.findViewById(android.R.id.empty);
+        ListView listView = containerView.findViewById(R.id.media_route_list);
+        listView.setEmptyView(emptyView);
+
+        if (!mShowProgressBarWhenEmpty) {
+            containerView.findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
+
+            // Center the empty view when the progress bar is not shown.
+            LinearLayout.LayoutParams params =
+                    (LinearLayout.LayoutParams) emptyView.getLayoutParams();
+            params.gravity = Gravity.CENTER;
+            emptyView.setLayoutParams(params);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialog.java b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
index 23d966fd..5030a14 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialog.java
@@ -23,14 +23,12 @@
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.TypedValue;
-import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
-import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
 
@@ -52,15 +50,15 @@
 public class MediaRouteChooserDialog extends AlertDialog {
     private final MediaRouter mRouter;
     private final MediaRouterCallback mCallback;
-    private final boolean mShowProgressBarWhenEmpty;
 
     private int mRouteTypes;
     private View.OnClickListener mExtendedSettingsClickListener;
     private RouteAdapter mAdapter;
-    private ListView mListView;
     private Button mExtendedSettingsButton;
     private boolean mAttachedToWindow;
 
+    private final MediaRouteChooserContentManager mContentManager;
+
     public MediaRouteChooserDialog(Context context, int theme) {
         this(context, theme, true);
     }
@@ -70,7 +68,7 @@
 
         mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
         mCallback = new MediaRouterCallback();
-        mShowProgressBarWhenEmpty = showProgressBarWhenEmpty;
+        mContentManager = new MediaRouteChooserContentManager(context, showProgressBarWhenEmpty);
     }
 
     /**
@@ -128,8 +126,9 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         // Note: setView must be called before super.onCreate().
-        setView(LayoutInflater.from(getContext()).inflate(R.layout.media_route_chooser_dialog,
-                null));
+        View containerView = LayoutInflater.from(getContext()).inflate(
+                R.layout.media_route_chooser_dialog, null);
+        setView(containerView);
 
         setTitle(mRouteTypes == MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY
                 ? R.string.media_route_chooser_title_for_remote_display
@@ -140,25 +139,15 @@
 
         super.onCreate(savedInstanceState);
 
-        View emptyView = findViewById(android.R.id.empty);
         mAdapter = new RouteAdapter(getContext());
-        mListView = (ListView) findViewById(R.id.media_route_list);
-        mListView.setAdapter(mAdapter);
-        mListView.setOnItemClickListener(mAdapter);
-        mListView.setEmptyView(emptyView);
+        ListView listView = findViewById(R.id.media_route_list);
+        listView.setAdapter(mAdapter);
+        listView.setOnItemClickListener(mAdapter);
 
-        mExtendedSettingsButton = (Button) findViewById(R.id.media_route_extended_settings_button);
+        mExtendedSettingsButton = findViewById(R.id.media_route_extended_settings_button);
         updateExtendedSettingsButton();
 
-        if (!mShowProgressBarWhenEmpty) {
-            findViewById(R.id.media_route_progress_bar).setVisibility(View.GONE);
-
-            // Center the empty view when the progress bar is not shown.
-            LinearLayout.LayoutParams params =
-                    (LinearLayout.LayoutParams) emptyView.getLayoutParams();
-            params.gravity = Gravity.CENTER;
-            emptyView.setLayoutParams(params);
-        }
+        mContentManager.bindViews(containerView);
     }
 
     private void updateExtendedSettingsButton() {
@@ -240,8 +229,8 @@
                 view = mInflater.inflate(R.layout.media_route_list_item, parent, false);
             }
             MediaRouter.RouteInfo route = getItem(position);
-            TextView text1 = (TextView)view.findViewById(android.R.id.text1);
-            TextView text2 = (TextView)view.findViewById(android.R.id.text2);
+            TextView text1 = view.findViewById(android.R.id.text1);
+            TextView text2 = view.findViewById(android.R.id.text2);
             text1.setText(route.getName());
             CharSequence description = route.getDescription();
             if (TextUtils.isEmpty(description)) {
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 54c0e61..4d9ce86 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -680,6 +680,10 @@
         }
     }
 
+    protected void onIconLoaded(DisplayResolveInfo info) {
+        notifyDataSetChanged();
+    }
+
     private void loadLabel(DisplayResolveInfo info) {
         LoadLabelTask task = mLabelLoaders.get(info);
         if (task == null) {
@@ -1004,7 +1008,7 @@
                 mResolverListCommunicator.updateProfileViewButton();
             } else if (!mDisplayResolveInfo.hasDisplayIcon()) {
                 mDisplayResolveInfo.setDisplayIcon(d);
-                notifyDataSetChanged();
+                onIconLoaded(mDisplayResolveInfo);
             }
         }
     }
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 41e2ca9..e125e25 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -313,8 +313,17 @@
      */
     public static final int CUJ_DEFAULT_TASK_TO_TASK_ANIMATION = 128;
 
+    /**
+     * Track moving a window to another display in Desktop Windowing mode.
+     *
+     * <p>Tracking starts when the DesktopModeMoveToDisplayTransitionHandler starts animating the
+     * task to move it to another display. This is triggered when the user presses a keyboard
+     * shortcut or clicks the menu in the overview. Tracking ends when the animation completes.</p>
+     */
+    public static final int CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY = 129;
+
     // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
-    @VisibleForTesting static final int LAST_CUJ = CUJ_DEFAULT_TASK_TO_TASK_ANIMATION;
+    @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY;
 
     /** @hide */
     @IntDef({
@@ -434,7 +443,8 @@
             CUJ_DESKTOP_MODE_KEYBOARD_QUICK_SWITCH_APP_LAUNCH,
             CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND,
             CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK,
-            CUJ_DEFAULT_TASK_TO_TASK_ANIMATION
+            CUJ_DEFAULT_TASK_TO_TASK_ANIMATION,
+            CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {}
@@ -565,6 +575,7 @@
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_WORK_UTILITY_VIEW_EXPAND;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_WORK_UTILITY_VIEW_SHRINK;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DEFAULT_TASK_TO_TASK_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DEFAULT_TASK_TO_TASK_ANIMATION;
+        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY;
     }
 
     private Cuj() {
@@ -817,6 +828,8 @@
                 return "LAUNCHER_WORK_UTILITY_VIEW_SHRINK";
             case CUJ_DEFAULT_TASK_TO_TASK_ANIMATION:
                 return "DEFAULT_TASK_TO_TASK_ANIMATION";
+            case CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY:
+                return "DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 81ca231..8151429 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -45,11 +45,13 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.ConcurrentModificationException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Queue;
 import java.util.concurrent.locks.ReentrantLock;
 
 /**
@@ -203,15 +205,6 @@
         BatteryHistoryFragment getLatestFragment();
 
         /**
-         * Given a fragment, returns the earliest fragment that follows it whose monotonic
-         * start time falls within the specified range. `startTimeMs` is inclusive, `endTimeMs`
-         * is exclusive.
-         */
-        @Nullable
-        BatteryHistoryFragment getNextFragment(BatteryHistoryFragment current, long startTimeMs,
-                long endTimeMs);
-
-        /**
          * Acquires a lock on the entire store.
          */
         void lock();
@@ -268,6 +261,60 @@
         void reset();
     }
 
+    class BatteryHistoryParcelContainer {
+        private boolean mParcelReadyForReading;
+        private Parcel mParcel;
+        private BatteryStatsHistory.BatteryHistoryFragment mFragment;
+        private long mMonotonicStartTime;
+
+        BatteryHistoryParcelContainer(@NonNull Parcel parcel, long monotonicStartTime) {
+            mParcel = parcel;
+            mMonotonicStartTime = monotonicStartTime;
+            mParcelReadyForReading = true;
+        }
+
+        BatteryHistoryParcelContainer(@NonNull BatteryHistoryFragment fragment) {
+            mFragment = fragment;
+            mMonotonicStartTime = fragment.monotonicTimeMs;
+            mParcelReadyForReading = false;
+        }
+
+        @Nullable
+        Parcel getParcel() {
+            if (mParcelReadyForReading) {
+                return mParcel;
+            }
+
+            Parcel parcel = Parcel.obtain();
+            if (readFragmentToParcel(parcel, mFragment)) {
+                parcel.readInt();       // skip buffer size
+                mParcel = parcel;
+            } else {
+                parcel.recycle();
+            }
+            mParcelReadyForReading = true;
+            return mParcel;
+        }
+
+        long getMonotonicStartTime() {
+            return mMonotonicStartTime;
+        }
+
+        /**
+         * Recycles the parcel (if appropriate). Should be called after the parcel has been
+         * processed by the iterator.
+         */
+        void close() {
+            if (mParcel != null && mFragment != null) {
+                mParcel.recycle();
+            }
+            // ParcelContainers are not meant to be reusable. To prevent any unintentional
+            // access to the parcel after it has been recycled, clear the references to it.
+            mParcel = null;
+            mFragment = null;
+        }
+    }
+
     private final Parcel mHistoryBuffer;
     private final HistoryStepDetailsCalculator mStepDetailsCalculator;
     private final Clock mClock;
@@ -447,6 +494,7 @@
         mWritableHistory = writableHistory;
         if (mWritableHistory != null) {
             mMutable = false;
+            mHistoryBufferStartTime = mWritableHistory.mHistoryBufferStartTime;
             mHistoryMonotonicEndTime = mWritableHistory.mHistoryMonotonicEndTime;
         }
 
@@ -624,6 +672,7 @@
      */
     public void reset() {
         synchronized (this) {
+            mMonotonicHistorySize = 0;
             initHistoryBuffer();
             if (mStore != null) {
                 mStore.reset();
@@ -689,95 +738,66 @@
     }
 
     /**
-     * When iterating history files and history buffer, always start from the lowest numbered
-     * history file, when reached the mActiveFile (highest numbered history file), do not read from
-     * mActiveFile, read from history buffer instead because the buffer has more updated data.
-     *
-     * @return The parcel that has next record. null if finished all history files and history
-     * buffer
+     * Returns all chunks of accumulated history that contain items within the range between
+     * `startTimeMs` (inclusive) and `endTimeMs` (exclusive)
      */
-    @Nullable
-    public Parcel getNextParcel(long startTimeMs, long endTimeMs) {
-        checkImmutable();
-
-        // First iterate through all records in current parcel.
-        if (mCurrentParcel != null) {
-            if (mCurrentParcel.dataPosition() < mCurrentParcelEnd) {
-                // There are more records in current parcel.
-                return mCurrentParcel;
-            } else if (mHistoryBuffer == mCurrentParcel) {
-                // finished iterate through all history files and history buffer.
-                return null;
-            } else if (mHistoryParcels == null
-                    || !mHistoryParcels.contains(mCurrentParcel)) {
-                // current parcel is from history file.
-                mCurrentParcel.recycle();
-            }
-        }
-
-        if (mStore != null) {
-            BatteryHistoryFragment next = mStore.getNextFragment(mCurrentFragment, startTimeMs,
-                    endTimeMs);
-            while (next != null) {
-                mCurrentParcel = null;
-                mCurrentParcelEnd = 0;
-                final Parcel p = Parcel.obtain();
-                if (readFragmentToParcel(p, next)) {
-                    int bufSize = p.readInt();
-                    int curPos = p.dataPosition();
-                    mCurrentParcelEnd = curPos + bufSize;
-                    mCurrentParcel = p;
-                    if (curPos < mCurrentParcelEnd) {
-                        mCurrentFragment = next;
-                        return mCurrentParcel;
-                    }
-                } else {
-                    p.recycle();
-                }
-                next = mStore.getNextFragment(next, startTimeMs, endTimeMs);
-            }
-        }
-
-        // mHistoryParcels is created when BatteryStatsImpl object is created from deserialization
-        // of a parcel, such as Settings app or checkin file.
-        if (mHistoryParcels != null) {
-            while (mParcelIndex < mHistoryParcels.size()) {
-                final Parcel p = mHistoryParcels.get(mParcelIndex++);
-                if (!verifyVersion(p)) {
-                    continue;
-                }
-                // skip monotonic time field.
-                p.readLong();
-                // skip monotonic end time field
-                p.readLong();
-                // skip monotonic size field
-                p.readLong();
-
-                final int bufSize = p.readInt();
-                final int curPos = p.dataPosition();
-                mCurrentParcelEnd = curPos + bufSize;
-                mCurrentParcel = p;
-                if (curPos < mCurrentParcelEnd) {
-                    return mCurrentParcel;
-                }
-            }
-        }
-
-        // finished iterator through history files (except the last one), now history buffer.
-        if (mHistoryBuffer.dataSize() <= 0) {
-            // buffer is empty.
-            return null;
-        }
-        mHistoryBuffer.setDataPosition(0);
-        mCurrentParcel = mHistoryBuffer;
-        mCurrentParcelEnd = mCurrentParcel.dataSize();
-        return mCurrentParcel;
-    }
-
-    private void checkImmutable() {
+    Queue<BatteryHistoryParcelContainer> getParcelContainers(long startTimeMs, long endTimeMs) {
         if (mMutable) {
             throw new IllegalStateException("Iterating over a mutable battery history");
         }
+
+        if (endTimeMs == MonotonicClock.UNDEFINED || endTimeMs == 0) {
+            endTimeMs = Long.MAX_VALUE;
+        }
+
+        Queue<BatteryHistoryParcelContainer> containers = new ArrayDeque<>();
+
+        if (mStore != null) {
+            List<BatteryHistoryFragment> fragments = mStore.getFragments();
+            for (int i = 0; i < fragments.size(); i++) {
+                BatteryHistoryFragment fragment = fragments.get(i);
+                if (fragment.monotonicTimeMs >= endTimeMs) {
+                    break;
+                }
+
+                if (fragment.monotonicTimeMs >= startTimeMs && fragment != mActiveFragment) {
+                    containers.add(new BatteryHistoryParcelContainer(fragment));
+                }
+            }
+        }
+
+        if (mHistoryParcels != null) {
+            for (int i = 0; i < mHistoryParcels.size(); i++) {
+                final Parcel p = mHistoryParcels.get(i);
+                if (!verifyVersion(p)) {
+                    continue;
+                }
+
+                long monotonicStartTime = p.readLong();
+                if (monotonicStartTime >= endTimeMs) {
+                    continue;
+                }
+
+                long monotonicEndTime = p.readLong();
+                if (monotonicEndTime < startTimeMs) {
+                    continue;
+                }
+
+                // skip monotonic size field
+                p.readLong();
+                // skip buffer size field
+                p.readInt();
+
+                containers.add(new BatteryHistoryParcelContainer(p, monotonicStartTime));
+            }
+        }
+
+        if (mHistoryBufferStartTime < endTimeMs) {
+            mHistoryBuffer.setDataPosition(0);
+            containers.add(
+                    new BatteryHistoryParcelContainer(mHistoryBuffer, mHistoryBufferStartTime));
+        }
+        return containers;
     }
 
     /**
@@ -818,21 +838,6 @@
     }
 
     /**
-     * Extracts the monotonic time, as per {@link MonotonicClock}, from the supplied battery history
-     * buffer.
-     */
-    public long getHistoryBufferStartTime(Parcel p) {
-        int pos = p.dataPosition();
-        p.setDataPosition(0);
-        p.readInt();        // Skip the version field
-        long monotonicTime = p.readLong();
-        p.readLong();       // Skip monotonic end time field
-        p.readLong();       // Skip monotonic size field
-        p.setDataPosition(pos);
-        return monotonicTime;
-    }
-
-    /**
      * Writes the battery history contents for persistence.
      */
     public void writeSummaryToParcel(Parcel out, boolean inclHistory) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index 38398b4..09f9b0bf 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -24,6 +24,7 @@
 import android.util.SparseArray;
 
 import java.util.Iterator;
+import java.util.Queue;
 
 /**
  * An iterator for {@link BatteryStats.HistoryItem}'s.
@@ -48,7 +49,10 @@
     private long mBaseMonotonicTime;
     private long mBaseTimeUtc;
     private int mItemIndex = 0;
-    private int mMaxHistoryItems;
+    private final int mMaxHistoryItems;
+    private int mParcelDataPosition;
+
+    private Queue<BatteryStatsHistory.BatteryHistoryParcelContainer> mParcelContainers;
 
     public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs,
             long endTimeMs) {
@@ -62,7 +66,11 @@
     @Override
     public boolean hasNext() {
         if (!mNextItemReady) {
-            advance();
+            if (!advance()) {
+                mHistoryItem = null;
+                close();
+            }
+            mNextItemReady = true;
         }
 
         return mHistoryItem != null;
@@ -75,35 +83,48 @@
     @Override
     public BatteryStats.HistoryItem next() {
         if (!mNextItemReady) {
-            advance();
+            if (!advance()) {
+                mHistoryItem = null;
+                close();
+            }
         }
         mNextItemReady = false;
         return mHistoryItem;
     }
 
-    private void advance() {
-        while (true) {
-            if (mItemIndex > mMaxHistoryItems) {
-                Slog.wtfStack(TAG, "Number of battery history items is too large: " + mItemIndex);
-                break;
-            }
+    private boolean advance() {
+        if (mParcelContainers == null) {
+            mParcelContainers = mBatteryStatsHistory.getParcelContainers(mStartTimeMs, mEndTimeMs);
+        }
 
-            Parcel p = mBatteryStatsHistory.getNextParcel(mStartTimeMs, mEndTimeMs);
-            if (p == null) {
-                break;
+        BatteryStatsHistory.BatteryHistoryParcelContainer container;
+        while ((container = mParcelContainers.peek()) != null) {
+            Parcel p = container.getParcel();
+            if (p == null || p.dataPosition() >= p.dataSize()) {
+                container.close();
+                mParcelContainers.remove();
+                mParcelDataPosition = 0;
+                continue;
             }
 
             if (!mTimeInitialized) {
-                mBaseMonotonicTime = mBatteryStatsHistory.getHistoryBufferStartTime(p);
+                mBaseMonotonicTime = container.getMonotonicStartTime();
                 mHistoryItem.time = mBaseMonotonicTime;
                 mTimeInitialized = true;
             }
 
             try {
                 readHistoryDelta(p, mHistoryItem);
+                int dataPosition = p.dataPosition();
+                if (dataPosition <= mParcelDataPosition) {
+                    Slog.wtf(TAG, "Corrupted battery history, parcel is not progressing: "
+                            + dataPosition + " of " + p.dataSize());
+                    return false;
+                }
+                mParcelDataPosition = dataPosition;
             } catch (Throwable t) {
                 Slog.wtf(TAG, "Corrupted battery history", t);
-                break;
+                return false;
             }
 
             if (mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
@@ -111,21 +132,24 @@
                 mBaseTimeUtc = mHistoryItem.currentTime - (mHistoryItem.time - mBaseMonotonicTime);
             }
 
+            if (mHistoryItem.time < mStartTimeMs) {
+                continue;
+            }
+
+            if (mEndTimeMs != 0 && mEndTimeMs != MonotonicClock.UNDEFINED
+                    && mHistoryItem.time >= mEndTimeMs) {
+                return false;
+            }
+
+            if (mItemIndex++ > mMaxHistoryItems) {
+                Slog.wtfStack(TAG, "Number of battery history items is too large: " + mItemIndex);
+                return false;
+            }
+
             mHistoryItem.currentTime = mBaseTimeUtc + (mHistoryItem.time - mBaseMonotonicTime);
-
-            if (mEndTimeMs != 0 && mHistoryItem.time >= mEndTimeMs) {
-                break;
-            }
-            if (mHistoryItem.time >= mStartTimeMs) {
-                mItemIndex++;
-                mNextItemReady = true;
-                return;
-            }
+            return true;
         }
-
-        mHistoryItem = null;
-        mNextItemReady = true;
-        close();
+        return false;
     }
 
     private void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index e20a52b..3d81e4f 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -120,6 +120,7 @@
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.BackgroundFallback;
 import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
+import com.android.window.flags.Flags;
 
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -1003,7 +1004,8 @@
     public void onWindowSystemUiVisibilityChanged(int visible) {
         updateColorViews(null /* insets */, true /* animate */);
 
-        if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
+        if (!Flags.actionModeEdgeToEdge()
+                && mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
             updateStatusGuardColor();
         }
     }
@@ -1040,7 +1042,7 @@
         }
         mFrameOffsets.set(insets.getSystemWindowInsetsAsRect());
         insets = updateColorViews(insets, true /* animate */);
-        insets = updateStatusGuard(insets);
+        insets = updateActionModeInsets(insets);
         if (getForeground() != null) {
             drawableChanged();
         }
@@ -1592,7 +1594,7 @@
         }
     }
 
-    private WindowInsets updateStatusGuard(WindowInsets insets) {
+    private WindowInsets updateActionModeInsets(WindowInsets insets) {
         boolean showStatusGuard = false;
         // Show the status guard when the non-overlay contextual action bar is showing
         if (mPrimaryActionModeView != null) {
@@ -1608,54 +1610,78 @@
                     final Rect rect = mTempRect;
 
                     // Apply the insets that have not been applied by the contentParent yet.
-                    WindowInsets innerInsets =
+                    final WindowInsets innerInsets =
                             mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
-                    int newTopMargin = innerInsets.getSystemWindowInsetTop();
-                    int newLeftMargin = innerInsets.getSystemWindowInsetLeft();
-                    int newRightMargin = innerInsets.getSystemWindowInsetRight();
+                    final boolean consumesSystemWindowInsetsTop;
+                    if (Flags.actionModeEdgeToEdge()) {
+                        final Insets newPadding = innerInsets.getSystemWindowInsets();
+                        final Insets newMargin = innerInsets.getInsets(
+                                WindowInsets.Type.navigationBars());
 
-                    // Must use root window insets for the guard, because the color views consume
-                    // the navigation bar inset if the window does not request LAYOUT_HIDE_NAV - but
-                    // the status guard is attached at the root.
-                    WindowInsets rootInsets = getRootWindowInsets();
-                    int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft();
-                    int newGuardRightMargin = rootInsets.getSystemWindowInsetRight();
+                        // Don't extend into navigation bar area so the width can align with status
+                        // bar color view.
+                        if (mlp.leftMargin != newMargin.left
+                                || mlp.rightMargin != newMargin.right) {
+                            mlpChanged = true;
+                            mlp.leftMargin = newMargin.left;
+                            mlp.rightMargin = newMargin.right;
+                        }
 
-                    if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin
-                            || mlp.rightMargin != newRightMargin) {
-                        mlpChanged = true;
-                        mlp.topMargin = newTopMargin;
-                        mlp.leftMargin = newLeftMargin;
-                        mlp.rightMargin = newRightMargin;
-                    }
+                        mPrimaryActionModeView.setPadding(
+                                newPadding.left - newMargin.left,
+                                newPadding.top,
+                                newPadding.right - newMargin.right,
+                                0);
+                        consumesSystemWindowInsetsTop = newPadding.top > 0;
+                    } else {
+                        int newTopMargin = innerInsets.getSystemWindowInsetTop();
+                        int newLeftMargin = innerInsets.getSystemWindowInsetLeft();
+                        int newRightMargin = innerInsets.getSystemWindowInsetRight();
 
-                    if (newTopMargin > 0 && mStatusGuard == null) {
-                        mStatusGuard = new View(mContext);
-                        mStatusGuard.setVisibility(GONE);
-                        final LayoutParams lp = new LayoutParams(MATCH_PARENT,
-                                mlp.topMargin, Gravity.LEFT | Gravity.TOP);
-                        lp.leftMargin = newGuardLeftMargin;
-                        lp.rightMargin = newGuardRightMargin;
-                        addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp);
-                    } else if (mStatusGuard != null) {
-                        final LayoutParams lp = (LayoutParams)
-                                mStatusGuard.getLayoutParams();
-                        if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin
-                                || lp.rightMargin != newGuardRightMargin) {
-                            lp.height = mlp.topMargin;
+                        // Must use root window insets for the guard, because the color views
+                        // consume the navigation bar inset if the window does not request
+                        // LAYOUT_HIDE_NAV - but the status guard is attached at the root.
+                        WindowInsets rootInsets = getRootWindowInsets();
+                        int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft();
+                        int newGuardRightMargin = rootInsets.getSystemWindowInsetRight();
+
+                        if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin
+                                || mlp.rightMargin != newRightMargin) {
+                            mlpChanged = true;
+                            mlp.topMargin = newTopMargin;
+                            mlp.leftMargin = newLeftMargin;
+                            mlp.rightMargin = newRightMargin;
+                        }
+
+                        if (newTopMargin > 0 && mStatusGuard == null) {
+                            mStatusGuard = new View(mContext);
+                            mStatusGuard.setVisibility(GONE);
+                            final LayoutParams lp = new LayoutParams(MATCH_PARENT,
+                                    mlp.topMargin, Gravity.LEFT | Gravity.TOP);
                             lp.leftMargin = newGuardLeftMargin;
                             lp.rightMargin = newGuardRightMargin;
-                            mStatusGuard.setLayoutParams(lp);
+                            addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp);
+                        } else if (mStatusGuard != null) {
+                            final LayoutParams lp = (LayoutParams)
+                                    mStatusGuard.getLayoutParams();
+                            if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin
+                                    || lp.rightMargin != newGuardRightMargin) {
+                                lp.height = mlp.topMargin;
+                                lp.leftMargin = newGuardLeftMargin;
+                                lp.rightMargin = newGuardRightMargin;
+                                mStatusGuard.setLayoutParams(lp);
+                            }
                         }
-                    }
 
-                    // The action mode's theme may differ from the app, so
-                    // always show the status guard above it if we have one.
-                    showStatusGuard = mStatusGuard != null;
+                        // The action mode's theme may differ from the app, so
+                        // always show the status guard above it if we have one.
+                        showStatusGuard = mStatusGuard != null;
 
-                    if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) {
-                        // If it wasn't previously shown, the color may be stale
-                        updateStatusGuardColor();
+                        if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) {
+                            // If it wasn't previously shown, the color may be stale
+                            updateStatusGuardColor();
+                        }
+                        consumesSystemWindowInsetsTop = showStatusGuard;
                     }
 
                     // We only need to consume the insets if the action
@@ -1664,14 +1690,16 @@
                     // screen_simple_overlay_action_mode.xml).
                     final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
                             & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
-                    if (nonOverlay && showStatusGuard) {
+                    if (nonOverlay && consumesSystemWindowInsetsTop) {
                         insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
                     }
                 } else {
-                    // reset top margin
-                    if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) {
-                        mlpChanged = true;
-                        mlp.topMargin = 0;
+                    if (!Flags.actionModeEdgeToEdge()) {
+                        // reset top margin
+                        if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) {
+                            mlpChanged = true;
+                            mlp.topMargin = 0;
+                        }
                     }
                 }
                 if (mlpChanged) {
@@ -1679,7 +1707,7 @@
                 }
             }
         }
-        if (mStatusGuard != null) {
+        if (!Flags.actionModeEdgeToEdge() && mStatusGuard != null) {
             mStatusGuard.setVisibility(showStatusGuard ? VISIBLE : GONE);
         }
         return insets;
@@ -2183,7 +2211,7 @@
         for (int i = getChildCount() - 1; i >= 0; i--) {
             View v = getChildAt(i);
             if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
-                    && v != mStatusGuard) {
+                    && (Flags.actionModeEdgeToEdge() || v != mStatusGuard)) {
                 removeViewAt(i);
             }
         }
diff --git a/core/java/com/android/internal/policy/DesktopModeCompatUtils.java b/core/java/com/android/internal/policy/DesktopModeCompatUtils.java
new file mode 100644
index 0000000..d7cfbdf
--- /dev/null
+++ b/core/java/com/android/internal/policy/DesktopModeCompatUtils.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.policy;
+
+import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
+import static android.content.pm.ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS;
+
+import android.annotation.NonNull;
+import android.content.pm.ActivityInfo;
+import android.window.DesktopModeFlags;
+
+/**
+ * Utility functions for app compat in desktop windowing used by both window manager and System UI.
+ * @hide
+ */
+public final class DesktopModeCompatUtils {
+
+    /**
+     * Whether the caption insets should be excluded from configuration for system to handle.
+     * The caller should ensure the activity is in or entering desktop view.
+     *
+     * <p> The treatment is enabled when all the of the following is true:
+     * <li> Any flags to forcibly consume caption insets are enabled.
+     * <li> Top activity have configuration coupled with insets.
+     * <li> Task is not resizeable or per-app override
+     * {@link ActivityInfo#OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS} is enabled.
+     */
+    public static boolean shouldExcludeCaptionFromAppBounds(@NonNull ActivityInfo info,
+            boolean isResizeable, boolean optOutEdgeToEdge) {
+        return DesktopModeFlags.EXCLUDE_CAPTION_FROM_APP_BOUNDS.isTrue()
+                && isAnyForceConsumptionFlagsEnabled()
+                && !isConfigurationDecoupled(info, optOutEdgeToEdge)
+                && (!isResizeable
+                    || info.isChangeEnabled(OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS));
+    }
+
+    private static boolean isConfigurationDecoupled(@NonNull ActivityInfo info,
+            boolean optOutEdgeToEdge) {
+        return info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED) && !optOutEdgeToEdge;
+    }
+
+    private static boolean isAnyForceConsumptionFlagsEnabled() {
+        return DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isTrue()
+                || DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue();
+    }
+}
diff --git a/core/java/com/android/internal/policy/SystemBarUtils.java b/core/java/com/android/internal/policy/SystemBarUtils.java
index 4ed15fa..783c686 100644
--- a/core/java/com/android/internal/policy/SystemBarUtils.java
+++ b/core/java/com/android/internal/policy/SystemBarUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.policy;
 
+import android.annotation.DimenRes;
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Insets;
@@ -99,4 +101,19 @@
     public static int getTaskbarHeight(Resources res) {
         return res.getDimensionPixelSize(R.dimen.taskbar_frame_height);
     }
+
+    /**
+     * Gets the default app header height in desktop view in pixels.
+     */
+    public static int getDesktopViewAppHeaderHeightPx(@NonNull Context context) {
+        return context.getResources().getDimensionPixelSize(getDesktopViewAppHeaderHeightId());
+    }
+
+    /**
+     * Gets the dimen resource id of the default app header height in desktop view.
+     */
+    @DimenRes
+    public static int getDesktopViewAppHeaderHeightId() {
+        return R.dimen.desktop_view_default_header_height;
+    }
 }
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index 2d98994..6f7e5ad 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -79,6 +79,7 @@
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.UUID;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
@@ -864,5 +865,24 @@
             throw new RuntimeException("Both mMessageString and mMessageHash should never be null");
         }
     }
+
+    /**
+     * This is only used by unit tests to wait until {@link #connectToConfigurationService} is
+     * done. Because unit tests are sensitive to concurrent accesses.
+     */
+    @VisibleForTesting
+    public static void waitForInitialization() {
+        final IProtoLog currentInstance = ProtoLog.getSingleInstance();
+        if (!(currentInstance instanceof PerfettoProtoLogImpl protoLog)) {
+            return;
+        }
+        try {
+            protoLog.mBackgroundLoggingService.submit(() -> {
+                Log.i(LOG_TAG, "Complete initialization");
+            }).get();
+        } catch (InterruptedException | ExecutionException e) {
+            Log.e(LOG_TAG, "Failed to wait for tracing service", e);
+        }
+    }
 }
 
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 9380d99..0791612 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -105,7 +105,7 @@
             in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
             in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, int userId,
-            in ImeOnBackInvokedDispatcher imeDispatcher);
+            in ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible);
 
     // If windowToken is null, this just does startInput().  Otherwise this reports that a window
     // has gained focus, and if 'editorInfo' is non-null then also does startInput.
@@ -120,8 +120,8 @@
             in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
             in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, int userId,
-            in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
-            boolean useAsyncShowHideMethod);
+            in ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible,
+            int startInputSeq, boolean useAsyncShowHideMethod);
 
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
@@ -224,7 +224,7 @@
      *  async **/
     oneway void acceptStylusHandwritingDelegationAsync(in IInputMethodClient client, in int userId,
             in String delegatePackageName, in String delegatorPackageName, int flags,
-            in IBooleanListener callback);
+                in IBooleanListener callback);
 
     /** Returns {@code true} if currently selected IME supports Stylus handwriting. */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 80fc2188..d5bb511 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -34,6 +34,7 @@
 
 import com.android.internal.R;
 import com.android.internal.view.menu.MenuBuilder;
+import com.android.window.flags.Flags;
 
 /**
  * @hide
@@ -315,12 +316,14 @@
 
         final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
 
-        int maxHeight = mContentHeight > 0 ?
-                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
+        final int maxHeight = !Flags.actionModeEdgeToEdge() && mContentHeight > 0
+                ? mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
 
         final int verticalPadding = getPaddingTop() + getPaddingBottom();
         int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
-        final int height = maxHeight - verticalPadding;
+        final int height = Flags.actionModeEdgeToEdge()
+                ? mContentHeight > 0 ? mContentHeight : maxHeight
+                : maxHeight - verticalPadding;
         final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
 
         if (mClose != null) {
@@ -376,7 +379,8 @@
             }
             setMeasuredDimension(contentWidth, measuredHeight);
         } else {
-            setMeasuredDimension(contentWidth, maxHeight);
+            setMeasuredDimension(contentWidth, Flags.actionModeEdgeToEdge()
+                    ? mContentHeight + verticalPadding : maxHeight);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index ff57fd4..362b79d 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -294,54 +294,24 @@
         }
     }
 
-    private boolean applyInsets(View view, Rect insets, boolean toPadding,
-            boolean left, boolean top, boolean right, boolean bottom) {
-        boolean changed;
-        if (toPadding) {
-            changed = setMargin(view, EMPTY_RECT, left, top, right, bottom);
-            changed |= setPadding(view, insets, left, top, right, bottom);
-        } else {
-            changed = setPadding(view, EMPTY_RECT, left, top, right, bottom);
-            changed |= setMargin(view, insets, left, top, right, bottom);
-        }
-        return changed;
-    }
-
-    private boolean setPadding(View view, Rect insets,
-            boolean left, boolean top, boolean right, boolean bottom) {
-        if ((left && view.getPaddingLeft() != insets.left)
-                || (top && view.getPaddingTop() != insets.top)
-                || (right && view.getPaddingRight() != insets.right)
-                || (bottom && view.getPaddingBottom() != insets.bottom)) {
-            view.setPadding(
-                    left ? insets.left : view.getPaddingLeft(),
-                    top ? insets.top : view.getPaddingTop(),
-                    right ? insets.right : view.getPaddingRight(),
-                    bottom ? insets.bottom : view.getPaddingBottom());
-            return true;
-        }
-        return false;
-    }
-
-    private boolean setMargin(View view,  Rect insets,
-            boolean left, boolean top, boolean right, boolean bottom) {
+    private boolean setMargin(View view, int left, int top, int right, int bottom) {
         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
         boolean changed = false;
-        if (left && lp.leftMargin != insets.left) {
+        if (lp.leftMargin != left) {
             changed = true;
-            lp.leftMargin = insets.left;
+            lp.leftMargin = left;
         }
-        if (top && lp.topMargin != insets.top) {
+        if (lp.topMargin != top) {
             changed = true;
-            lp.topMargin = insets.top;
+            lp.topMargin = top;
         }
-        if (right && lp.rightMargin != insets.right) {
+        if (lp.rightMargin != right) {
             changed = true;
-            lp.rightMargin = insets.right;
+            lp.rightMargin = right;
         }
-        if (bottom && lp.bottomMargin != insets.bottom) {
+        if (lp.bottomMargin != bottom) {
             changed = true;
-            lp.bottomMargin = insets.bottom;
+            lp.bottomMargin = bottom;
         }
         return changed;
     }
@@ -367,12 +337,30 @@
         final Insets sysInsets = insets.getSystemWindowInsets();
         mSystemInsets.set(sysInsets.left, sysInsets.top, sysInsets.right, sysInsets.bottom);
 
-        // The top and bottom action bars are always within the content area.
-        boolean changed = applyInsets(mActionBarTop, mSystemInsets,
-                mActionBarExtendsIntoSystemInsets, true, true, true, false);
-        if (mActionBarBottom != null) {
-            changed |= applyInsets(mActionBarBottom, mSystemInsets,
-                    mActionBarExtendsIntoSystemInsets, true, false, true, true);
+        boolean changed = false;
+        if (mActionBarExtendsIntoSystemInsets) {
+            // Don't extend into navigation bar area so the width can align with status bar
+            // color view.
+            final Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars());
+            final int paddingLeft = sysInsets.left - navBarInsets.left;
+            final int paddingRight = sysInsets.right - navBarInsets.right;
+            mActionBarTop.setPadding(paddingLeft, sysInsets.top, paddingRight, 0);
+            changed |= setMargin(
+                    mActionBarTop, navBarInsets.left, 0, navBarInsets.right, 0);
+            if (mActionBarBottom != null) {
+                mActionBarBottom.setPadding(paddingLeft, 0, paddingRight, sysInsets.bottom);
+                changed |= setMargin(
+                        mActionBarBottom, navBarInsets.left, 0, navBarInsets.right, 0);
+            }
+        } else {
+            mActionBarTop.setPadding(0, 0, 0, 0);
+            changed |= setMargin(
+                    mActionBarTop, sysInsets.left, sysInsets.top, sysInsets.right, 0);
+            if (mActionBarBottom != null) {
+                mActionBarBottom.setPadding(0, 0, 0, 0);
+                changed |= setMargin(
+                        mActionBarTop, sysInsets.left, 0, sysInsets.right, sysInsets.bottom);
+            }
         }
 
         // Cannot use the result of computeSystemWindowInsets, because that consumes the
@@ -521,7 +509,12 @@
                 );
             }
         }
-        setMargin(mContent, mContentInsets, true, true, true, true);
+        setMargin(
+                mContent,
+                mContentInsets.left,
+                mContentInsets.top,
+                mContentInsets.right,
+                mContentInsets.bottom);
 
         if (!mLastInnerInsets.equals(mInnerInsets)) {
             // If the inner insets have changed, we need to dispatch this down to
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3f96507..d35072f 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -22,7 +22,6 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-import static android.security.Flags.reportPrimaryAuthAttempts;
 import static android.security.Flags.shouldTrustManagerListenForPrimaryAuth;
 
 import static com.android.internal.widget.flags.Flags.hideLastCharWithPhysicalInput;
@@ -472,7 +471,7 @@
             return;
         }
         getDevicePolicyManager().reportFailedPasswordAttempt(userId);
-        if (!reportPrimaryAuthAttempts() || !shouldTrustManagerListenForPrimaryAuth()) {
+        if (!shouldTrustManagerListenForPrimaryAuth()) {
             getTrustManager().reportUnlockAttempt(/* authenticated= */ false, userId);
         }
     }
@@ -483,7 +482,7 @@
             return;
         }
         getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
-        if (!reportPrimaryAuthAttempts() || !shouldTrustManagerListenForPrimaryAuth()) {
+        if (!shouldTrustManagerListenForPrimaryAuth()) {
             getTrustManager().reportUnlockAttempt(/* authenticated= */ true, userId);
         }
     }
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index cac2024..cf9f1c8 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.widget;
 
+import static android.app.Flags.notificationsRedesignTemplates;
 import static android.app.Notification.CallStyle.DEBUG_NEW_ACTION_LAYOUT;
 import static android.app.Flags.evenlyDividedCallStyleActionLayout;
 
@@ -368,12 +369,17 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mDefaultPaddingBottom = getPaddingBottom();
-        mDefaultPaddingTop = getPaddingTop();
-        updateHeights();
+        if (!notificationsRedesignTemplates()) {
+            mDefaultPaddingBottom = getPaddingBottom();
+            mDefaultPaddingTop = getPaddingTop();
+            updateHeights();
+        }
     }
 
     private void updateHeights() {
+        if (notificationsRedesignTemplates()) {
+            return;
+        }
         int inset = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.button_inset_vertical_material);
         mEmphasizedPaddingTop = getResources().getDimensionPixelSize(
@@ -440,6 +446,9 @@
      */
     @RemotableViewMethod
     public void setEmphasizedMode(boolean emphasizedMode) {
+        if (notificationsRedesignTemplates()) {
+            return;
+        }
         mEmphasizedMode = emphasizedMode;
         int height;
         if (emphasizedMode) {
@@ -462,7 +471,9 @@
     }
 
     public int getExtraMeasureHeight() {
-        if (mEmphasizedMode) {
+        // Note: the emphasized height is no longer different from the regular height when the
+        // notificationsRedesignTemplates flag is on.
+        if (!notificationsRedesignTemplates() && mEmphasizedMode) {
             return mEmphasizedHeight - mRegularHeight;
         }
         return 0;
diff --git a/core/java/com/android/internal/widget/NotificationCloseButton.java b/core/java/com/android/internal/widget/NotificationCloseButton.java
index bce266d..0d801b1 100644
--- a/core/java/com/android/internal/widget/NotificationCloseButton.java
+++ b/core/java/com/android/internal/widget/NotificationCloseButton.java
@@ -21,6 +21,8 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
 import android.view.RemotableViewMethod;
 import android.view.View;
@@ -40,6 +42,8 @@
     @ColorInt private int mBackgroundColor;
     @ColorInt private int mForegroundColor;
 
+    private Drawable mPillDrawable;
+
     public NotificationCloseButton(Context context) {
         this(context, null, 0, 0);
     }
@@ -62,6 +66,10 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         setContentDescription(mContext.getText(R.string.close_button_text));
+
+        final LayerDrawable layeredPill = (LayerDrawable) this.getBackground();
+        mPillDrawable = layeredPill.findDrawableByLayerId(R.id.close_button_pill_colorized_layer);
+
         boolean notificationCloseButtonSupported = Resources.getSystem().getBoolean(
                 com.android.internal.R.bool.config_notificationCloseButtonSupported);
         this.setVisibility(notificationCloseButtonSupported ? View.VISIBLE : View.GONE);
@@ -76,8 +84,11 @@
 
     private void updateColors() {
         if (mBackgroundColor != 0) {
-            this.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
+            // TODO(http://b/365585705): Ensure this close button compatible with the ongoing effort
+            // that makes notification rows partially-transparent.
+            this.mPillDrawable.setTintList(ColorStateList.valueOf(mBackgroundColor));
         }
+
         if (mForegroundColor != 0) {
             this.setImageTintList(ColorStateList.valueOf(mForegroundColor));
         }
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index dd12f69..42b1bdb 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -129,6 +129,16 @@
         updateExpandedState();
     }
 
+    /**
+     * Adjust the padding at the start of the view based on the layout direction (RTL/LTR).
+     * This is needed because RemoteViews don't have an equivalent for
+     * {@link this#setPaddingRelative}.
+     */
+    @RemotableViewMethod
+    public void setStartPadding(int startPadding) {
+        setPaddingRelative(startPadding, getPaddingTop(), getPaddingEnd(), getPaddingBottom());
+    }
+
     private void updateExpandedState() {
         int drawableId;
         int contentDescriptionId;
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
index a53d6b8..3fe6873 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/AndroidPlatformSemanticNodeApplier.java
@@ -132,8 +132,13 @@
             }
         }
 
-        // TODO correct values
-        nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
+        if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
+            nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(1, -1, false));
+            nodeInfo.setClassName("android.widget.HorizontalScrollView");
+        } else {
+            nodeInfo.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(-1, 1, false));
+            nodeInfo.setClassName("android.widget.ScrollView");
+        }
 
         if (scrollDirection == RootContentBehavior.SCROLL_HORIZONTAL) {
             nodeInfo.setClassName("android.widget.HorizontalScrollView");
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
index d3a496d..db2c460 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/CoreDocumentAccessibility.java
@@ -33,6 +33,7 @@
 import com.android.internal.widget.remotecompose.core.semantics.AccessibleComponent;
 import com.android.internal.widget.remotecompose.core.semantics.CoreSemantics;
 import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent;
+import com.android.internal.widget.remotecompose.core.semantics.ScrollableComponent.ScrollDirection;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -104,9 +105,9 @@
             if (isClickAction(action)) {
                 return performClick(component);
             } else if (isScrollForwardAction(action)) {
-                return scrollByOffset(mRemoteContext, component, -500) != 0;
+                return scrollDirection(mRemoteContext, component, ScrollDirection.FORWARD);
             } else if (isScrollBackwardAction(action)) {
-                return scrollByOffset(mRemoteContext, component, 500) != 0;
+                return scrollDirection(mRemoteContext, component, ScrollDirection.BACKWARD);
             } else if (isShowOnScreenAction(action)) {
                 return showOnScreen(mRemoteContext, component);
             } else {
@@ -141,19 +142,32 @@
     }
 
     private boolean showOnScreen(RemoteContext context, Component component) {
-        if (component.getParent() instanceof LayoutComponent) {
-            LayoutComponent parent = (LayoutComponent) component.getParent();
-            ScrollableComponent scrollable = parent.selfOrModifier(ScrollableComponent.class);
+        ScrollableComponent scrollable = findScrollable(component);
 
-            if (scrollable != null) {
-                scrollable.showOnScreen(context, component.getComponentId());
-                return true;
-            }
+        if (scrollable != null) {
+            return scrollable.showOnScreen(context, component);
         }
 
         return false;
     }
 
+    @Nullable
+    private static ScrollableComponent findScrollable(Component component) {
+        Component parent = component.getParent();
+
+        while (parent != null) {
+            ScrollableComponent scrollable = parent.selfOrModifier(ScrollableComponent.class);
+
+            if (scrollable != null) {
+                return scrollable;
+            } else {
+                parent = parent.getParent();
+            }
+        }
+
+        return null;
+    }
+
     /**
      * scroll content by the given offset
      *
@@ -173,13 +187,32 @@
     }
 
     /**
+     * scroll content in a given direction
+     *
+     * @param context
+     * @param component
+     * @param direction
+     * @return
+     */
+    public boolean scrollDirection(
+            RemoteContext context, Component component, ScrollDirection direction) {
+        ScrollableComponent scrollable = component.selfOrModifier(ScrollableComponent.class);
+
+        if (scrollable != null) {
+            return scrollable.scrollDirection(context, direction);
+        }
+
+        return false;
+    }
+
+    /**
      * Perform a click on the given component
      *
      * @param component
      * @return
      */
     public boolean performClick(Component component) {
-        mDocument.performClick(mRemoteContext, component.getComponentId());
+        mDocument.performClick(mRemoteContext, component.getComponentId(), "");
         return true;
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
index c38a44a..da4e8d6 100644
--- a/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
+++ b/core/java/com/android/internal/widget/remotecompose/accessibility/PlatformRemoteComposeTouchHelper.java
@@ -39,6 +39,7 @@
     private final RemoteComposeDocumentAccessibility mRemoteDocA11y;
 
     private final SemanticNodeApplier<AccessibilityNodeInfo> mApplier;
+    private final View mHost;
 
     public PlatformRemoteComposeTouchHelper(
             View host,
@@ -47,6 +48,7 @@
         super(host);
         this.mRemoteDocA11y = remoteDocA11y;
         this.mApplier = applier;
+        this.mHost = host;
     }
 
     public static PlatformRemoteComposeTouchHelper forRemoteComposePlayer(
@@ -150,6 +152,7 @@
             boolean performed = mRemoteDocA11y.performAction(component, action, arguments);
 
             if (performed) {
+                mHost.invalidate();
                 invalidateRoot();
             }
 
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 796ea8d..766fbf1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -19,7 +19,10 @@
 import android.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.remotecompose.core.operations.BitmapData;
 import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
+import com.android.internal.widget.remotecompose.core.operations.DrawContent;
+import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
 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;
@@ -28,9 +31,12 @@
 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.Utils;
+import com.android.internal.widget.remotecompose.core.operations.layout.CanvasOperations;
 import com.android.internal.widget.remotecompose.core.operations.layout.Component;
 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.LayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
@@ -39,6 +45,8 @@
 import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
+import com.android.internal.widget.remotecompose.core.types.LongConstant;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -65,7 +73,9 @@
 
     // We also keep a more fine-grained BUILD number, exposed as
     // ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
-    static final float BUILD = 0.3f;
+    static final float BUILD = 0.7f;
+
+    private static final boolean UPDATE_VARIABLES_BEFORE_LAYOUT = false;
 
     @NonNull ArrayList<Operation> mOperations = new ArrayList<>();
 
@@ -102,6 +112,8 @@
 
     private IntMap<Object> mDocProperties;
 
+    boolean mFirstPaint = true;
+
     /** Returns a version number that is monotonically increasing. */
     public static int getDocumentApiLevel() {
         return DOCUMENT_API_LEVEL;
@@ -437,6 +449,94 @@
         return mDocProperties.get(key);
     }
 
+    /**
+     * Apply a collection of operations to the document
+     *
+     * @param delta the delta to apply
+     */
+    public void applyUpdate(CoreDocument delta) {
+        HashMap<Integer, TextData> txtData = new HashMap<Integer, TextData>();
+        HashMap<Integer, BitmapData> imgData = new HashMap<Integer, BitmapData>();
+        HashMap<Integer, FloatConstant> fltData = new HashMap<Integer, FloatConstant>();
+        HashMap<Integer, IntegerConstant> intData = new HashMap<Integer, IntegerConstant>();
+        HashMap<Integer, LongConstant> longData = new HashMap<Integer, LongConstant>();
+        recursiveTreverse(
+                mOperations,
+                (op) -> {
+                    if (op instanceof TextData) {
+                        TextData d = (TextData) op;
+                        txtData.put(d.mTextId, d);
+                    } else if (op instanceof BitmapData) {
+                        BitmapData d = (BitmapData) op;
+                        imgData.put(d.mImageId, d);
+                    } else if (op instanceof FloatConstant) {
+                        FloatConstant d = (FloatConstant) op;
+                        fltData.put(d.mId, d);
+                    } else if (op instanceof IntegerConstant) {
+                        IntegerConstant d = (IntegerConstant) op;
+                        intData.put(d.mId, d);
+                    } else if (op instanceof LongConstant) {
+                        LongConstant d = (LongConstant) op;
+                        longData.put(d.mId, d);
+                    }
+                });
+
+        recursiveTreverse(
+                delta.mOperations,
+                (op) -> {
+                    if (op instanceof TextData) {
+                        TextData t = (TextData) op;
+                        TextData txtInDoc = txtData.get(t.mTextId);
+                        if (txtInDoc != null) {
+                            txtInDoc.update(t);
+                            Utils.log("update" + t.mText);
+                            txtInDoc.markDirty();
+                        }
+                    } else if (op instanceof BitmapData) {
+                        BitmapData b = (BitmapData) op;
+                        BitmapData imgInDoc = imgData.get(b.mImageId);
+                        if (imgInDoc != null) {
+                            imgInDoc.update(b);
+                            imgInDoc.markDirty();
+                        }
+                    } else if (op instanceof FloatConstant) {
+                        FloatConstant f = (FloatConstant) op;
+                        FloatConstant fltInDoc = fltData.get(f.mId);
+                        if (fltInDoc != null) {
+                            fltInDoc.update(f);
+                            fltInDoc.markDirty();
+                        }
+                    } else if (op instanceof IntegerConstant) {
+                        IntegerConstant ic = (IntegerConstant) op;
+                        IntegerConstant intInDoc = intData.get(ic.mId);
+                        if (intInDoc != null) {
+                            intInDoc.update(ic);
+                            intInDoc.markDirty();
+                        }
+                    } else if (op instanceof LongConstant) {
+                        LongConstant lc = (LongConstant) op;
+                        LongConstant longInDoc = longData.get(lc.mId);
+                        if (longInDoc != null) {
+                            longInDoc.update(lc);
+                            longInDoc.markDirty();
+                        }
+                    }
+                });
+    }
+
+    private interface Visitor {
+        void visit(Operation op);
+    }
+
+    private void recursiveTreverse(ArrayList<Operation> mOperations, Visitor visitor) {
+        for (Operation op : mOperations) {
+            if (op instanceof Container) {
+                recursiveTreverse(((Component) op).mList, visitor);
+            }
+            visitor.visit(op);
+        }
+    }
+
     // ============== Haptic support ==================
     public interface HapticEngine {
         /**
@@ -678,6 +778,7 @@
         ArrayList<Operation> ops = finalOperationsList;
 
         ArrayList<Container> containers = new ArrayList<>();
+        LayoutComponent lastLayoutComponent = null;
 
         mLastId = -1;
         for (Operation o : operations) {
@@ -697,6 +798,9 @@
                     if (component.getComponentId() < mLastId) {
                         mLastId = component.getComponentId();
                     }
+                    if (component instanceof LayoutComponent) {
+                        lastLayoutComponent = (LayoutComponent) component;
+                    }
                 }
                 containers.add(container);
                 ops = container.getList();
@@ -723,7 +827,13 @@
                     }
                     ops.add((Operation) container);
                 }
+                if (container instanceof CanvasOperations) {
+                    ((CanvasOperations) container).setComponent(lastLayoutComponent);
+                }
             } else {
+                if (o instanceof DrawContent) {
+                    ((DrawContent) o).setComponent(lastLayoutComponent);
+                }
                 ops.add(o);
             }
         }
@@ -732,18 +842,25 @@
 
     @NonNull private HashMap<Integer, Component> mComponentMap = new HashMap<Integer, Component>();
 
+    /**
+     * Register all the operations recursively
+     *
+     * @param context
+     * @param list
+     */
     private void registerVariables(
             @NonNull RemoteContext context, @NonNull ArrayList<Operation> list) {
         for (Operation op : list) {
             if (op instanceof VariableSupport) {
-                ((VariableSupport) op).updateVariables(context);
                 ((VariableSupport) op).registerListening(context);
             }
             if (op instanceof Component) {
                 mComponentMap.put(((Component) op).getComponentId(), (Component) op);
-                registerVariables(context, ((Component) op).getList());
                 ((Component) op).registerVariables(context);
             }
+            if (op instanceof Container) {
+                registerVariables(context, ((Container) op).getList());
+            }
             if (op instanceof ComponentValue) {
                 ComponentValue v = (ComponentValue) op;
                 Component component = mComponentMap.get(v.getComponentId());
@@ -756,14 +873,34 @@
             if (op instanceof ComponentModifiers) {
                 for (ModifierOperation modifier : ((ComponentModifiers) op).getList()) {
                     if (modifier instanceof VariableSupport) {
-                        ((VariableSupport) modifier).updateVariables(context);
                         ((VariableSupport) modifier).registerListening(context);
                     }
                 }
             }
+        }
+    }
+
+    /**
+     * Apply the operations recursively, for the original initialization pass with mode == DATA
+     *
+     * @param context
+     * @param list
+     */
+    private void applyOperations(
+            @NonNull RemoteContext context, @NonNull ArrayList<Operation> list) {
+        for (Operation op : list) {
+            if (op instanceof VariableSupport) {
+                ((VariableSupport) op).updateVariables(context);
+            }
+            if (op instanceof Component) { // for componentvalues...
+                ((Component) op).updateVariables(context);
+            }
             op.markNotDirty();
             op.apply(context);
             context.incrementOpCount();
+            if (op instanceof Container) {
+                applyOperations(context, ((Container) op).getList());
+            }
         }
     }
 
@@ -783,7 +920,12 @@
         mTimeVariables.updateTime(context);
 
         registerVariables(context, mOperations);
+        applyOperations(context, mOperations);
         context.mMode = RemoteContext.ContextMode.UNSET;
+
+        if (UPDATE_VARIABLES_BEFORE_LAYOUT) {
+            mFirstPaint = true;
+        }
     }
 
     ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -895,7 +1037,7 @@
      *
      * @param id the click area id
      */
-    public void performClick(@NonNull RemoteContext context, int id) {
+    public void performClick(@NonNull RemoteContext context, int id, @NonNull String metadata) {
         for (ClickAreaRepresentation clickArea : mClickAreas) {
             if (clickArea.mId == id) {
                 warnClickListeners(clickArea);
@@ -904,7 +1046,7 @@
         }
 
         for (IdActionCallback listener : mIdActionListeners) {
-            listener.onAction(id, "");
+            listener.onAction(id, metadata);
         }
 
         Component component = getComponent(id);
@@ -1093,6 +1235,28 @@
     }
 
     /**
+     * Traverse the list of operations to update the variables. TODO: this should walk the
+     * dependency tree instead
+     *
+     * @param context
+     * @param operations
+     */
+    private void updateVariables(
+            @NonNull RemoteContext context, int theme, List<Operation> operations) {
+        for (int i = 0; i < operations.size(); i++) {
+            Operation op = operations.get(i);
+            if (op.isDirty() && op instanceof VariableSupport) {
+                ((VariableSupport) op).updateVariables(context);
+                op.apply(context);
+                op.markNotDirty();
+            }
+            if (op instanceof Container) {
+                updateVariables(context, theme, ((Container) op).getList());
+            }
+        }
+    }
+
+    /**
      * Paint the document
      *
      * @param context the provided PaintContext
@@ -1110,6 +1274,15 @@
         context.mRemoteComposeState = mRemoteComposeState;
         context.mRemoteComposeState.setContext(context);
 
+        if (UPDATE_VARIABLES_BEFORE_LAYOUT) {
+            // Update any dirty variables
+            if (mFirstPaint) {
+                mFirstPaint = false;
+            } else {
+                updateVariables(context, theme, mOperations);
+            }
+        }
+
         // If we have a content sizing set, we are going to take the original document
         // dimension into account and apply scale+translate according to the RootContentBehavior
         // rules.
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 9cbafab..add9d5b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -25,6 +25,7 @@
 import com.android.internal.widget.remotecompose.core.operations.ColorConstant;
 import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
 import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
+import com.android.internal.widget.remotecompose.core.operations.ConditionalOperations;
 import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
 import com.android.internal.widget.remotecompose.core.operations.DataListIds;
 import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
@@ -50,6 +51,7 @@
 import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
 import com.android.internal.widget.remotecompose.core.operations.FloatFunctionCall;
 import com.android.internal.widget.remotecompose.core.operations.FloatFunctionDefine;
+import com.android.internal.widget.remotecompose.core.operations.HapticFeedback;
 import com.android.internal.widget.remotecompose.core.operations.Header;
 import com.android.internal.widget.remotecompose.core.operations.ImageAttribute;
 import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
@@ -64,6 +66,7 @@
 import com.android.internal.widget.remotecompose.core.operations.ParticlesCreate;
 import com.android.internal.widget.remotecompose.core.operations.ParticlesLoop;
 import com.android.internal.widget.remotecompose.core.operations.PathAppend;
+import com.android.internal.widget.remotecompose.core.operations.PathCombine;
 import com.android.internal.widget.remotecompose.core.operations.PathCreate;
 import com.android.internal.widget.remotecompose.core.operations.PathData;
 import com.android.internal.widget.remotecompose.core.operations.PathTween;
@@ -101,17 +104,20 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleRowLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.FitBoxLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.ImageLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DrawContentOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostActionMetadataOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostActionOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostNamedActionOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.MarqueeModifierOperation;
@@ -222,6 +228,9 @@
     public static final int ATTRIBUTE_TIME = 172;
     public static final int CANVAS_OPERATIONS = 173;
     public static final int MODIFIER_DRAW_CONTENT = 174;
+    public static final int PATH_COMBINE = 175;
+    public static final int HAPTIC_FEEDBACK = 177;
+    public static final int CONDITIONAL_OPERATIONS = 178;
 
     ///////////////////////////////////////// ======================
 
@@ -241,6 +250,7 @@
     public static final int LAYOUT_CANVAS_CONTENT = 207;
     public static final int LAYOUT_TEXT = 208;
     public static final int LAYOUT_STATE = 217;
+    public static final int LAYOUT_IMAGE = 234;
 
     public static final int COMPONENT_START = 2;
 
@@ -248,6 +258,7 @@
     public static final int MODIFIER_HEIGHT = 67;
     public static final int MODIFIER_WIDTH_IN = 231;
     public static final int MODIFIER_HEIGHT_IN = 232;
+    public static final int MODIFIER_COLLAPSIBLE_PRIORITY = 235;
     public static final int MODIFIER_BACKGROUND = 55;
     public static final int MODIFIER_BORDER = 107;
     public static final int MODIFIER_PADDING = 58;
@@ -272,6 +283,7 @@
 
     public static final int MODIFIER_VISIBILITY = 211;
     public static final int HOST_ACTION = 209;
+    public static final int HOST_METADATA_ACTION = 216;
     public static final int HOST_NAMED_ACTION = 210;
 
     public static final int VALUE_INTEGER_CHANGE_ACTION = 212;
@@ -358,6 +370,7 @@
         map.put(MODIFIER_HEIGHT, HeightModifierOperation::read);
         map.put(MODIFIER_WIDTH_IN, WidthInModifierOperation::read);
         map.put(MODIFIER_HEIGHT_IN, HeightInModifierOperation::read);
+        map.put(MODIFIER_COLLAPSIBLE_PRIORITY, CollapsiblePriorityModifierOperation::read);
         map.put(MODIFIER_PADDING, PaddingModifierOperation::read);
         map.put(MODIFIER_BACKGROUND, BackgroundModifierOperation::read);
         map.put(MODIFIER_BORDER, BorderModifierOperation::read);
@@ -379,6 +392,7 @@
         map.put(CONTAINER_END, ContainerEnd::read);
 
         map.put(HOST_ACTION, HostActionOperation::read);
+        map.put(HOST_METADATA_ACTION, HostActionMetadataOperation::read);
         map.put(HOST_NAMED_ACTION, HostNamedActionOperation::read);
         map.put(VALUE_INTEGER_CHANGE_ACTION, ValueIntegerChangeActionOperation::read);
         map.put(
@@ -401,7 +415,7 @@
         map.put(LAYOUT_CANVAS, CanvasLayout::read);
         map.put(LAYOUT_CANVAS_CONTENT, CanvasContent::read);
         map.put(LAYOUT_TEXT, TextLayout::read);
-
+        map.put(LAYOUT_IMAGE, ImageLayout::read);
         map.put(LAYOUT_STATE, StateLayout::read);
         map.put(DRAW_CONTENT, DrawContent::read);
 
@@ -426,6 +440,9 @@
         map.put(ATTRIBUTE_IMAGE, ImageAttribute::read);
         map.put(ATTRIBUTE_TEXT, TextAttribute::read);
         map.put(ATTRIBUTE_TIME, TimeAttribute::read);
+        map.put(PATH_COMBINE, PathCombine::read);
+        map.put(HAPTIC_FEEDBACK, HapticFeedback::read);
+        map.put(CONDITIONAL_OPERATIONS, ConditionalOperations::read);
 
         //        map.put(ACCESSIBILITY_CUSTOM_ACTION, CoreSemantics::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 1b0b9d7..e61ca4c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -340,6 +340,17 @@
     public abstract void tweenPath(int out, int path1, int path2, float tween);
 
     /**
+     * Perform a between two path and return the resulting path
+     *
+     * @param out the interpolated path
+     * @param path1 start path
+     * @param path2 end path
+     * @param operation 0 = difference , 1 = intersection, 2 = reverse_difference, 3 = union, 4 =
+     *     xor
+     */
+    public abstract void combinePath(int out, int path1, int path2, byte operation);
+
+    /**
      * This applies changes to the current paint
      *
      * @param mPaintData the list of changes
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 c249adf..1f02668 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -28,6 +28,7 @@
 import com.android.internal.widget.remotecompose.core.operations.ColorConstant;
 import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
 import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
+import com.android.internal.widget.remotecompose.core.operations.ConditionalOperations;
 import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
 import com.android.internal.widget.remotecompose.core.operations.DataListIds;
 import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
@@ -53,6 +54,7 @@
 import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
 import com.android.internal.widget.remotecompose.core.operations.FloatFunctionCall;
 import com.android.internal.widget.remotecompose.core.operations.FloatFunctionDefine;
+import com.android.internal.widget.remotecompose.core.operations.HapticFeedback;
 import com.android.internal.widget.remotecompose.core.operations.Header;
 import com.android.internal.widget.remotecompose.core.operations.ImageAttribute;
 import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
@@ -67,6 +69,7 @@
 import com.android.internal.widget.remotecompose.core.operations.ParticlesCreate;
 import com.android.internal.widget.remotecompose.core.operations.ParticlesLoop;
 import com.android.internal.widget.remotecompose.core.operations.PathAppend;
+import com.android.internal.widget.remotecompose.core.operations.PathCombine;
 import com.android.internal.widget.remotecompose.core.operations.PathCreate;
 import com.android.internal.widget.remotecompose.core.operations.PathData;
 import com.android.internal.widget.remotecompose.core.operations.PathTween;
@@ -99,6 +102,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleRowLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.FitBoxLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.ImageLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
 import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
@@ -890,7 +894,7 @@
      * @return new id that merges the two text
      */
     public int textMerge(int id1, int id2) {
-        int textId = addText(id1 + "+" + id2);
+        int textId = nextId();
         TextMerge.apply(mBuffer, textId, id1, id2);
         return textId;
     }
@@ -2091,6 +2095,19 @@
     }
 
     /**
+     * Add an imagelayout command
+     *
+     * @param componentId component id
+     * @param animationId animation id
+     * @param bitmapId bitmap id
+     */
+    public void addImage(
+            int componentId, int animationId, int bitmapId, int scaleType, float alpha) {
+        mLastComponentId = getComponentId(componentId);
+        ImageLayout.apply(mBuffer, componentId, animationId, bitmapId, scaleType, alpha);
+    }
+
+    /**
      * Add a row start tag
      *
      * @param componentId component id
@@ -2273,8 +2290,6 @@
         return mRemoteComposeState.nextId();
     }
 
-    private boolean mInImpulseProcess = false;
-
     /**
      * add an impulse. (must be followed by impulse end)
      *
@@ -2283,22 +2298,16 @@
      */
     public void addImpulse(float duration, float start) {
         ImpulseOperation.apply(mBuffer, duration, start);
-        mInImpulseProcess = false;
     }
 
     /** add an impulse process */
     public void addImpulseProcess() {
         ImpulseProcess.apply(mBuffer);
-        mInImpulseProcess = true;
     }
 
     /** Add an impulse end */
     public void addImpulseEnd() {
         ContainerEnd.apply(mBuffer);
-        if (mInImpulseProcess) {
-            ContainerEnd.apply(mBuffer);
-        }
-        mInImpulseProcess = false;
     }
 
     /**
@@ -2422,4 +2431,37 @@
 
         return imageId;
     }
+
+    /**
+     * Combine two paths
+     *
+     * @param id output id
+     * @param path1 first path
+     * @param path2 second path
+     * @param op operation to perform OP_DIFFERENCE, OP_INTERSECT, OP_REVERSE_DIFFERENCE, OP_UNION,
+     *     OP_XOR
+     */
+    public void pathCombine(int id, int path1, int path2, byte op) {
+        PathCombine.apply(mBuffer, id, path1, path2, op);
+    }
+
+    /**
+     * Perform a haptic feedback
+     *
+     * @param feedbackConstant
+     */
+    public void performHaptic(int feedbackConstant) {
+        HapticFeedback.apply(mBuffer, feedbackConstant);
+    }
+
+    /**
+     * Add a conditional operation
+     *
+     * @param type type of comparison
+     * @param a first value
+     * @param b second value
+     */
+    public void addConditionalOperations(byte type, float a, float b) {
+        ConditionalOperations.apply(mBuffer, type, a, b);
+    }
 }
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 c6b17e4..b297a02 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -46,7 +46,7 @@
             new CoreDocument(); // todo: is this a valid way to initialize? bbade@
     public @NonNull RemoteComposeState mRemoteComposeState =
             new RemoteComposeState(); // todo, is this a valid use of RemoteComposeState -- bbade@
-
+    private long mDocLoadTime = System.currentTimeMillis();
     @Nullable protected PaintContext mPaintContext = null;
     protected float mDensity = Float.NaN;
 
@@ -83,6 +83,20 @@
         }
     }
 
+    /**
+     * Get the time the document was loaded
+     *
+     * @return time in ms since the document was loaded
+     */
+    public long getDocLoadTime() {
+        return mDocLoadTime;
+    }
+
+    /** Set the time the document was loaded */
+    public void setDocLoadTime() {
+        mDocLoadTime = System.currentTimeMillis();
+    }
+
     public boolean isAnimationEnabled() {
         return mAnimate;
     }
@@ -552,6 +566,14 @@
     public abstract int getInteger(int id);
 
     /**
+     * Get a Long given an id
+     *
+     * @param id of the long
+     * @return the value
+     */
+    public abstract long getLong(int id);
+
+    /**
      * Get the color given and ID
      *
      * @param id of the color
@@ -629,6 +651,8 @@
     /** The delta between current and last Frame */
     public static final int ID_ANIMATION_DELTA_TIME = 31;
 
+    public static final int ID_EPOCH_SECOND = 32;
+
     public static final float FLOAT_DENSITY = Utils.asNan(ID_DENSITY);
 
     /** CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600 */
@@ -714,6 +738,9 @@
     /** When was this player built */
     public static final float FLOAT_API_LEVEL = Utils.asNan(ID_API_LEVEL);
 
+    /** The time in seconds since the epoch. */
+    public static final long INT_EPOCH_SECOND = ((long) ID_EPOCH_SECOND) + 0x100000000L;
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     // Click handling
     ///////////////////////////////////////////////////////////////////////////////////////////////
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 cd5b202..5730036 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -49,9 +49,12 @@
 
         OffsetDateTime offsetDateTime = dateTime.atZone(zoneId).toOffsetDateTime();
         ZoneOffset offset = offsetDateTime.getOffset();
+        long epochSec = dateTime.toEpochSecond(offset);
 
         context.loadFloat(RemoteContext.ID_OFFSET_TO_UTC, offset.getTotalSeconds());
         context.loadFloat(RemoteContext.ID_CONTINUOUS_SEC, sec);
+        // This will overflow in 2038.
+        context.loadInteger(RemoteContext.ID_EPOCH_SECOND, (int) epochSec);
         context.loadFloat(RemoteContext.ID_TIME_IN_SEC, currentSeconds);
         context.loadFloat(RemoteContext.ID_TIME_IN_MIN, currentMinute);
         context.loadFloat(RemoteContext.ID_TIME_IN_HR, hour);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 255d7a4..90929e0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -41,12 +41,12 @@
 public class BitmapData extends Operation implements SerializableToString, Serializable {
     private static final int OP_CODE = Operations.DATA_BITMAP;
     private static final String CLASS_NAME = "BitmapData";
-    int mImageId;
+    public final int mImageId;
     int mImageWidth;
     int mImageHeight;
     short mType;
     short mEncoding;
-    @NonNull final byte[] mBitmap;
+    @NonNull byte[] mBitmap;
 
     /** The max size of width or height */
     public static final int MAX_IMAGE_DIMENSION = 8000;
@@ -91,6 +91,19 @@
     }
 
     /**
+     * Update the bitmap data
+     *
+     * @param from the bitmap to copy
+     */
+    public void update(BitmapData from) {
+        this.mImageWidth = from.mImageWidth;
+        this.mImageHeight = from.mImageHeight;
+        this.mBitmap = from.mBitmap;
+        this.mType = from.mType;
+        this.mEncoding = from.mEncoding;
+    }
+
+    /**
      * The width of the image
      *
      * @return the width
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ConditionalOperations.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ConditionalOperations.java
new file mode 100644
index 0000000..da6035e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ConditionalOperations.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import 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.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.Container;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Represents conditional execution of a block of commands */
+public class ConditionalOperations extends PaintOperation
+        implements Container, VariableSupport, Serializable {
+    private static final String CLASS_NAME = "ConditionalOperations";
+
+    private static final int OP_CODE = Operations.CONDITIONAL_OPERATIONS;
+
+    @NonNull public ArrayList<Operation> mList = new ArrayList<>();
+
+    int mIndexVariableId;
+    byte mType;
+    float mVarA;
+    float mVarB;
+    float mVarAOut;
+    float mVarBOut;
+
+    /** Equality comparison */
+    public static final byte TYPE_EQ = 0;
+
+    /** Not equal comparison */
+    public static final byte TYPE_NEQ = 1;
+
+    /** Less than comparison */
+    public static final byte TYPE_LT = 2;
+
+    /** Less than or equal comparison */
+    public static final byte TYPE_LTE = 3;
+
+    /** Greater than comparison */
+    public static final byte TYPE_GT = 4;
+
+    /** Greater than or equal comparison */
+    public static final byte TYPE_GTE = 5;
+
+    private static final String[] TYPE_STR = {"EQ", "NEQ", "LT", "LTE", "GT", "GTE"};
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        if (Float.isNaN(mVarA)) {
+            context.listensTo(Utils.idFromNan(mVarA), this);
+        }
+        if (Float.isNaN(mVarB)) {
+            context.listensTo(Utils.idFromNan(mVarB), this);
+        }
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        mVarAOut = Float.isNaN(mVarA) ? context.getFloat(Utils.idFromNan(mVarA)) : mVarA;
+        mVarBOut = Float.isNaN(mVarB) ? context.getFloat(Utils.idFromNan(mVarB)) : mVarB;
+    }
+
+    /**
+     * Constructor
+     *
+     * @param type type of comparison
+     * @param a first value
+     * @param b second value
+     */
+    public ConditionalOperations(byte type, float a, float b) {
+        mType = type;
+        mVarAOut = mVarA = a;
+        mVarBOut = mVarB = b;
+    }
+
+    @Override
+    @NonNull
+    public ArrayList<Operation> getList() {
+        return mList;
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, mType, mVarA, mVarB);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder builder =
+                new StringBuilder(
+                        CLASS_NAME
+                                + " "
+                                + TYPE_STR[mType]
+                                + "("
+                                + Utils.idFromNan(mVarA)
+                                + ","
+                                + Utils.idFromNan(mVarB)
+                                + ")\n");
+        for (Operation operation : mList) {
+            builder.append("  ");
+            builder.append(operation);
+            builder.append("\n");
+        }
+        return builder.toString();
+    }
+
+    @NonNull
+    @Override
+    public String deepToString(@NonNull String indent) {
+        return (indent != null ? indent : "") + toString();
+    }
+
+    @Override
+    public void paint(@NonNull PaintContext context) {
+        RemoteContext remoteContext = context.getContext();
+        boolean run = false;
+        switch (mType) {
+            case TYPE_EQ:
+                run = mVarAOut == mVarBOut;
+                break;
+            case TYPE_NEQ:
+                run = mVarAOut != mVarBOut;
+                break;
+            case TYPE_LT:
+                run = mVarAOut < mVarBOut;
+                break;
+            case TYPE_LTE:
+                run = mVarAOut <= mVarBOut;
+                break;
+            case TYPE_GT:
+                run = mVarAOut > mVarBOut;
+                break;
+            case TYPE_GTE:
+                run = mVarAOut >= mVarBOut;
+                break;
+        }
+        if (run) {
+            for (Operation op : mList) {
+                remoteContext.incrementOpCount();
+                op.apply(context.getContext());
+            }
+        }
+    }
+
+    /**
+     * The name of the class
+     *
+     * @return the name
+     */
+    @NonNull
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    /**
+     * Write the operation on the buffer
+     *
+     * @param type type of operation
+     * @param a first value
+     * @param b second value
+     * @param buffer the buffer to write to
+     */
+    public static void apply(@NonNull WireBuffer buffer, byte type, float a, float b) {
+        buffer.start(OP_CODE);
+        buffer.writeByte(type);
+        buffer.writeFloat(a);
+        buffer.writeFloat(b);
+    }
+
+    /**
+     * 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) {
+        byte type = (byte) buffer.readByte();
+        float a = buffer.readFloat();
+        float b = buffer.readFloat();
+        operations.add(new ConditionalOperations(type, a, b));
+    }
+
+    /**
+     * 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", OP_CODE, name())
+                .description("Run if the condition is true")
+                .field(DocumentedOperation.BYTE, "type", "type of comparison")
+                .field(DocumentedOperation.FLOAT, "a", "first value")
+                .field(DocumentedOperation.FLOAT, "b", "second value");
+    }
+
+    /**
+     * Calculate and estimate of the number of iterations
+     *
+     * @return number of loops or 10 if based on variables
+     */
+    public int estimateIterations() {
+        return 1; // this is a generic estmate if the values are variables;
+    }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        serializer
+                .addType(CLASS_NAME)
+                .add("type", mType)
+                .add("varA", mVarA, mVarAOut)
+                .add("VarB", mVarB, mVarBOut)
+                .add("list", mList);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawContent.java
index 4d2a939..326f060 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawContent.java
@@ -46,7 +46,7 @@
      *
      * @param component
      */
-    public void setComponent(LayoutComponent component) {
+    public void setComponent(@Nullable LayoutComponent component) {
         mComponent = component;
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
index 31d9b6a..5853578 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
@@ -110,7 +110,7 @@
                         "The x-radius of the oval used to round the corners")
                 .field(
                         DocumentedOperation.FLOAT,
-                        "sweepAngle",
+                        "ry",
                         "The y-radius of the oval used to round the corners");
     }
 
@@ -126,7 +126,6 @@
 
     @Override
     public void serialize(MapSerializer serializer) {
-        serialize(serializer, "left", "top", "right", "bottom", "rx", "sweepAngle")
-                .addType(CLASS_NAME);
+        serialize(serializer, "left", "top", "right", "bottom", "rx", "ry").addType(CLASS_NAME);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
index ee1689c..cec01c4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
@@ -211,6 +211,7 @@
     public void serialize(MapSerializer serializer) {
         serializer
                 .addType(CLASS_NAME)
+                .add("textId", mTextID)
                 .add("start", mStart)
                 .add("end", mEnd)
                 .add("contextStart", mContextStart)
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
index 233e246..5096aaf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
@@ -42,6 +42,15 @@
         this.mValue = value;
     }
 
+    /**
+     * Copy the value from another operation
+     *
+     * @param from value to copy from
+     */
+    public void update(FloatConstant from) {
+        mValue = from.mValue;
+    }
+
     @Override
     public void write(@NonNull WireBuffer buffer) {
         apply(buffer, mId, mValue);
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 eba201b..0901ae3 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
@@ -149,7 +149,6 @@
 
     @Override
     public void apply(@NonNull RemoteContext context) {
-        updateVariables(context);
         float t = context.getAnimationTime();
         if (Float.isNaN(mLastChange)) {
             mLastChange = t;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/HapticFeedback.java b/core/java/com/android/internal/widget/remotecompose/core/operations/HapticFeedback.java
new file mode 100644
index 0000000..1b7f5e8
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/HapticFeedback.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import 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.SerializableToString;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+
+import java.util.List;
+
+/** Generate HapticFeedback */
+public class HapticFeedback extends Operation implements SerializableToString, Serializable {
+    private static final int OP_CODE = Operations.HAPTIC_FEEDBACK;
+    private static final String CLASS_NAME = "HapticFeedback";
+    private int mHapticFeedbackType;
+
+    public HapticFeedback(int hapticFeedbackType) {
+        this.mHapticFeedbackType = hapticFeedbackType;
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, mHapticFeedbackType);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return CLASS_NAME + "(" + mHapticFeedbackType + ")";
+    }
+
+    /**
+     * The name of the class
+     *
+     * @return the name
+     */
+    @NonNull
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    /**
+     * The OP_CODE for this command
+     *
+     * @return the opcode
+     */
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * add a text data operation
+     *
+     * @param buffer buffer to add to
+     * @param hapticFeedbackType the vibration effect
+     */
+    public static void apply(@NonNull WireBuffer buffer, int hapticFeedbackType) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(hapticFeedbackType);
+    }
+
+    /**
+     * 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) {
+        int hapticFeedbackType = buffer.readInt();
+
+        operations.add(new HapticFeedback(hapticFeedbackType));
+    }
+
+    /**
+     * 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("Data Operations", OP_CODE, CLASS_NAME)
+                .description("Generate an haptic feedback")
+                .field(DocumentedOperation.INT, "HapticFeedbackType", "Type of haptic feedback");
+    }
+
+    @Override
+    public void apply(@NonNull RemoteContext context) {
+        context.hapticEffect(mHapticFeedbackType);
+    }
+
+    @NonNull
+    @Override
+    public String deepToString(@NonNull String indent) {
+        return indent + toString();
+    }
+
+    @Override
+    public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+        serializer.append(indent, getSerializedName() + "<" + mHapticFeedbackType + ">");
+    }
+
+    @NonNull
+    private String getSerializedName() {
+        return "HAPTIC_FEEDBACK";
+    }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        serializer.addType(CLASS_NAME).add("hapticFeedbackType", mHapticFeedbackType);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
index ee9e7a4..e86eabf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ParticlesCreate.java
@@ -23,6 +23,8 @@
 
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
@@ -30,6 +32,7 @@
 import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
 import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
 import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 
 import java.util.Arrays;
 import java.util.List;
@@ -38,7 +41,7 @@
  * This creates a particle system. which consist of id, particleCount, array of id's and equations
  * for constructing the particles
  */
-public class ParticlesCreate extends Operation implements VariableSupport {
+public class ParticlesCreate extends PaintOperation implements VariableSupport {
     private static final int OP_CODE = Operations.PARTICLE_DEFINE;
     private static final String CLASS_NAME = "ParticlesCreate";
     private final int mId;
@@ -214,6 +217,13 @@
         return indent + toString();
     }
 
+    @Override
+    public void paint(@NonNull PaintContext context) {
+        for (int i = 0; i < mParticles.length; i++) {
+            initializeParticle(i);
+        }
+    }
+
     void initializeParticle(int pNo) {
         for (int j = 0; j < mParticles[pNo].length; j++) {
             for (int k = 0; k < mIndexeVars.length; k++) {
@@ -226,13 +236,6 @@
         }
     }
 
-    @Override
-    public void apply(@NonNull RemoteContext context) {
-        for (int i = 0; i < mParticles.length; i++) {
-            initializeParticle(i);
-        }
-    }
-
     public float[][] getParticles() {
         return mParticles;
     }
@@ -244,4 +247,7 @@
     public float[][] getEquations() {
         return mOutEquations;
     }
+
+    @Override
+    public void serialize(MapSerializer serializer) {}
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
index 8a747e1..31d21c4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
@@ -258,6 +258,6 @@
 
     @Override
     public void serialize(MapSerializer serializer) {
-        serializer.addType(CLASS_NAME).add("id", mInstanceId).add("path", pathString(mFloatPath));
+        serializer.addType(CLASS_NAME).add("id", mInstanceId).addPath("path", mFloatPath);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCombine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCombine.java
new file mode 100644
index 0000000..5f761d1
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCombine.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import 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.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+
+import java.util.List;
+
+/** Operation to perform Constructive area geometry operations, combining two Paths */
+public class PathCombine extends PaintOperation implements VariableSupport, Serializable {
+    private static final int OP_CODE = Operations.PATH_COMBINE;
+    private static final String CLASS_NAME = "PathCombine";
+    public int mOutId;
+    public int mPathId1;
+    public int mPathId2;
+    private byte mOperation;
+
+    /** Subtract the second path from the first path. */
+    public static final byte OP_DIFFERENCE = 0;
+
+    /** Intersect the second path with the first path. */
+    public static final byte OP_INTERSECT = 1;
+
+    /** Subtract the first path from the second path. */
+    public static final byte OP_REVERSE_DIFFERENCE = 2;
+
+    /** Union (inclusive-or) the two paths. */
+    public static final byte OP_UNION = 3;
+
+    /** Exclusive-or the two paths. */
+    public static final byte OP_XOR = 4;
+
+    public PathCombine(int outId, int pathId1, int pathId2, byte operation) {
+        this.mOutId = outId;
+        this.mPathId1 = pathId1;
+        this.mPathId2 = pathId2;
+        this.mOperation = operation;
+    }
+
+    @Override
+    public void updateVariables(@NonNull RemoteContext context) {}
+
+    @Override
+    public void registerListening(@NonNull RemoteContext context) {}
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, mOutId, mPathId1, mPathId2, mOperation);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return CLASS_NAME
+                + "["
+                + mOutId
+                + "] = ["
+                + mPathId1
+                + " ] + [ "
+                + mPathId2
+                + "], "
+                + mOperation;
+    }
+
+    /**
+     * The name of the class
+     *
+     * @return the name
+     */
+    @NonNull
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    /**
+     * The OP_CODE for this command
+     *
+     * @return the opcode
+     */
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * Writes out the operation to the buffer
+     *
+     * @param buffer buffer to write to
+     * @param outId id of the path
+     * @param pathId1 source path 1
+     * @param pathId2 source path 2
+     * @param op the operation to perform
+     */
+    public static void apply(
+            @NonNull WireBuffer buffer, int outId, int pathId1, int pathId2, byte op) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(outId);
+        buffer.writeInt(pathId1);
+        buffer.writeInt(pathId2);
+        buffer.writeByte(op);
+    }
+
+    /**
+     * 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) {
+        int outId1 = buffer.readInt();
+        int pathId1 = buffer.readInt();
+        int pathId2 = buffer.readInt();
+        byte op = (byte) buffer.readByte();
+        operations.add(new PathCombine(outId1, pathId1, pathId2, op));
+    }
+
+    /**
+     * 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("Data Operations", OP_CODE, CLASS_NAME)
+                .description("Merge two string into one")
+                .field(INT, "srcPathId1", "id of the path")
+                .field(INT, "srcPathId1", "x Shift of the path")
+                .field(DocumentedOperation.BYTE, "operation", "the operation");
+    }
+
+    @NonNull
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+
+    @Override
+    public void paint(PaintContext context) {
+        context.combinePath(mOutId, mPathId1, mPathId2, mOperation);
+    }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        serializer
+                .addType(CLASS_NAME)
+                .add("outId", mOutId)
+                .add("pathId1", mPathId1)
+                .add("pathId2", mPathId2)
+                .add("operation", mOperation);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
index 78e3b9e..7a28992 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
@@ -242,6 +242,6 @@
 
     @Override
     public void serialize(MapSerializer serializer) {
-        serializer.addType(CLASS_NAME).add("id", mInstanceId).add("path", pathString(mFloatPath));
+        serializer.addType(CLASS_NAME).add("id", mInstanceId).addPath("path", mFloatPath);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
index cedc4f3b..8b01722 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
@@ -243,6 +243,6 @@
 
     @Override
     public void serialize(MapSerializer serializer) {
-        serializer.addType(CLASS_NAME).add("id", mInstanceId).add("path", pathString(mFloatPath));
+        serializer.addType(CLASS_NAME).add("id", mInstanceId).addPath("path", mFloatPath);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index 67773d1..d8ef4cb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -37,7 +37,7 @@
     private static final int OP_CODE = Operations.DATA_TEXT;
     private static final String CLASS_NAME = "TextData";
     public final int mTextId;
-    @NonNull public final String mText;
+    @NonNull public String mText;
     public static final int MAX_STRING_SIZE = 4000;
 
     public TextData(int textId, @NonNull String text) {
@@ -45,6 +45,15 @@
         this.mText = text;
     }
 
+    /**
+     * Copy the data from another text data
+     *
+     * @param from source to copy from
+     */
+    public void update(TextData from) {
+        mText = from.mText;
+    }
+
     @Override
     public void write(@NonNull WireBuffer buffer) {
         apply(buffer, mTextId, mText);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
index 1239b56..2bc77cc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
@@ -22,6 +22,7 @@
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
 import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
@@ -31,7 +32,7 @@
 import java.util.List;
 
 /** Operation to deal with Text data */
-public class TextMerge extends Operation implements Serializable {
+public class TextMerge extends Operation implements VariableSupport, Serializable {
     private static final int OP_CODE = Operations.TEXT_MERGE;
     private static final String CLASS_NAME = "TextMerge";
     public int mTextId;
@@ -123,10 +124,21 @@
         context.loadText(mTextId, str1 + str2);
     }
 
+    @Override
+    public void updateVariables(@NonNull RemoteContext context) {
+        apply(context);
+    }
+
+    @Override
+    public void registerListening(@NonNull RemoteContext context) {
+        context.listensTo(mSrcId1, this);
+        context.listensTo(mSrcId2, this);
+    }
+
     @NonNull
     @Override
     public String deepToString(@NonNull String indent) {
-        return indent + toString();
+        return indent + this;
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
index fd9a2bf..dee79a4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
@@ -85,6 +85,9 @@
     /** the year */
     public static final short TIME_YEAR = 12;
 
+    /** (value - doc_load_time) * 1E-3 */
+    public static final short TIME_FROM_LOAD_SEC = 14;
+
     /**
      * creates a new operation
      *
@@ -226,6 +229,7 @@
         int val = mType & 255;
         int flags = mType >> 8;
         RemoteContext ctx = context.getContext();
+        long load_time = ctx.getDocLoadTime();
         LongConstant longConstant = (LongConstant) ctx.getObject(mTimeId);
         long value = longConstant.getValue();
         long delta = 0;
@@ -275,7 +279,7 @@
                 ctx.loadFloat(mId, time.getSecond());
                 break;
             case TIME_IN_MIN:
-                ctx.loadFloat(mId, time.getDayOfMonth());
+                ctx.loadFloat(mId, time.getMinute());
                 break;
             case TIME_IN_HR:
                 ctx.loadFloat(mId, time.getHour());
@@ -292,6 +296,9 @@
             case TIME_YEAR:
                 ctx.loadFloat(mId, time.getYear());
                 break;
+            case TIME_FROM_LOAD_SEC:
+                ctx.loadFloat(mId, (value - load_time) * 1E-3f);
+                break;
         }
     }
 
@@ -334,6 +341,8 @@
                 return "TIME_DAY_OF_WEEK";
             case TIME_YEAR:
                 return "TIME_YEAR";
+            case TIME_FROM_LOAD_SEC:
+                return "TIME_FROM_LOAD_SEC";
             default:
                 return "INVALID_TIME_TYPE";
         }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasOperations.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasOperations.java
index 25a10ab..e473d9e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasOperations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasOperations.java
@@ -27,7 +27,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.ComponentValue;
-import com.android.internal.widget.remotecompose.core.operations.DrawContent;
 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 import com.android.internal.widget.remotecompose.core.serialize.Serializable;
 
@@ -164,12 +163,10 @@
      *
      * @param layoutComponent
      */
-    public void setComponent(LayoutComponent layoutComponent) {
+    public void setComponent(@Nullable LayoutComponent layoutComponent) {
         mComponent = layoutComponent;
-        for (Operation op : mList) {
-            if (op instanceof DrawContent) {
-                ((DrawContent) op).setComponent(layoutComponent);
-            }
+        if (layoutComponent != null) {
+            layoutComponent.setCanvasOperations(this);
         }
     }
 }
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 b30dade..425c61b 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
@@ -27,6 +27,7 @@
 import com.android.internal.widget.remotecompose.core.TouchListener;
 import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.BitmapData;
 import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
 import com.android.internal.widget.remotecompose.core.operations.TextData;
 import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
@@ -174,20 +175,25 @@
                             + mComponentId);
         }
         for (ComponentValue v : mComponentValues) {
-            switch (v.getType()) {
-                case ComponentValue.WIDTH:
-                    context.loadFloat(v.getValueId(), mWidth);
-                    if (DEBUG) {
-                        System.out.println("Updating WIDTH for " + mComponentId + " to " + mWidth);
-                    }
-                    break;
-                case ComponentValue.HEIGHT:
-                    context.loadFloat(v.getValueId(), mHeight);
-                    if (DEBUG) {
-                        System.out.println(
-                                "Updating HEIGHT for " + mComponentId + " to " + mHeight);
-                    }
-                    break;
+            if (context.getMode() == RemoteContext.ContextMode.DATA) {
+                context.loadFloat(v.getValueId(), 1f);
+            } else {
+                switch (v.getType()) {
+                    case ComponentValue.WIDTH:
+                        context.loadFloat(v.getValueId(), mWidth);
+                        if (DEBUG) {
+                            System.out.println(
+                                    "Updating WIDTH for " + mComponentId + " to " + mWidth);
+                        }
+                        break;
+                    case ComponentValue.HEIGHT:
+                        context.loadFloat(v.getValueId(), mHeight);
+                        if (DEBUG) {
+                            System.out.println(
+                                    "Updating HEIGHT for " + mComponentId + " to " + mHeight);
+                        }
+                        break;
+                }
             }
         }
     }
@@ -556,6 +562,7 @@
         ComponentMeasure m = measure.get(this);
         if (!mFirstLayout
                 && context.isAnimationEnabled()
+                && mAnimationSpec.isAnimationEnabled()
                 && !(this instanceof LayoutComponentContent)) {
             if (mAnimateMeasure == null) {
                 ComponentMeasure origin =
@@ -822,15 +829,27 @@
      *
      * @param value a 2 dimension float array that will receive the horizontal and vertical position
      *     of the component.
+     * @param forSelf whether the location is for this container or a child, relevant for scrollable
+     *     items.
      */
-    public void getLocationInWindow(@NonNull float[] value) {
+    public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
         value[0] += mX;
         value[1] += mY;
         if (mParent != null) {
-            mParent.getLocationInWindow(value);
+            mParent.getLocationInWindow(value, false);
         }
     }
 
+    /**
+     * Returns the location of the component relative to the root component
+     *
+     * @param value a 2 dimension float array that will receive the horizontal and vertical position
+     *     of the component.
+     */
+    public void getLocationInWindow(@NonNull float[] value) {
+        getLocationInWindow(value, true);
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -1129,26 +1148,15 @@
         }
     }
 
-    /** Extract CanvasOperations if present */
-    public @Nullable CanvasOperations getCanvasOperations(LayoutComponent layoutComponent) {
-        for (Operation op : mList) {
-            if (op instanceof CanvasOperations) {
-                ((CanvasOperations) op).setComponent(layoutComponent);
-                return (CanvasOperations) op;
-            }
-        }
-        return null;
-    }
-
     /**
-     * Extract child TextData elements
+     * Extract child Data elements
      *
-     * @param data an ArrayList that will be populated with the TextData elements (if any)
+     * @param data an ArrayList that will be populated with the Data elements (if any)
      */
-    public void getData(@NonNull ArrayList<TextData> data) {
+    public void getData(@NonNull ArrayList<Operation> data) {
         for (Operation op : mList) {
-            if (op instanceof TextData) {
-                data.add((TextData) op);
+            if (op instanceof TextData || op instanceof BitmapData) {
+                data.add(op);
             }
         }
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseOperation.java
index 0e629c5..3e1cf35 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ImpulseOperation.java
@@ -117,7 +117,7 @@
     @NonNull
     @Override
     public String toString() {
-        StringBuilder builder = new StringBuilder("LoopOperation\n");
+        StringBuilder builder = new StringBuilder("ImpulseOperation\n");
         for (Operation operation : mList) {
             builder.append("  startAt: ");
             builder.append(mStartAt);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index 7e2a4cc..bc099e3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -30,7 +30,6 @@
 import com.android.internal.widget.remotecompose.core.operations.MatrixSave;
 import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
 import com.android.internal.widget.remotecompose.core.operations.PaintData;
-import com.android.internal.widget.remotecompose.core.operations.TextData;
 import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
 import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
@@ -127,9 +126,18 @@
     // Should be removed after ImageLayout is in
     private static final boolean USE_IMAGE_TEMP_FIX = true;
 
+    /**
+     * Set canvas operations op on this component
+     *
+     * @param operations
+     */
+    public void setCanvasOperations(@Nullable CanvasOperations operations) {
+        mDrawContentOperations = operations;
+    }
+
     @Override
     public void inflate() {
-        ArrayList<TextData> data = new ArrayList<>();
+        ArrayList<Operation> data = new ArrayList<>();
         ArrayList<Operation> supportedOperations = new ArrayList<>();
 
         for (Operation op : mList) {
@@ -139,7 +147,6 @@
                 mChildrenComponents.clear();
                 LayoutComponentContent content = (LayoutComponentContent) op;
                 content.getComponents(mChildrenComponents);
-                mDrawContentOperations = content.getCanvasOperations(this);
                 if (USE_IMAGE_TEMP_FIX) {
                     if (mChildrenComponents.isEmpty() && !mContent.mList.isEmpty()) {
                         CanvasContent canvasContent =
@@ -178,8 +185,6 @@
                     ((ScrollModifierOperation) op).inflate(this);
                 }
                 mComponentModifiers.add((ModifierOperation) op);
-            } else if (op instanceof TextData) {
-                data.add((TextData) op);
             } else if (op instanceof TouchExpression
                     || (op instanceof PaintData)
                     || (op instanceof FloatExpression)) {
@@ -284,11 +289,11 @@
     }
 
     @Override
-    public void getLocationInWindow(@NonNull float[] value) {
+    public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
         value[0] += mX + mPaddingLeft;
         value[1] += mY + mPaddingTop;
         if (mParent != null) {
-            mParent.getLocationInWindow(value);
+            mParent.getLocationInWindow(value, false);
         }
     }
 
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 dda328f..e5914eb7 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
@@ -211,7 +211,6 @@
                 .add("until", mUntil, mUntilOut)
                 .add("from", mFrom, mFromOut)
                 .add("step", mStep, mStepOut)
-                .add("untilOut", mUntilOut)
                 .add("list", mList);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
index c87bbdc..4f552e7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
@@ -35,6 +35,7 @@
 /** Basic component animation spec */
 public class AnimationSpec extends Operation implements ModifierOperation {
     public static final AnimationSpec DEFAULT = new AnimationSpec();
+    public static final AnimationSpec DISABLED = new AnimationSpec(0);
     int mAnimationId = -1;
     float mMotionDuration = 300;
     int mMotionEasingType = GeneralEasing.CUBIC_STANDARD;
@@ -71,6 +72,15 @@
                 ANIMATION.FADE_OUT);
     }
 
+    public AnimationSpec(int value) {
+        this();
+        mAnimationId = value;
+    }
+
+    public boolean isAnimationEnabled() {
+        return mAnimationId != 0;
+    }
+
     public int getAnimationId() {
         return mAnimationId;
     }
@@ -130,6 +140,7 @@
     public void serialize(MapSerializer serializer) {
         serializer
                 .addType("AnimationSpec")
+                .add("animationId", mAnimationId)
                 .add("motionDuration", getMotionDuration())
                 .add("motionEasingType", Easing.getString(getMotionEasingType()))
                 .add("visibilityDuration", getVisibilityDuration())
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
index f9111df..8e1c872 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
@@ -155,23 +155,5 @@
     public void serialize(MapSerializer serializer) {
         super.serialize(serializer);
         serializer.addType(getSerializedName());
-        serializer.add("horizontalPositioning", mHorizontalPositioning);
-    }
-
-    private String getPositioningString(int pos) {
-        switch (pos) {
-            case START:
-                return "START";
-            case CENTER:
-                return "CENTER";
-            case END:
-                return "END";
-            case TOP:
-                return "TOP";
-            case BOTTOM:
-                return "BOTTOM";
-            default:
-                return "NONE";
-        }
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
index b008952..00ec605 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
@@ -24,10 +24,13 @@
 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.LayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class CollapsibleColumnLayout extends ColumnLayout {
@@ -153,7 +156,7 @@
     }
 
     @Override
-    protected boolean hasVerticalIntrinsicDimension() {
+    public boolean hasVerticalIntrinsicDimension() {
         return true;
     }
 
@@ -166,25 +169,72 @@
             boolean verticalWrap,
             @NonNull MeasurePass measure,
             @NonNull Size size) {
+        computeVisibleChildren(
+                context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
+    }
+
+    @Override
+    public void computeSize(
+            @NonNull PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            @NonNull MeasurePass measure) {
+        computeVisibleChildren(context, maxWidth, maxHeight, false, false, measure, null);
+    }
+
+    @Override
+    public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
+        // if needed, take care of weight calculations
+        super.internalLayoutMeasure(context, measure);
+        // Check again for visibility
+        ComponentMeasure m = measure.get(this);
+        computeVisibleChildren(context, m.getW(), m.getH(), false, false, measure, null);
+    }
+
+    private void computeVisibleChildren(
+            @NonNull PaintContext context,
+            float maxWidth,
+            float maxHeight,
+            boolean horizontalWrap,
+            boolean verticalWrap,
+            @NonNull MeasurePass measure,
+            @Nullable Size size) {
         int visibleChildren = 0;
         ComponentMeasure self = measure.get(this);
         self.addVisibilityOverride(Visibility.OVERRIDE_VISIBLE);
         float currentMaxHeight = maxHeight;
+        boolean hasPriorities = false;
         for (Component c : mChildrenComponents) {
-            if (c instanceof CollapsibleColumnLayout) {
-                c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
-            } else {
-                c.measure(context, 0f, maxWidth, 0f, Float.MAX_VALUE, measure);
+            if (!measure.contains(c.getComponentId())) {
+                // No need to remeasure here if already done
+                if (c instanceof CollapsibleColumnLayout) {
+                    c.measure(context, 0f, maxWidth, 0f, currentMaxHeight, measure);
+                } else {
+                    c.measure(context, 0f, maxWidth, 0f, Float.MAX_VALUE, measure);
+                }
             }
+
             ComponentMeasure m = measure.get(c);
             if (!m.isGone()) {
-                size.setWidth(Math.max(size.getWidth(), m.getW()));
-                size.setHeight(size.getHeight() + m.getH());
+                if (size != null) {
+                    size.setWidth(Math.max(size.getWidth(), m.getW()));
+                    size.setHeight(size.getHeight() + m.getH());
+                }
                 visibleChildren++;
                 currentMaxHeight -= m.getH();
             }
+            if (c instanceof LayoutComponent) {
+                LayoutComponent lc = (LayoutComponent) c;
+                CollapsiblePriorityModifierOperation priority =
+                        lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+                if (priority != null) {
+                    hasPriorities = true;
+                }
+            }
         }
-        if (!mChildrenComponents.isEmpty()) {
+        if (!mChildrenComponents.isEmpty() && size != null) {
             size.setHeight(size.getHeight() + (mSpacedBy * (visibleChildren - 1)));
         }
 
@@ -192,7 +242,14 @@
         float childrenHeight = 0f;
 
         boolean overflow = false;
-        for (Component child : mChildrenComponents) {
+        ArrayList<Component> children = mChildrenComponents;
+        if (hasPriorities) {
+            // TODO: We need to cache this.
+            children =
+                    CollapsiblePriority.sortWithPriorities(
+                            mChildrenComponents, CollapsiblePriority.VERTICAL);
+        }
+        for (Component child : children) {
             ComponentMeasure childMeasure = measure.get(child);
             if (overflow || childMeasure.isGone()) {
                 childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
@@ -209,10 +266,10 @@
                 visibleChildren++;
             }
         }
-        if (verticalWrap) {
+        if (verticalWrap && size != null) {
             size.setHeight(Math.min(maxHeight, childrenHeight));
         }
-        if (visibleChildren == 0 || size.getHeight() <= 0f) {
+        if (visibleChildren == 0 || (size != null && size.getHeight() <= 0f)) {
             self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
         }
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java
new file mode 100644
index 0000000..46cd45e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsiblePriority.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
+
+import java.util.ArrayList;
+
+/** Utility class to manage collapsible priorities on components */
+public class CollapsiblePriority {
+
+    public static final int HORIZONTAL = 0;
+    public static final int VERTICAL = 1;
+
+    /**
+     * Returns the priority of a child component
+     *
+     * @param c the child component
+     * @return priority value, or 0f if not found
+     */
+    static float getPriority(Component c, int orientation) {
+        if (c instanceof LayoutComponent) {
+            LayoutComponent lc = (LayoutComponent) c;
+            CollapsiblePriorityModifierOperation priority =
+                    lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+            if (priority != null && priority.getOrientation() == orientation) {
+                return priority.getPriority();
+            }
+        }
+        return Float.MAX_VALUE;
+    }
+
+    /**
+     * Allocate and return a sorted array of components by their priorities
+     *
+     * @param components the children components
+     * @return list of components sorted by their priority in decreasing order
+     */
+    static ArrayList<Component> sortWithPriorities(
+            ArrayList<Component> components, int orientation) {
+        ArrayList<Component> sorted = new ArrayList<>(components);
+        sorted.sort(
+                (t1, t2) -> {
+                    float p1 = getPriority(t1, orientation);
+                    float p2 = getPriority(t2, orientation);
+                    return (int) (p2 - p1);
+                });
+        return sorted;
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
index 05f3329..e3632f9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
@@ -24,10 +24,13 @@
 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.LayoutComponent;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.CollapsiblePriorityModifierOperation;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class CollapsibleRowLayout extends RowLayout {
@@ -135,8 +138,12 @@
     }
 
     @Override
-    protected boolean hasHorizontalIntrinsicDimension() {
-        return true;
+    public float minIntrinsicHeight(@NonNull RemoteContext context) {
+        float height = computeModifierDefinedHeight(context);
+        if (!mChildrenComponents.isEmpty()) {
+            height += mChildrenComponents.get(0).minIntrinsicHeight(context);
+        }
+        return height;
     }
 
     @Override
@@ -149,12 +156,8 @@
     }
 
     @Override
-    public float minIntrinsicHeight(@NonNull RemoteContext context) {
-        float height = computeModifierDefinedHeight(context);
-        if (!mChildrenComponents.isEmpty()) {
-            height += mChildrenComponents.get(0).minIntrinsicHeight(context);
-        }
-        return height;
+    public boolean hasHorizontalIntrinsicDimension() {
+        return true;
     }
 
     @Override
@@ -166,45 +169,107 @@
             boolean verticalWrap,
             @NonNull MeasurePass measure,
             @NonNull Size size) {
-        super.computeWrapSize(
-                context, Float.MAX_VALUE, maxHeight, horizontalWrap, verticalWrap, measure, size);
+        computeVisibleChildren(
+                context, maxWidth, maxHeight, horizontalWrap, verticalWrap, measure, size);
     }
 
     @Override
-    public boolean applyVisibility(
-            float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
-        float childrenWidth = 0f;
-        float childrenHeight = 0f;
-        boolean changedVisibility = false;
+    public void computeSize(
+            @NonNull PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            @NonNull MeasurePass measure) {
+        computeVisibleChildren(context, maxWidth, maxHeight, false, false, measure, null);
+    }
+
+    @Override
+    public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
+        // if needed, take care of weight calculations
+        super.internalLayoutMeasure(context, measure);
+        // Check again for visibility
+        ComponentMeasure m = measure.get(this);
+        computeVisibleChildren(context, m.getW(), m.getH(), false, false, measure, null);
+    }
+
+    private void computeVisibleChildren(
+            @NonNull PaintContext context,
+            float maxWidth,
+            float maxHeight,
+            boolean horizontalWrap,
+            boolean verticalWrap,
+            @NonNull MeasurePass measure,
+            @Nullable Size size) {
         int visibleChildren = 0;
         ComponentMeasure self = measure.get(this);
-        self.clearVisibilityOverride();
-        if (selfWidth <= 0 || selfHeight <= 0) {
-            self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
-            return true;
+        self.addVisibilityOverride(Visibility.OVERRIDE_VISIBLE);
+        float currentMaxWidth = maxWidth;
+        boolean hasPriorities = false;
+        for (Component c : mChildrenComponents) {
+            if (!measure.contains(c.getComponentId())) {
+                // No need to remeasure here if already done
+                if (c instanceof CollapsibleRowLayout) {
+                    c.measure(context, 0f, currentMaxWidth, 0f, maxHeight, measure);
+                } else {
+                    c.measure(context, 0f, Float.MAX_VALUE, 0f, maxHeight, measure);
+                }
+            }
+            ComponentMeasure m = measure.get(c);
+            if (!m.isGone()) {
+                if (size != null) {
+                    size.setHeight(Math.max(size.getHeight(), m.getH()));
+                    size.setWidth(size.getWidth() + m.getW());
+                }
+                visibleChildren++;
+                currentMaxWidth -= m.getW();
+            }
+            if (c instanceof LayoutComponent) {
+                LayoutComponent lc = (LayoutComponent) c;
+                CollapsiblePriorityModifierOperation priority =
+                        lc.selfOrModifier(CollapsiblePriorityModifierOperation.class);
+                if (priority != null) {
+                    hasPriorities = true;
+                }
+            }
         }
-        for (Component child : mChildrenComponents) {
+        if (!mChildrenComponents.isEmpty() && size != null) {
+            size.setWidth(size.getWidth() + (mSpacedBy * (visibleChildren - 1)));
+        }
+
+        float childrenWidth = 0f;
+        float childrenHeight = 0f;
+
+        boolean overflow = false;
+        ArrayList<Component> children = mChildrenComponents;
+        if (hasPriorities) {
+            // TODO: We need to cache this.
+            children =
+                    CollapsiblePriority.sortWithPriorities(
+                            mChildrenComponents, CollapsiblePriority.HORIZONTAL);
+        }
+        for (Component child : children) {
             ComponentMeasure childMeasure = measure.get(child);
-            int visibility = childMeasure.getVisibility();
-            childMeasure.clearVisibilityOverride();
-            if (!childMeasure.isVisible()) {
+            if (overflow || childMeasure.isGone()) {
+                childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
                 continue;
             }
-            if (childrenWidth + childMeasure.getW() > selfWidth
-                    && childrenHeight + childMeasure.getH() > selfHeight) {
+            float childWidth = childMeasure.getW();
+            boolean childDoesNotFits = childrenWidth + childWidth > maxWidth;
+            if (childDoesNotFits) {
                 childMeasure.addVisibilityOverride(Visibility.OVERRIDE_GONE);
-                if (visibility != childMeasure.getVisibility()) {
-                    changedVisibility = true;
-                }
+                overflow = true;
             } else {
-                childrenWidth += childMeasure.getW();
+                childrenWidth += childWidth;
                 childrenHeight = Math.max(childrenHeight, childMeasure.getH());
                 visibleChildren++;
             }
         }
-        if (visibleChildren == 0) {
+        if (horizontalWrap && size != null) {
+            size.setWidth(Math.min(maxWidth, childrenWidth));
+        }
+        if (visibleChildren == 0 || (size != null && size.getWidth() <= 0f)) {
             self.addVisibilityOverride(Visibility.OVERRIDE_GONE);
         }
-        return changedVisibility;
     }
 }
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 cda90c2..9566242 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
@@ -33,6 +33,7 @@
 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.layout.modifiers.HeightInModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
 
@@ -372,6 +373,17 @@
         DebugLog.e();
     }
 
+    @Override
+    public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
+        super.getLocationInWindow(value, forSelf);
+
+        if (!forSelf && mVerticalScrollDelegate instanceof ScrollModifierOperation) {
+            ScrollModifierOperation smo = (ScrollModifierOperation) mVerticalScrollDelegate;
+
+            value[1] += smo.getScrollY();
+        }
+    }
+
     /**
      * The name of the class
      *
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ImageLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ImageLayout.java
new file mode 100644
index 0000000..a4ed0c1
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ImageLayout.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.BitmapData;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
+import com.android.internal.widget.remotecompose.core.operations.utilities.ImageScaling;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+public class ImageLayout extends LayoutManager implements VariableSupport {
+
+    private static final boolean DEBUG = false;
+    private int mBitmapId = -1;
+    private int mScaleType;
+    private float mAlpha = 1f;
+
+    @NonNull ImageScaling mScaling = new ImageScaling();
+    @NonNull PaintBundle mPaint = new PaintBundle();
+
+    @Override
+    public void registerListening(@NonNull RemoteContext context) {
+        if (mBitmapId != -1) {
+            context.listensTo(mBitmapId, this);
+        }
+    }
+
+    public ImageLayout(
+            @Nullable Component parent,
+            int componentId,
+            int animationId,
+            int bitmapId,
+            float x,
+            float y,
+            float width,
+            float height,
+            int scaleType,
+            float alpha) {
+        super(parent, componentId, animationId, x, y, width, height);
+        mBitmapId = bitmapId;
+        mScaleType = scaleType & 0xFF;
+        mAlpha = alpha;
+    }
+
+    public ImageLayout(
+            @Nullable Component parent,
+            int componentId,
+            int animationId,
+            int bitmapId,
+            int scaleType,
+            float alpha) {
+        this(parent, componentId, animationId, bitmapId, 0, 0, 0, 0, scaleType, alpha);
+    }
+
+    @Override
+    public void computeWrapSize(
+            @NonNull PaintContext context,
+            float maxWidth,
+            float maxHeight,
+            boolean horizontalWrap,
+            boolean verticalWrap,
+            @NonNull MeasurePass measure,
+            @NonNull Size size) {
+
+        BitmapData bitmapData = (BitmapData) context.getContext().getObject(mBitmapId);
+        if (bitmapData != null) {
+            size.setWidth(bitmapData.getWidth());
+            size.setHeight(bitmapData.getHeight());
+        }
+    }
+
+    @Override
+    public void computeSize(
+            @NonNull PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            @NonNull MeasurePass measure) {
+        float modifiersWidth = computeModifierDefinedWidth(context.getContext());
+        float modifiersHeight = computeModifierDefinedHeight(context.getContext());
+        ComponentMeasure m = measure.get(this);
+        m.setW(modifiersWidth);
+        m.setH(modifiersHeight);
+    }
+
+    @Override
+    public void paintingComponent(@NonNull PaintContext context) {
+        context.save();
+        context.translate(mX, mY);
+        mComponentModifiers.paint(context);
+        float tx = mPaddingLeft;
+        float ty = mPaddingTop;
+        context.translate(tx, ty);
+        float w = mWidth - mPaddingLeft - mPaddingRight;
+        float h = mHeight - mPaddingTop - mPaddingBottom;
+        context.clipRect(0f, 0f, w, h);
+
+        BitmapData bitmapData = (BitmapData) context.getContext().getObject(mBitmapId);
+        if (bitmapData != null) {
+            mScaling.setup(
+                    0f,
+                    0f,
+                    bitmapData.getWidth(),
+                    bitmapData.getHeight(),
+                    0f,
+                    0f,
+                    w,
+                    h,
+                    mScaleType,
+                    1f);
+
+            context.savePaint();
+            if (mAlpha == 1f) {
+                context.drawBitmap(
+                        mBitmapId,
+                        (int) 0f,
+                        (int) 0f,
+                        (int) bitmapData.getWidth(),
+                        (int) bitmapData.getHeight(),
+                        (int) mScaling.mFinalDstLeft,
+                        (int) mScaling.mFinalDstTop,
+                        (int) mScaling.mFinalDstRight,
+                        (int) mScaling.mFinalDstBottom,
+                        -1);
+            } else {
+                context.savePaint();
+                mPaint.reset();
+                mPaint.setColor(0f, 0f, 0f, mAlpha);
+                context.applyPaint(mPaint);
+                context.drawBitmap(
+                        mBitmapId,
+                        (int) 0f,
+                        (int) 0f,
+                        (int) bitmapData.getWidth(),
+                        (int) bitmapData.getHeight(),
+                        (int) mScaling.mFinalDstLeft,
+                        (int) mScaling.mFinalDstTop,
+                        (int) mScaling.mFinalDstRight,
+                        (int) mScaling.mFinalDstBottom,
+                        -1);
+                context.restorePaint();
+            }
+            context.restorePaint();
+        }
+
+        // debugBox(this, context);
+        context.translate(-tx, -ty);
+        context.restore();
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "IMAGE_LAYOUT ["
+                + mComponentId
+                + ":"
+                + mAnimationId
+                + "] ("
+                + mX
+                + ", "
+                + mY
+                + " - "
+                + mWidth
+                + " x "
+                + mHeight
+                + ") "
+                + mVisibility;
+    }
+
+    @NonNull
+    @Override
+    protected String getSerializedName() {
+        return "IMAGE_LAYOUT";
+    }
+
+    @Override
+    public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+        serializer.append(
+                indent,
+                getSerializedName()
+                        + " ["
+                        + mComponentId
+                        + ":"
+                        + mAnimationId
+                        + "] = "
+                        + "["
+                        + mX
+                        + ", "
+                        + mY
+                        + ", "
+                        + mWidth
+                        + ", "
+                        + mHeight
+                        + "] "
+                        + mVisibility
+                        + " ("
+                        + mBitmapId
+                        + "\")");
+    }
+
+    /**
+     * The name of the class
+     *
+     * @return the name
+     */
+    @NonNull
+    public static String name() {
+        return "ImageLayout";
+    }
+
+    /**
+     * The OP_CODE for this command
+     *
+     * @return the opcode
+     */
+    public static int id() {
+        return Operations.LAYOUT_IMAGE;
+    }
+
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer
+     * @param componentId
+     * @param animationId
+     * @param bitmapId
+     * @param scaleType
+     * @param alpha
+     */
+    public static void apply(
+            @NonNull WireBuffer buffer,
+            int componentId,
+            int animationId,
+            int bitmapId,
+            int scaleType,
+            float alpha) {
+        buffer.start(id());
+        buffer.writeInt(componentId);
+        buffer.writeInt(animationId);
+        buffer.writeInt(bitmapId);
+        buffer.writeInt(scaleType);
+        buffer.writeFloat(alpha);
+    }
+
+    /**
+     * 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) {
+        int componentId = buffer.readInt();
+        int animationId = buffer.readInt();
+        int bitmapId = buffer.readInt();
+        int scaleType = buffer.readInt();
+        float alpha = buffer.readFloat();
+        operations.add(new ImageLayout(null, componentId, animationId, bitmapId, scaleType, alpha));
+    }
+
+    /**
+     * 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("Image layout implementation.\n\n")
+                .field(INT, "COMPONENT_ID", "unique id for this component")
+                .field(
+                        INT,
+                        "ANIMATION_ID",
+                        "id used to match components," + " for animation purposes")
+                .field(INT, "BITMAP_ID", "bitmap id")
+                .field(INT, "SCALE_TYPE", "scale type")
+                .field(FLOAT, "ALPHA", "alpha");
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, mComponentId, mAnimationId, mBitmapId, mScaleType, mAlpha);
+    }
+}
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 5b66b95..eb10ead 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
@@ -226,9 +226,17 @@
                         measure,
                         mCachedWrapSize);
                 float w = mCachedWrapSize.getWidth();
-                computeSize(context, 0f, w, 0, measuredHeight, measure);
                 if (hasHorizontalScroll()) {
+                    computeSize(context, 0f, w, 0, measuredHeight, measure);
                     mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w);
+                } else {
+                    computeSize(
+                            context,
+                            0f,
+                            Math.min(measuredWidth, insetMaxWidth),
+                            0,
+                            Math.min(measuredHeight, insetMaxHeight),
+                            measure);
                 }
             } else if (hasVerticalIntrinsicDimension()) {
                 mCachedWrapSize.setWidth(0f);
@@ -236,9 +244,17 @@
                 computeWrapSize(
                         context, maxWidth, Float.MAX_VALUE, false, false, measure, mCachedWrapSize);
                 float h = mCachedWrapSize.getHeight();
-                computeSize(context, 0f, measuredWidth, 0, h, measure);
                 if (hasVerticalScroll()) {
+                    computeSize(context, 0f, measuredWidth, 0, h, measure);
                     mComponentModifiers.setVerticalScrollDimension(measuredHeight, h);
+                } else {
+                    computeSize(
+                            context,
+                            0f,
+                            Math.min(measuredWidth, insetMaxWidth),
+                            0,
+                            Math.min(measuredHeight, insetMaxHeight),
+                            measure);
                 }
             } else {
                 float maxChildWidth = measuredWidth - mPaddingLeft - mPaddingRight;
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 d5d2e03..15b54a3 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
@@ -32,6 +32,7 @@
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
 import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
@@ -386,6 +387,17 @@
         DebugLog.e();
     }
 
+    @Override
+    public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
+        super.getLocationInWindow(value, forSelf);
+
+        if (!forSelf && mHorizontalScrollDelegate instanceof ScrollModifierOperation) {
+            ScrollModifierOperation smo = (ScrollModifierOperation) mHorizontalScrollDelegate;
+
+            value[0] += smo.getScrollX();
+        }
+    }
+
     /**
      * The name of the class
      *
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 2595a71..120c740 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
@@ -31,6 +31,7 @@
 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
 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.measure.ComponentMeasure;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
 import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
@@ -73,7 +74,10 @@
     private float mTextW = -1;
     private float mTextH = -1;
 
+    private final Size mCachedSize = new Size(0f, 0f);
+
     @Nullable private String mCachedString = "";
+    @Nullable private String mNewString;
 
     Platform.ComputedTextLayout mComputedTextLayout;
 
@@ -96,7 +100,7 @@
         if (cachedString != null && cachedString.equalsIgnoreCase(mCachedString)) {
             return;
         }
-        mCachedString = cachedString;
+        mNewString = cachedString;
         if (mType == -1) {
             if (mFontFamilyId != -1) {
                 String fontFamily = context.getText(mFontFamilyId);
@@ -116,8 +120,6 @@
                 mType = 0;
             }
         }
-        mTextW = -1;
-        mTextH = -1;
 
         if (mHorizontalScrollDelegate != null) {
             mHorizontalScrollDelegate.reset();
@@ -230,6 +232,7 @@
                 case TEXT_ALIGN_START:
                 default:
             }
+
             if (mTextW > (mWidth - mPaddingLeft - mPaddingRight)) {
                 context.save();
                 context.clipRect(
@@ -317,6 +320,21 @@
     }
 
     @Override
+    public void computeSize(
+            @NonNull PaintContext context,
+            float minWidth,
+            float maxWidth,
+            float minHeight,
+            float maxHeight,
+            @NonNull MeasurePass measure) {
+        super.computeSize(context, minWidth, maxWidth, minHeight, maxHeight, measure);
+        computeWrapSize(context, maxWidth, maxHeight, true, true, measure, mCachedSize);
+        ComponentMeasure m = measure.get(this);
+        m.setW(mCachedSize.getWidth());
+        m.setH(mCachedSize.getHeight());
+    }
+
+    @Override
     public void computeWrapSize(
             @NonNull PaintContext context,
             float maxWidth,
@@ -332,9 +350,14 @@
         mPaint.setColor(mColor);
         context.replacePaint(mPaint);
         float[] bounds = new float[4];
+        if (mNewString != null && !mNewString.equals(mCachedString)) {
+            mCachedString = mNewString;
+        }
         if (mCachedString == null) {
             return;
         }
+
+        boolean forceComplex = false;
         int flags = PaintContext.TEXT_MEASURE_FONT_HEIGHT | PaintContext.TEXT_MEASURE_SPACES;
         if (mMaxLines == 1
                 && (mOverflow == OVERFLOW_START_ELLIPSIS
@@ -342,8 +365,20 @@
                         || mOverflow == OVERFLOW_ELLIPSIS)) {
             flags |= PaintContext.TEXT_COMPLEX;
         }
-        context.getTextBounds(mTextId, 0, mCachedString.length(), flags, bounds);
-        if (bounds[2] - bounds[1] > maxWidth && mMaxLines > 1 && maxWidth > 0f) {
+        if ((flags & PaintContext.TEXT_COMPLEX) != PaintContext.TEXT_COMPLEX) {
+            for (int i = 0; i < mCachedString.length(); i++) {
+                char c = mCachedString.charAt(i);
+                if ((c == '\n') || (c == '\t')) {
+                    flags |= PaintContext.TEXT_COMPLEX;
+                    forceComplex = true;
+                    break;
+                }
+            }
+        }
+        if (!forceComplex) {
+            context.getTextBounds(mTextId, 0, mCachedString.length(), flags, bounds);
+        }
+        if (forceComplex || bounds[2] - bounds[1] > maxWidth && mMaxLines > 1 && maxWidth > 0f) {
             mComputedTextLayout =
                     context.layoutComplexText(
                             mTextId,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
index 656a3c0..b3d76b7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
@@ -250,7 +250,7 @@
         context.savePaint();
         paint.reset();
         paint.setColor(mR, mG, mB, mA);
-        paint.setStrokeWidth(mBorderWidth);
+        paint.setStrokeWidth(mBorderWidth * context.getContext().getDensity());
         paint.setStyle(PaintBundle.STYLE_STROKE);
         context.replacePaint(paint);
         if (mShapeType == ShapeType.RECTANGLE) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java
new file mode 100644
index 0000000..b1f2d2d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/CollapsiblePriorityModifierOperation.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import 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 com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+import com.android.internal.widget.remotecompose.core.serialize.SerializeTags;
+
+import java.util.List;
+
+/** Set an optional priority on a component within a collapsible layout */
+public class CollapsiblePriorityModifierOperation extends Operation
+        implements ModifierOperation, Serializable {
+    private static final int OP_CODE = Operations.MODIFIER_COLLAPSIBLE_PRIORITY;
+    public static final String CLASS_NAME = "CollapsiblePriorityModifierOperation";
+
+    private float mPriority;
+    private int mOrientation;
+
+    public CollapsiblePriorityModifierOperation(int orientation, float priority) {
+        mOrientation = orientation;
+        mPriority = priority;
+    }
+
+    public float getPriority() {
+        return mPriority;
+    }
+
+    public int getOrientation() {
+        return mOrientation;
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {
+        apply(buffer, mOrientation, mPriority);
+    }
+
+    @Override
+    public void apply(@NonNull RemoteContext context) {
+        // nothing
+    }
+
+    @NonNull
+    @Override
+    public String deepToString(@NonNull String indent) {
+        return "";
+    }
+
+    /**
+     * 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) {
+        int orientation = buffer.readInt();
+        float priority = buffer.readFloat();
+        operations.add(new CollapsiblePriorityModifierOperation(orientation, priority));
+    }
+
+    /**
+     * The OP_CODE for this command
+     *
+     * @return the opcode
+     */
+    public static int id() {
+        return OP_CODE;
+    }
+
+    /**
+     * The name of the class
+     *
+     * @return the name
+     */
+    @NonNull
+    public static String name() {
+        return CLASS_NAME;
+    }
+
+    /**
+     * 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", OP_CODE, "CollapsiblePriorityModifier")
+                .description("Add additional priority to children of Collapsible layouts")
+                .field(DocumentedOperation.INT, "orientation", "Horizontal(0) or Vertical (1)")
+                .field(DocumentedOperation.FLOAT, "priority", "The associated priority");
+    }
+
+    /**
+     * Writes out the CollapsiblePriorityModifier to the buffer
+     *
+     * @param buffer buffer to write to
+     * @param priority priority value
+     * @param orientation orientation (HORIZONTAL or VERTICAL)
+     */
+    public static void apply(@NonNull WireBuffer buffer, int orientation, float priority) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(orientation);
+        buffer.writeFloat(priority);
+    }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        serializer
+                .addTags(SerializeTags.MODIFIER)
+                .addType(name())
+                .add("orientation", mOrientation)
+                .add("priority", mPriority);
+    }
+
+    @Override
+    public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+        serializer.append(indent, "PRIORITY = [" + getPriority() + "] (" + mOrientation + ")");
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionMetadataOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionMetadataOperation.java
new file mode 100644
index 0000000..2170efd
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionMetadataOperation.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+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.RemoteContext;
+import com.android.internal.widget.remotecompose.core.SerializableToString;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.layout.ActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
+import com.android.internal.widget.remotecompose.core.serialize.Serializable;
+import com.android.internal.widget.remotecompose.core.serialize.SerializeTags;
+
+import java.util.List;
+
+/** Capture a host action information. This can be triggered on eg. a click. */
+public class HostActionMetadataOperation extends Operation
+        implements ActionOperation, SerializableToString, Serializable {
+    private static final int OP_CODE = Operations.HOST_METADATA_ACTION;
+
+    int mActionId = -1;
+    int mMetadataId = -1;
+
+    public HostActionMetadataOperation(int id, int metadataId) {
+        mActionId = id;
+        mMetadataId = metadataId;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "HostMetadataActionOperation(" + mActionId + ":" + mMetadataId + ")";
+    }
+
+    public int getActionId() {
+        return mActionId;
+    }
+
+    /**
+     * Returns the serialized name for this operation
+     *
+     * @return the serialized name
+     */
+    @NonNull
+    public String serializedName() {
+        return "HOST_METADATA_ACTION";
+    }
+
+    @Override
+    public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+        serializer.append(indent, serializedName() + " = " + mActionId + ", " + mMetadataId);
+    }
+
+    @Override
+    public void apply(@NonNull RemoteContext context) {}
+
+    @NonNull
+    @Override
+    public String deepToString(@NonNull String indent) {
+        return (indent != null ? indent : "") + toString();
+    }
+
+    @Override
+    public void write(@NonNull WireBuffer buffer) {}
+
+    @Override
+    public void runAction(
+            @NonNull RemoteContext context,
+            @NonNull CoreDocument document,
+            @NonNull Component component,
+            float x,
+            float y) {
+        String metadata = context.getText(mMetadataId);
+        if (metadata == null) {
+            metadata = "";
+        }
+        context.runAction(mActionId, metadata);
+    }
+
+    /**
+     * Write the operation to the buffer
+     *
+     * @param buffer a WireBuffer
+     * @param actionId the action id
+     */
+    public static void apply(@NonNull WireBuffer buffer, int actionId, int metadataId) {
+        buffer.start(OP_CODE);
+        buffer.writeInt(actionId);
+        buffer.writeInt(metadataId);
+    }
+
+    /**
+     * 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) {
+        int actionId = buffer.readInt();
+        int metadataId = buffer.readInt();
+        operations.add(new HostActionMetadataOperation(actionId, metadataId));
+    }
+
+    /**
+     * 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", OP_CODE, "HostAction")
+                .description("Host action. This operation represents a host action")
+                .field(INT, "ACTION_ID", "Host Action ID")
+                .field(INT, "METADATA", "Host Action Text Metadata ID");
+    }
+
+    @Override
+    public void serialize(MapSerializer serializer) {
+        serializer
+                .addTags(SerializeTags.MODIFIER)
+                .addType("HostActionOperation")
+                .add("id", mActionId)
+                .add("metadata", mMetadataId);
+    }
+}
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 466e435e..42692f9 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
@@ -401,10 +401,8 @@
                 .add("direction", mDirection)
                 .add("max", mMax)
                 .add("notchMax", mNotchMax)
-                .add("scrollX", mScrollX)
-                .add("scrollY", mScrollY)
-                .add("maxScrollX", mMaxScrollX)
-                .add("maxScrollY", mMaxScrollY)
+                .add("scrollValue", isVerticalScroll() ? mScrollY : mScrollX)
+                .add("maxScrollValue", isVerticalScroll() ? mMaxScrollY : mMaxScrollX)
                 .add("contentDimension", mContentDimension)
                 .add("hostDimension", mHostDimension);
     }
@@ -432,9 +430,35 @@
     }
 
     @Override
-    public boolean showOnScreen(RemoteContext context, int childId) {
-        // TODO correct this when we trust the bounds in parent
-        return scrollByOffset(context, -1000) != 0;
+    public boolean scrollDirection(RemoteContext context, ScrollDirection direction) {
+        float offset = mHostDimension * 0.7f;
+
+        if (direction == ScrollDirection.FORWARD
+                || direction == ScrollDirection.DOWN
+                || direction == ScrollDirection.RIGHT) {
+            offset *= -1;
+        }
+
+        return scrollByOffset(context, (int) offset) != 0;
+    }
+
+    @Override
+    public boolean showOnScreen(RemoteContext context, Component child) {
+        float[] locationInWindow = new float[2];
+        child.getLocationInWindow(locationInWindow);
+
+        int offset = 0;
+        if (handlesVerticalScroll()) {
+            offset = (int) -locationInWindow[1];
+        } else {
+            offset = (int) -locationInWindow[0];
+        }
+
+        if (offset == 0) {
+            return true;
+        } else {
+            return scrollByOffset(context, offset) != 0;
+        }
     }
 
     @Nullable
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
index a95a175..120c7ac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -35,7 +35,10 @@
     @NonNull
     public static String floatToString(
             float value, int beforeDecimalPoint, int afterDecimalPoint, char pre, char post) {
-
+        boolean isNeg = value < 0;
+        if (isNeg) {
+            value = -value;
+        }
         int integerPart = (int) value;
         float fractionalPart = value % 1;
 
@@ -54,14 +57,13 @@
             integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
         }
         if (afterDecimalPoint == 0) {
-            return integerPartString;
+            return ((isNeg) ? "-" : "") + integerPartString;
         }
         // Convert fractional part to string and pad with zeros
 
         for (int i = 0; i < afterDecimalPoint; i++) {
             fractionalPart *= 10;
         }
-
         fractionalPart = Math.round(fractionalPart);
 
         for (int i = 0; i < afterDecimalPoint; i++) {
@@ -87,6 +89,6 @@
             fact = fact + new String(c);
         }
 
-        return integerPartString + "." + fact;
+        return ((isNeg) ? "-" : "") + integerPartString + "." + fact;
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java b/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
index 3d1bd12..1610e63 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/semantics/ScrollableComponent.java
@@ -18,6 +18,7 @@
 import android.annotation.Nullable;
 
 import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
 
 /**
  * Interface for components that support scrolling.
@@ -48,13 +49,23 @@
     }
 
     /**
+     * Scrolls the content in the specified direction.
+     *
+     * @param direction the direction to scroll
+     * @return whether a scroll was possible
+     */
+    default boolean scrollDirection(RemoteContext context, ScrollDirection direction) {
+        return false;
+    }
+
+    /**
      * Show a child with the given ID on the screen, typically scrolling so it's fully on screen.
      *
-     * @param childId The ID of the child to check for visibility.
+     * @param child The child (including nested) to check for visibility.
      * @return {@code true} if the child with the given ID could be shown on screen; {@code false}
      *     otherwise.
      */
-    default boolean showOnScreen(RemoteContext context, int childId) {
+    default boolean showOnScreen(RemoteContext context, Component child) {
         return false;
     }
 
@@ -108,4 +119,13 @@
             return mCanScrollBackwards;
         }
     }
+
+    enum ScrollDirection {
+        FORWARD,
+        BACKWARD,
+        UP,
+        DOWN,
+        LEFT,
+        RIGHT,
+    }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java b/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java
index 20e94ab..32a0ccc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/serialize/MapSerializer.java
@@ -49,6 +49,14 @@
     MapSerializer addIntExpressionSrc(String key, int[] value, int mask);
 
     /**
+     * Add a path
+     *
+     * @param key The key
+     * @param path The path
+     */
+    MapSerializer addPath(String key, float[] path);
+
+    /**
      * Add metadata to this map for filtering by the data format generator.
      *
      * @param value A set of tags to add
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
index bdc7659..25dcb67 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
@@ -34,14 +34,23 @@
 public class IntegerConstant extends Operation implements Serializable {
     private static final String CLASS_NAME = "IntegerConstant";
 
-    private final int mValue;
-    private final int mId;
+    private int mValue;
+    public final int mId;
 
     IntegerConstant(int id, int value) {
         mId = id;
         mValue = value;
     }
 
+    /**
+     * Updates the value of the integer constant
+     *
+     * @param ic the integer constant to copy
+     */
+    public void update(IntegerConstant ic) {
+        mValue = ic.mValue;
+    }
+
     @Override
     public void write(@NonNull WireBuffer buffer) {
         apply(buffer, mId, mValue);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
index d071e0a2..ab0f735 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
@@ -36,7 +36,7 @@
 
     private static final int OP_CODE = Operations.DATA_LONG;
     private long mValue;
-    private final int mId;
+    public final int mId;
 
     /**
      * @param id the id of the constant
@@ -48,6 +48,15 @@
     }
 
     /**
+     * Copy the value from another longConstant
+     *
+     * @param from the constant to copy from
+     */
+    public void update(LongConstant from) {
+        mValue = from.mValue;
+    }
+
+    /**
      * Get the value of the long constant
      *
      * @return the value of the long
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 1f9a274..f5b2cca 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -41,6 +41,7 @@
 import com.android.internal.widget.remotecompose.core.RemoteContextAware;
 import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
 import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
+import com.android.internal.widget.remotecompose.player.platform.AndroidRemoteContext;
 import com.android.internal.widget.remotecompose.player.platform.RemoteComposeCanvas;
 
 /** A view to to display and play RemoteCompose documents */
@@ -114,6 +115,23 @@
         return mInner.getDocument();
     }
 
+    /**
+     * This will update values in the already loaded document.
+     *
+     * @param value the document to update variables in the current document width
+     */
+    public void updateDocument(RemoteComposeDocument value) {
+        RemoteComposeDocument document = value;
+        AndroidRemoteContext tmpContext = new AndroidRemoteContext();
+        document.initializeContext(tmpContext);
+        float density = getContext().getResources().getDisplayMetrics().density;
+        tmpContext.setAnimationEnabled(true);
+        tmpContext.setDensity(density);
+        tmpContext.setUseChoreographer(false);
+        mInner.getDocument().mDocument.applyUpdate(document.mDocument);
+        mInner.invalidate();
+    }
+
     public void setDocument(RemoteComposeDocument value) {
         if (value != null) {
             if (value.canBeDisplayed(
@@ -312,7 +330,8 @@
     }
 
     /**
-     * Add a callback for handling id actions events on the document
+     * Add a callback for handling id actions events on the document. Can only be added after the
+     * document has been loaded.
      *
      * @param callback the callback lambda that will be used when a action is executed
      *     <p>The parameter of the callback are:
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 b5aedd8..680a221 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
@@ -808,6 +808,24 @@
     }
 
     @Override
+    public void combinePath(int out, int path1, int path2, byte operation) {
+        Path p1 = getPath(path1, 0, 1);
+        Path p2 = getPath(path2, 0, 1);
+        Path.Op[] op = {
+            Path.Op.DIFFERENCE,
+            Path.Op.INTERSECT,
+            Path.Op.REVERSE_DIFFERENCE,
+            Path.Op.UNION,
+            Path.Op.XOR,
+        };
+        Path p = new Path(p1);
+        p.op(p2, op[operation]);
+
+        AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
+        androidContext.mRemoteComposeState.putPath(out, p);
+    }
+
+    @Override
     public void reset() {
         mPaint.reset();
     }
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 b31c760..575a6b2 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
@@ -22,7 +22,6 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 
-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;
@@ -43,7 +42,6 @@
  *
  * <p>This is used to play the RemoteCompose operations on Android.
  */
-@VisibleForTesting
 public class AndroidRemoteContext extends RemoteContext {
 
     public void useCanvas(Canvas canvas) {
@@ -197,7 +195,7 @@
 
     @Override
     public void runAction(int id, @NonNull String metadata) {
-        mDocument.performClick(this, id);
+        mDocument.performClick(this, id, metadata);
     }
 
     @Override
@@ -392,6 +390,11 @@
     }
 
     @Override
+    public long getLong(int id) {
+        return ((LongConstant) mRemoteComposeState.getObject(id)).getValue();
+    }
+
+    @Override
     public int getColor(int id) {
         return mRemoteComposeState.getColor(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 29cd40d..17f4fc8 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
@@ -102,6 +102,7 @@
         mDocument = value;
         mDocument.initializeContext(mARContext);
         mDisable = false;
+        mARContext.setDocLoadTime();
         mARContext.setAnimationEnabled(true);
         mARContext.setDensity(mDensity);
         mARContext.setUseChoreographer(true);
@@ -154,7 +155,11 @@
                 param.leftMargin = (int) area.getLeft();
                 param.topMargin = (int) area.getTop();
                 viewArea.setOnClickListener(
-                        view1 -> mDocument.getDocument().performClick(mARContext, area.getId()));
+                        view1 ->
+                                mDocument
+                                        .getDocument()
+                                        .performClick(
+                                                mARContext, area.getId(), area.getMetadata()));
                 addView(viewArea, param);
             }
             if (!clickAreas.isEmpty()) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 748c5b4..bfa0aa9 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -31,6 +31,7 @@
     name: "libandroid_runtime",
     host_supported: true,
     cflags: [
+        "-Wno-cast-function-type-mismatch",
         "-Wno-unused-parameter",
         "-Wno-non-virtual-dtor",
         "-Wno-maybe-uninitialized",
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index 36c08a5..95bab54 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -231,12 +231,6 @@
     }
 }
 
-// This method is deprecated and should be removed when it is no longer needed by the
-// robolectric tests.
-static void nativeClose(JNIEnv* env, jclass clazz, jlong connectionPtr) {
-    nativeClose(env, clazz, connectionPtr, false);
-}
-
 static void sqliteCustomScalarFunctionCallback(sqlite3_context *context,
         int argc, sqlite3_value **argv) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -974,8 +968,6 @@
             (void*)nativeOpen },
     { "nativeClose", "(JZ)V",
       (void*) static_cast<void(*)(JNIEnv*,jclass,jlong,jboolean)>(nativeClose) },
-    { "nativeClose", "(J)V",
-      (void*) static_cast<void(*)(JNIEnv*,jclass,jlong)>(nativeClose) },
     { "nativeRegisterCustomScalarFunction", "(JLjava/lang/String;Ljava/util/function/UnaryOperator;)V",
             (void*)nativeRegisterCustomScalarFunction },
     { "nativeRegisterCustomAggregateFunction", "(JLjava/lang/String;Ljava/util/function/BinaryOperator;)V",
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index dec724b..e1b3479 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -558,8 +558,7 @@
     delete parcel;
 }
 
-static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
-{
+static Parcel* parcel_for_marshall(JNIEnv* env, jlong nativePtr) {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel == NULL) {
        return NULL;
@@ -577,6 +576,16 @@
         return NULL;
     }
 
+    return parcel;
+}
+
+static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
+{
+    Parcel* parcel = parcel_for_marshall(env, nativePtr);
+    if (parcel == NULL) {
+       return NULL;
+    }
+
     jbyteArray ret = env->NewByteArray(parcel->dataSize());
 
     if (ret != NULL)
@@ -592,6 +601,56 @@
     return ret;
 }
 
+static long ensure_capacity(JNIEnv* env, Parcel* parcel, jint remaining) {
+    long dataSize = parcel->dataSize();
+    if (remaining < dataSize) {
+        jnihelp::ThrowException(env, "java/nio/BufferOverflowException", "()V");
+        return -1;
+    }
+    return dataSize;
+}
+
+static int android_os_Parcel_marshall_array(JNIEnv* env, jclass clazz, jlong nativePtr,
+                                            jbyteArray data, jint offset, jint remaining)
+{
+    Parcel* parcel = parcel_for_marshall(env, nativePtr);
+    if (parcel == NULL) {
+       return 0;
+    }
+
+    long data_size = ensure_capacity(env, parcel, remaining);
+    if (data_size < 0) {
+        return 0;
+    }
+
+    jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
+    if (array != NULL)
+    {
+        memcpy(array + offset, parcel->data(), data_size);
+        env->ReleasePrimitiveArrayCritical(data, array, 0);
+    }
+    return data_size;
+}
+
+static int android_os_Parcel_marshall_buffer(JNIEnv* env, jclass clazz, jlong nativePtr,
+                                             jobject javaBuffer, jint offset, jint remaining) {
+    Parcel* parcel = parcel_for_marshall(env, nativePtr);
+    if (parcel == NULL) {
+       return 0;
+    }
+
+    long data_size = ensure_capacity(env, parcel, remaining);
+    if (data_size < 0) {
+        return 0;
+    }
+
+    jbyte* buffer = (jbyte*)env->GetDirectBufferAddress(javaBuffer);
+    if (buffer != NULL) {
+        memcpy(buffer + offset, parcel->data(), data_size);
+    }
+    return data_size;
+}
+
 static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
                                           jbyteArray data, jint offset, jint length)
 {
@@ -613,6 +672,25 @@
     }
 }
 
+static void android_os_Parcel_unmarshall_buffer(JNIEnv* env, jclass clazz, jlong nativePtr,
+                                                jobject javaBuffer, jint offset, jint length)
+{
+    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+    if (parcel == NULL || length < 0) {
+       return;
+    }
+
+    jbyte* buffer = (jbyte*)env->GetDirectBufferAddress(javaBuffer);
+    if (buffer)
+    {
+        parcel->setDataSize(length);
+        parcel->setDataPosition(0);
+
+        void* raw = parcel->writeInplace(length);
+        memcpy(raw, (buffer + offset), length);
+    }
+}
+
 static jint android_os_Parcel_compareData(JNIEnv* env, jclass clazz, jlong thisNativePtr,
                                           jlong otherNativePtr)
 {
@@ -911,7 +989,10 @@
     {"nativeDestroy",             "(J)V", (void*)android_os_Parcel_destroy},
 
     {"nativeMarshall",            "(J)[B", (void*)android_os_Parcel_marshall},
+    {"nativeMarshallArray",       "(J[BII)I", (void*)android_os_Parcel_marshall_array},
+    {"nativeMarshallBuffer",      "(JLjava/nio/ByteBuffer;II)I", (void*)android_os_Parcel_marshall_buffer},
     {"nativeUnmarshall",          "(J[BII)V", (void*)android_os_Parcel_unmarshall},
+    {"nativeUnmarshallBuffer",    "(JLjava/nio/ByteBuffer;II)V", (void*)android_os_Parcel_unmarshall_buffer},
     {"nativeCompareData",         "(JJ)I", (void*)android_os_Parcel_compareData},
     {"nativeCompareDataInRange",  "(JIJII)Z", (void*)android_os_Parcel_compareDataInRange},
     {"nativeAppendFrom",          "(JJII)V", (void*)android_os_Parcel_appendFrom},
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index e874163..a6a748c 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -55,41 +55,25 @@
 
     inline std::shared_ptr<InputChannel> getInputChannel() { return mInputChannel; }
 
-    void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data);
     void dispose(JNIEnv* env, jobject obj);
 
 private:
     std::shared_ptr<InputChannel> mInputChannel;
-    InputChannelObjDisposeCallback mDisposeCallback;
-    void* mDisposeData;
 };
 
 // ----------------------------------------------------------------------------
 
 NativeInputChannel::NativeInputChannel(std::unique_ptr<InputChannel> inputChannel)
-      : mInputChannel(std::move(inputChannel)), mDisposeCallback(nullptr) {}
+      : mInputChannel(std::move(inputChannel)) {}
 
 NativeInputChannel::~NativeInputChannel() {
 }
 
-void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) {
-    if (input_flags::remove_input_channel_from_windowstate()) {
-        return;
-    }
-    mDisposeCallback = callback;
-    mDisposeData = data;
-}
-
 void NativeInputChannel::dispose(JNIEnv* env, jobject obj) {
     if (!mInputChannel) {
         return;
     }
 
-    if (mDisposeCallback) {
-        mDisposeCallback(env, obj, mInputChannel, mDisposeData);
-        mDisposeCallback = nullptr;
-        mDisposeData = nullptr;
-    }
     mInputChannel.reset();
 }
 
@@ -108,17 +92,6 @@
     return nativeInputChannel != nullptr ? nativeInputChannel->getInputChannel() : nullptr;
 }
 
-void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
-        InputChannelObjDisposeCallback callback, void* data) {
-    NativeInputChannel* nativeInputChannel =
-            android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
-    if (!nativeInputChannel || !nativeInputChannel->getInputChannel()) {
-        ALOGW("Cannot set dispose callback because input channel object has not been initialized.");
-    } else {
-        nativeInputChannel->setDisposeCallback(callback, data);
-    }
-}
-
 static jlong android_view_InputChannel_createInputChannel(
         JNIEnv* env, std::unique_ptr<InputChannel> inputChannel) {
     std::unique_ptr<NativeInputChannel> nativeInputChannel =
diff --git a/core/jni/android_window_InputTransferToken.cpp b/core/jni/android_window_InputTransferToken.cpp
index f92d128..b4efacb 100644
--- a/core/jni/android_window_InputTransferToken.cpp
+++ b/core/jni/android_window_InputTransferToken.cpp
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#undef ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION // TODO:remove this and fix code
 
 #define LOG_TAG "InputTransferToken"
 
@@ -53,7 +52,7 @@
 }
 
 static void nativeWriteToParcel(JNIEnv* env, jclass clazz, jlong nativeObj, jobject parcelObj) {
-    InputTransferToken* inputTransferToken = reinterpret_cast<InputTransferToken*>(nativeObj);
+    auto inputTransferToken = reinterpret_cast<InputTransferToken*>(nativeObj);
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
     inputTransferToken->writeToParcel(parcel);
 }
@@ -67,12 +66,12 @@
 }
 
 static jobject nativeGetBinderToken(JNIEnv* env, jclass clazz, jlong nativeObj) {
-    sp<InputTransferToken> inputTransferToken = reinterpret_cast<InputTransferToken*>(nativeObj);
+    auto inputTransferToken = reinterpret_cast<InputTransferToken*>(nativeObj);
     return javaObjectForIBinder(env, inputTransferToken->mToken);
 }
 
 static jlong nativeGetBinderTokenRef(JNIEnv*, jclass, jlong nativeObj) {
-    sp<InputTransferToken> inputTransferToken = reinterpret_cast<InputTransferToken*>(nativeObj);
+    auto inputTransferToken = reinterpret_cast<InputTransferToken*>(nativeObj);
     return reinterpret_cast<jlong>(inputTransferToken->mToken.get());
 }
 
@@ -105,12 +104,9 @@
 
 static bool nativeEquals(JNIEnv* env, jclass clazz, jlong inputTransferTokenObj1,
                          jlong inputTransferTokenObj2) {
-    sp<InputTransferToken> inputTransferToken1(
-            reinterpret_cast<InputTransferToken*>(inputTransferTokenObj1));
-    sp<InputTransferToken> inputTransferToken2(
-            reinterpret_cast<InputTransferToken*>(inputTransferTokenObj2));
-
-    return inputTransferToken1 == inputTransferToken2;
+    auto token1 = reinterpret_cast<InputTransferToken*>(inputTransferTokenObj1);
+    auto token2 = reinterpret_cast<InputTransferToken*>(inputTransferTokenObj2);
+    return (token1 != nullptr) && (token2 != nullptr) && (*token1 == *token2);
 }
 
 static const JNINativeMethod sInputTransferTokenMethods[] = {
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index 7b085b1..ba74b0e 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -109,27 +109,29 @@
             return binder::Status::ok();
         }
         captureResults.fenceResult.value()->waitForever(LOG_TAG);
-        jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
-                env, captureResults.buffer->toAHardwareBuffer());
-        jobject jGainmap = nullptr;
+        auto jhardwareBuffer = ScopedLocalRef<jobject>(
+                env, android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+                        env, captureResults.buffer->toAHardwareBuffer()));
+        auto jGainmap = ScopedLocalRef<jobject>(env);
         if (captureResults.optionalGainMap) {
-            jGainmap = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
-                    env, captureResults.optionalGainMap->toAHardwareBuffer());
+            jGainmap = ScopedLocalRef<jobject>(
+                    env, android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+                            env, captureResults.optionalGainMap->toAHardwareBuffer()));
         }
-        jobject screenshotHardwareBuffer =
-                env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
+        auto screenshotHardwareBuffer =
+                ScopedLocalRef<jobject>(env, env->CallStaticObjectMethod(
+                                            gScreenshotHardwareBufferClassInfo.clazz,
                                             gScreenshotHardwareBufferClassInfo.builder,
-                                            jhardwareBuffer,
+                                            jhardwareBuffer.get(),
                                             static_cast<jint>(captureResults.capturedDataspace),
                                             captureResults.capturedSecureLayers,
-                                            captureResults.capturedHdrLayers, jGainmap,
-                                            captureResults.hdrSdrRatio);
+                                            captureResults.capturedHdrLayers, jGainmap.get(),
+                                            captureResults.hdrSdrRatio));
         checkAndClearException(env, "builder");
-        env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, screenshotHardwareBuffer,
+        env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept,
+                            screenshotHardwareBuffer.get(),
                             fenceStatus(captureResults.fenceResult));
         checkAndClearException(env, "accept");
-        env->DeleteLocalRef(jhardwareBuffer);
-        env->DeleteLocalRef(screenshotHardwareBuffer);
         return binder::Status::ok();
     }
 
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
index 48c92c8..886b9f1 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.cpp
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -201,8 +201,8 @@
     return true;
 }
 
-bool getLoadSegmentPhdrs(const char *filePath, const uint64_t offset,
-                         std::vector<Elf64_Phdr> &programHeaders) {
+read_elf_status_t getLoadSegmentPhdrs(const char *filePath, const uint64_t offset,
+                                      std::vector<Elf64_Phdr> &programHeaders) {
     // Open Elf file
     Elf64_Ehdr ehdr;
     std::ifstream inputStream(filePath, std::ifstream::in);
@@ -212,13 +212,13 @@
     // read executable headers
     inputStream.read((char *)&ehdr, sizeof(ehdr));
     if (!inputStream.good()) {
-        return false;
+        return ELF_READ_ERROR;
     }
 
-    // only consider elf64 for punching holes
+    // only consider ELF64 files
     if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
         ALOGW("Provided file is not ELF64");
-        return false;
+        return ELF_IS_NOT_64_BIT;
     }
 
     // read the program headers from elf file
@@ -229,7 +229,7 @@
     uint64_t phOffset;
     if (__builtin_add_overflow(offset, programHeaderOffset, &phOffset)) {
         ALOGE("Overflow occurred when calculating phOffset");
-        return false;
+        return ELF_READ_ERROR;
     }
     inputStream.seekg(phOffset);
 
@@ -237,7 +237,7 @@
         Elf64_Phdr header;
         inputStream.read((char *)&header, sizeof(header));
         if (!inputStream.good()) {
-            return false;
+            return ELF_READ_ERROR;
         }
 
         if (header.p_type != PT_LOAD) {
@@ -246,13 +246,14 @@
         programHeaders.push_back(header);
     }
 
-    return true;
+    return ELF_READ_OK;
 }
 
 bool punchHolesInElf64(const char *filePath, const uint64_t offset) {
     std::vector<Elf64_Phdr> programHeaders;
-    if (!getLoadSegmentPhdrs(filePath, offset, programHeaders)) {
-        ALOGE("Failed to read program headers from ELF file.");
+    read_elf_status_t status = getLoadSegmentPhdrs(filePath, offset, programHeaders);
+    if (status != ELF_READ_OK) {
+        ALOGE("Failed to read program headers from 64 bit ELF file.");
         return false;
     }
     return punchHoles(filePath, offset, programHeaders);
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.h b/core/jni/com_android_internal_content_FileSystemUtils.h
index 4a95686c..c4dc111 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.h
+++ b/core/jni/com_android_internal_content_FileSystemUtils.h
@@ -22,6 +22,12 @@
 
 namespace android {
 
+enum read_elf_status_t {
+    ELF_IS_NOT_64_BIT = -2,
+    ELF_READ_ERROR = -1,
+    ELF_READ_OK = 0,
+};
+
 /*
  * This function deallocates space used by zero padding at the end of LOAD segments in given
  * uncompressed ELF file. Read ELF headers and find out the offset and sizes of LOAD segments.
@@ -39,10 +45,10 @@
 bool punchHolesInZip(const char* filePath, uint64_t offset, uint16_t extraFieldLen);
 
 /*
- * This function reads program headers from ELF file. ELF can be specified with file path directly
- * or it should be at offset inside Apk. Program headers passed to function is populated.
+ * This function reads program headers from 64 bit ELF file. ELF can be specified with file path
+ * directly or it should be at offset inside Apk. Program headers passed to function is populated.
  */
-bool getLoadSegmentPhdrs(const char* filePath, const uint64_t offset,
-                         std::vector<Elf64_Phdr>& programHeaders);
+read_elf_status_t getLoadSegmentPhdrs(const char* filePath, const uint64_t offset,
+                                      std::vector<Elf64_Phdr>& programHeaders);
 
 } // namespace android
\ No newline at end of file
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 14132e6..7cf523f 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -640,7 +640,17 @@
 
 static jint checkLoadSegmentAlignment(const char* fileName, off64_t offset) {
     std::vector<Elf64_Phdr> programHeaders;
-    if (!getLoadSegmentPhdrs(fileName, offset, programHeaders)) {
+    read_elf_status_t status = getLoadSegmentPhdrs(fileName, offset, programHeaders);
+    // Ignore the ELFs which are not 64 bit.
+    if (status == ELF_IS_NOT_64_BIT) {
+        ALOGW("ELF file is not 64 Bit");
+        // PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED is equivalent of skipping the current file.
+        // on return, flag is OR'ed with flags from other ELF files. If some app has 32 bit ELF in
+        // 64 bit directory, alignment of that ELF will be ignored.
+        return PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+    }
+
+    if (status == ELF_READ_ERROR) {
         ALOGE("Failed to read program headers from ELF file.");
         return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
     }
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 59e01bf..9df351f 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -35,7 +35,6 @@
 import "frameworks/base/core/proto/android/providers/settings.proto";
 import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
 import "frameworks/base/core/proto/android/server/alarm/alarmmanagerservice.proto";
-import "frameworks/base/core/proto/android/server/bluetooth_manager_service.proto";
 import "frameworks/base/core/proto/android/server/fingerprint.proto";
 import "frameworks/base/core/proto/android/server/jobscheduler.proto";
 import "frameworks/base/core/proto/android/server/location/context_hub.proto";
@@ -483,10 +482,8 @@
         (section).args = "connmetrics --proto"
     ];
 
-    optional com.android.server.BluetoothManagerServiceDumpProto bluetooth_manager = 3050 [
-        (section).type = SECTION_DUMPSYS,
-        (section).args = "bluetooth_manager --proto"
-    ];
+    // Deprecated BluetoothManagerServiceDumpProto
+    reserved 3050;
 
     optional com.android.server.location.ContextHubServiceProto context_hub = 3051 [
         (section).type = SECTION_DUMPSYS,
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index ac4bac6..5831a0b 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -112,7 +112,8 @@
         optional SettingProto autoclick_ignore_minor_cursor_movement = 63 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto autoclick_panel_position = 64 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto autoclick_revert_to_left_click = 65 [ (android.privacy).dest = DEST_AUTOMATIC ];
-
+        // Setting for accessibility magnification for cursor following mode.
+        optional SettingProto accessibility_magnification_cursor_following_mode = 66 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Accessibility accessibility = 2;
 
@@ -238,6 +239,10 @@
     repeated SettingProto completed_categories = 15;
     optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto adaptive_connectivity_enabled = 84 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto adaptive_connectivity_wifi_enabled = 105 [ (android.privacy).dest =
+        DEST_AUTOMATIC ];
+    optional SettingProto adaptive_connectivity_mobile_network_enabled = 106 [ (android.privacy)
+        .dest = DEST_AUTOMATIC ];
 
     message Controls {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -740,5 +745,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 105;
+    // Next tag = 107;
 }
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 325790c..8393f8b 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -290,7 +290,16 @@
 
     optional SettingProto apply_ramping_ringer = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    message Display {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto cv_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Display display = 39;
+
+
+
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 39;
+    // Next tag = 40;
 }
diff --git a/core/proto/android/server/Android.bp b/core/proto/android/server/Android.bp
deleted file mode 100644
index 362daa7..0000000
--- a/core/proto/android/server/Android.bp
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// 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.
-//
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
-  name: "srcs_bluetooth_manager_service_proto",
-  srcs: [
-      "bluetooth_manager_service.proto",
-  ],
-  visibility: ["//packages/modules/Bluetooth:__subpackages__"],
-}
-
diff --git a/core/proto/android/server/bluetooth_manager_service.proto b/core/proto/android/server/bluetooth_manager_service.proto
deleted file mode 100644
index c33f66a..0000000
--- a/core/proto/android/server/bluetooth_manager_service.proto
+++ /dev/null
@@ -1,49 +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.
- */
-
-syntax = "proto2";
-package com.android.server;
-
-import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/bluetooth/enums.proto";
-
-option java_multiple_files = true;
-
-message BluetoothManagerServiceDumpProto {
-   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-   message ActiveLog {
-      option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-      optional int64 timestamp_ms = 1;
-      optional bool enable = 2;
-      optional string package_name = 3;
-      optional .android.bluetooth.EnableDisableReasonEnum reason = 4;
-   }
-
-   optional bool enabled = 1;
-   optional int32 state = 2;
-   optional string state_name = 3;
-   optional string address = 4 [(.android.privacy).dest = DEST_EXPLICIT];
-   optional string name = 5 [(.android.privacy).dest = DEST_EXPLICIT];
-   optional int64 last_enabled_time_ms = 6;
-   optional int64 curr_timestamp_ms = 7;
-   repeated ActiveLog active_logs = 8;
-   optional int32 num_crashes = 9;
-   optional bool crash_log_maxed = 10;
-   repeated int64 crash_timestamps_ms = 11;
-   optional int32 num_ble_apps = 12;
-   repeated string ble_app_package_names = 13;
-}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 36b65ba..e16ce98 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -9292,9 +9292,6 @@
                 <action android:name="android.intent.action.UPDATE_PINS" />
                 <data android:scheme="content" android:host="*" android:mimeType="*/*" />
             </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.BOOT_COMPLETED" />
-            </intent-filter>
         </receiver>
 
         <receiver android:name="com.android.server.updates.IntentFirewallInstallReceiver"
diff --git a/core/res/res/color/notification_close_button_state_tint.xml b/core/res/res/color/notification_close_button_state_tint.xml
new file mode 100644
index 0000000..bc42ceb
--- /dev/null
+++ b/core/res/res/color/notification_close_button_state_tint.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, softwarere
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/notification_expand_button_state_tint" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable-w192dp/loader_horizontal_watch.xml b/core/res/res/drawable-w192dp/loader_horizontal_watch.xml
new file mode 100644
index 0000000..18cea6e
--- /dev/null
+++ b/core/res/res/drawable-w192dp/loader_horizontal_watch.xml
@@ -0,0 +1,97 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:height="15dp" android:width="67dp" android:viewportHeight="15" android:viewportWidth="67">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_1_G" android:translateX="33.5" android:translateY="7.5">
+                    <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M33.5 -7.5 C33.5,-7.5 33.5,7.5 33.5,7.5 C33.5,7.5 -33.5,7.5 -33.5,7.5 C-33.5,7.5 -33.5,-7.5 -33.5,-7.5 C-33.5,-7.5 33.5,-7.5 33.5,-7.5c "/>
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="-296.5" android:translateY="-62.5" android:pivotX="330" android:pivotY="70" android:scaleX="0.1" android:scaleY="0.1">
+                    <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-224.84700000000004" android:translateY="-321.948" android:pivotX="555.09" android:pivotY="-329" android:rotation="28.9" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M194.88 359 C190,359 185.05,357.81 180.48,355.3 C59.86,289.14 -41.55,191.9 -112.79,74.11 C-186.14,-47.16 -224.91,-186.55 -224.91,-329 C-224.91,-345.57 -211.48,-359 -194.91,-359 C-178.34,-359 -164.91,-345.57 -164.91,-329 C-164.91,-197.5 -129.13,-68.84 -61.45,43.06 C4.33,151.82 97.97,241.6 209.33,302.69 C223.86,310.66 229.18,328.9 221.21,343.42 C215.75,353.37 205.48,359 194.88,359c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_5_G" android:translateX="744.323" android:translateY="-277.96299999999997" android:pivotX="-414.08" android:pivotY="-372.985" android:rotation="28.9">
+                        <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-335.95 402.99 C-351.13,402.99 -364.16,391.5 -365.76,376.07 C-367.46,359.59 -355.49,344.85 -339.01,343.14 C-162.93,324.91 -0.15,242.33 119.34,110.62 C239.66,-22.01 305.92,-193.76 305.92,-372.98 C305.92,-389.55 319.35,-402.98 335.92,-402.98 C352.49,-402.98 365.92,-389.55 365.92,-372.98 C365.92,-178.82 294.13,7.24 163.78,150.93 C34.34,293.61 -142.03,383.07 -332.83,402.82 C-333.88,402.93 -334.92,402.99 -335.95,402.99c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_2_G" android:translateX="185.385" android:translateY="70.09100000000001" android:pivotX="144.858" android:pivotY="-721.039" android:rotation="28.9" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M144.62 58.96 C144.61,58.96 144.61,58.96 144.6,58.96 C40.39,58.93 -60.82,38.66 -156.19,-1.28 C-171.48,-7.68 -178.68,-25.26 -172.28,-40.54 C-165.88,-55.82 -148.3,-63.02 -133.02,-56.62 C-45.02,-19.77 48.4,-1.07 144.63,-1.04 C161.19,-1.03 174.62,12.4 174.62,28.97 C174.61,45.53 161.18,58.96 144.62,58.96c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_0_G" android:translateX="330" android:translateY="70">
+                        <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-660 -313 C-660,-313 -660,313 -660,313 C-660,313 660,313 660,313 C660,313 660,-313 660,-313 C660,-313 -660,-313 -660,-313c  M300.74 -1.16 C205.46,38.62 103.22,59.09 -0.03,59.05 C-103.28,59.01 -205.51,38.48 -300.76,-1.37 C-316.05,-7.76 -323.26,-25.34 -316.86,-40.62 C-310.47,-55.91 -292.9,-63.12 -277.61,-56.72 C-189.68,-19.94 -95.32,-0.98 -0.01,-0.95 C95.3,-0.92 189.67,-19.81 277.63,-56.53 C292.92,-62.91 310.49,-55.69 316.87,-40.4 C323.25,-25.11 316.03,-7.54 300.74,-1.16c "/>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.9" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.135 0.202,0.848 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.9" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.135 0.202,0.848 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.9" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.135 0.202,0.848 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
+
diff --git a/core/res/res/drawable-w204dp/loader_horizontal_watch.xml b/core/res/res/drawable-w204dp/loader_horizontal_watch.xml
new file mode 100644
index 0000000..fbc6eab
--- /dev/null
+++ b/core/res/res/drawable-w204dp/loader_horizontal_watch.xml
@@ -0,0 +1,104 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:height="15dp" android:width="70dp" android:viewportHeight="15" android:viewportWidth="70">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_1_G" android:translateX="35" android:translateY="7.5">
+                    <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M35 -7.5 C35,-7.5 35,7.5 35,7.5 C35,7.5 -35,7.5 -35,7.5 C-35,7.5 -35,-7.5 -35,-7.5 C-35,-7.5 35,-7.5 35,-7.5c "/>
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="-310" android:translateY="-64" android:pivotX="345" android:pivotY="71.5" android:scaleX="0.1" android:scaleY="0.1">
+                    <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-239.44799999999998" android:translateY="-341.45" android:pivotX="584.448" android:pivotY="-346.55" android:rotation="28.8" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M205.28 376.55 C200.4,376.55 195.46,375.36 190.88,372.85 C64.08,303.29 -42.54,201.07 -117.44,77.24 C-194.55,-50.25 -235.31,-196.79 -235.31,-346.55 C-235.31,-363.12 -221.88,-376.55 -205.31,-376.55 C-188.74,-376.55 -175.31,-363.12 -175.31,-346.55 C-175.31,-207.74 -137.54,-71.93 -66.1,46.19 C3.34,160.99 102.18,255.76 219.73,320.24 C234.26,328.21 239.58,346.45 231.61,360.97 C226.15,370.92 215.88,376.55 205.28,376.55c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_5_G" android:translateX="781.413" android:translateY="-295.124" android:pivotX="-436.413" android:pivotY="-392.876" android:rotation="28.8">
+                        <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-353.86 422.88 C-369.04,422.88 -382.07,411.4 -383.67,395.97 C-385.37,379.49 -373.4,364.74 -356.92,363.03 C-171.06,343.79 0.76,256.62 126.89,117.59 C253.89,-22.41 323.83,-203.7 323.83,-392.88 C323.83,-409.44 337.26,-422.88 353.83,-422.88 C370.4,-422.88 383.83,-409.44 383.83,-392.88 C383.83,-188.76 308.36,6.84 171.32,157.9 C35.25,307.89 -150.15,401.94 -350.74,422.72 C-351.79,422.82 -352.83,422.88 -353.86,422.88c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_2_G" android:translateX="192.671" android:translateY="71.49599999999998" android:pivotX="152.329" android:pivotY="-759.496" android:rotation="28.8" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M152.33 60.5 C152.33,60.5 152.32,60.5 152.32,60.5 C42.76,60.47 -63.64,39.16 -163.91,-2.82 C-179.19,-9.22 -186.39,-26.8 -179.99,-42.08 C-173.59,-57.36 -156.02,-64.57 -140.73,-58.16 C-47.84,-19.27 50.77,0.47 152.34,0.5 C168.91,0.51 182.33,13.94 182.33,30.51 C182.32,47.08 168.89,60.5 152.33,60.5c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_0_G" android:translateX="345" android:translateY="71.5">
+                        <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-579 -259.5 C-579,-259.5 -579,259.5 -579,259.5 C-579,259.5 579,259.5 579,259.5 C579,259.5 579,-259.5 579,-259.5 C579,-259.5 -579,-259.5 -579,-259.5c  M316.17 -2.8 C216,39.02 108.52,60.54 -0.03,60.5 C-108.58,60.46 -216.04,38.87 -316.18,-3.02 C-331.47,-9.41 -338.68,-26.99 -332.28,-42.27 C-325.89,-57.56 -308.32,-64.76 -293.03,-58.37 C-200.22,-19.55 -100.61,0.46 -0.01,0.5 C100.6,0.54 200.22,-19.41 293.06,-58.17 C308.35,-64.55 325.92,-57.33 332.3,-42.04 C338.68,-26.75 331.46,-9.18 316.17,-2.8c "/>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
+
diff --git a/core/res/res/drawable-w216dp/loader_horizontal_watch.xml b/core/res/res/drawable-w216dp/loader_horizontal_watch.xml
new file mode 100644
index 0000000..ed4b7ea
--- /dev/null
+++ b/core/res/res/drawable-w216dp/loader_horizontal_watch.xml
@@ -0,0 +1,105 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:height="16dp" android:width="74dp" android:viewportHeight="16" android:viewportWidth="74">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_1_G" android:translateX="37" android:translateY="8">
+                    <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M37 -8 C37,-8 37,8 37,8 C37,8 -37,8 -37,8 C-37,8 -37,-8 -37,-8 C-37,-8 37,-8 37,-8c "/>
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="-328" android:translateY="-65.5" android:pivotX="365" android:pivotY="73.5" android:scaleX="0.1" android:scaleY="0.1">
+                    <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-256.447" android:translateY="-365.014" android:pivotX="621.447" android:pivotY="-368.486" android:rotation="28.8" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M218.28 398.49 C213.4,398.49 208.46,397.3 203.88,394.78 C69.34,320.99 -43.78,212.53 -123.25,81.15 C-205.06,-54.11 -248.31,-209.59 -248.31,-368.49 C-248.31,-385.05 -234.88,-398.49 -218.31,-398.49 C-201.74,-398.49 -188.31,-385.05 -188.31,-368.49 C-188.31,-220.54 -148.06,-75.8 -71.91,50.09 C2.1,172.45 107.45,273.45 232.73,342.18 C247.26,350.15 252.58,368.38 244.61,382.91 C239.15,392.86 228.88,398.49 218.28,398.49c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_5_G" android:translateX="829.0260000000001" android:translateY="-315.759" android:pivotX="-464.026" android:pivotY="-417.741" android:rotation="28.8">
+                        <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-376.25 447.74 C-391.43,447.74 -404.46,436.26 -406.05,420.83 C-407.76,404.35 -395.78,389.61 -379.3,387.9 C-181.22,367.38 1.9,274.48 136.32,126.3 C271.67,-22.9 346.22,-216.12 346.22,-417.74 C346.22,-434.31 359.65,-447.74 376.22,-447.74 C392.79,-447.74 406.22,-434.31 406.22,-417.74 C406.22,-201.18 326.15,6.35 180.76,166.61 C36.39,325.75 -160.31,425.54 -373.12,447.58 C-374.17,447.69 -375.22,447.74 -376.25,447.74c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_2_G" android:translateX="203.029" android:translateY="74.06899999999996" android:pivotX="161.971" android:pivotY="-807.569" android:rotation="28.8" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M161.97 62.43 C161.97,62.43 161.96,62.43 161.96,62.43 C45.71,62.4 -67.17,39.79 -173.55,-4.75 C-188.83,-11.15 -196.03,-28.72 -189.63,-44.01 C-183.24,-59.29 -165.66,-66.49 -150.38,-60.09 C-51.37,-18.64 53.72,2.4 161.98,2.43 C178.55,2.44 191.98,15.87 191.97,32.44 C191.97,49 178.54,62.43 161.97,62.43c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_0_G" android:translateX="365" android:translateY="73.5">
+                        <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-609 -244.5 C-609,-244.5 -609,244.5 -609,244.5 C-609,244.5 609,244.5 609,244.5 C609,244.5 609,-244.5 609,-244.5 C609,-244.5 -609,-244.5 -609,-244.5c  M335.44 -4.16 C229.17,40.21 115.13,63.04 -0.04,63 C-115.21,62.96 -229.22,40.05 -335.47,-4.39 C-350.76,-10.79 -357.95,-28.36 -351.56,-43.65 C-345.17,-58.93 -327.59,-66.14 -312.31,-59.74 C-213.39,-18.36 -107.24,2.96 -0.02,3 C107.21,3.04 213.38,-18.22 312.33,-59.53 C327.62,-65.91 345.19,-58.69 351.57,-43.4 C357.95,-28.11 350.73,-10.54 335.44,-4.16c "/>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.3" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.3" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.3" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
+
+
diff --git a/core/res/res/drawable-w228dp/loader_horizontal_watch.xml b/core/res/res/drawable-w228dp/loader_horizontal_watch.xml
new file mode 100644
index 0000000..c4574d8
--- /dev/null
+++ b/core/res/res/drawable-w228dp/loader_horizontal_watch.xml
@@ -0,0 +1,103 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:height="16dp" android:width="78dp" android:viewportHeight="16" android:viewportWidth="78">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_1_G" android:translateX="39" android:translateY="8">
+                    <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M39 -8 C39,-8 39,8 39,8 C39,8 -39,8 -39,8 C-39,8 -39,-8 -39,-8 C-39,-8 39,-8 39,-8c "/>
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="-345" android:translateY="-67" android:pivotX="384" android:pivotY="75" android:scaleX="0.1" android:scaleY="0.1">
+                    <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-274.19" android:translateY="-390.077" android:pivotX="658.448" android:pivotY="-390.423" android:rotation="28.7" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M231.28 420.42 C226.4,420.42 221.45,419.23 216.88,416.72 C74.61,338.68 -45.02,224 -129.06,85.06 C-171.54,14.82 -204.38,-60.73 -226.66,-139.5 C-249.65,-220.76 -261.31,-305.18 -261.31,-390.42 C-261.31,-406.99 -247.88,-420.42 -231.31,-420.42 C-214.74,-420.42 -201.31,-406.99 -201.31,-390.42 C-201.31,-310.71 -190.42,-231.78 -168.93,-155.83 C-148.11,-82.23 -117.42,-11.63 -77.72,54 C0.86,183.92 112.71,291.15 245.73,364.12 C260.26,372.08 265.58,390.32 257.61,404.85 C252.15,414.79 241.88,420.42 231.28,420.42c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_5_G" android:translateX="875.8979999999999" android:translateY="-337.894" android:pivotX="-491.64" android:pivotY="-442.606" android:rotation="28.7">
+                        <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-398.64 472.61 C-413.82,472.61 -426.84,461.13 -428.44,445.7 C-430.15,429.22 -418.17,414.47 -401.69,412.77 C-191.38,390.98 3.04,292.33 145.75,135.01 C289.46,-23.4 368.6,-228.54 368.6,-442.61 C368.6,-459.17 382.04,-472.61 398.6,-472.61 C415.17,-472.61 428.6,-459.17 428.6,-442.61 C428.6,-213.6 343.93,5.85 190.19,175.33 C37.53,343.61 -170.48,449.13 -395.51,472.44 C-396.56,472.55 -397.6,472.61 -398.64,472.61c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_2_G" android:translateX="212.64499999999998" android:translateY="75.14200000000005" android:pivotX="171.613" android:pivotY="-855.642" android:rotation="28.7" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M171.61 64.36 C171.61,64.36 171.61,64.36 171.61,64.36 C48.68,64.32 -70.7,40.42 -183.19,-6.68 C-198.47,-13.07 -205.68,-30.65 -199.28,-45.93 C-192.88,-61.22 -175.3,-68.42 -160.02,-62.02 C-54.9,-18.01 56.68,4.33 171.62,4.36 C188.19,4.36 201.62,17.8 201.61,34.36 C201.61,50.93 188.18,64.36 171.61,64.36c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_0_G" android:translateX="384" android:translateY="75">
+                        <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-611 -259 C-611,-259 -611,259 -611,259 C-611,259 611,259 611,259 C611,259 611,-259 611,-259 C611,-259 -611,-259 -611,-259c  M354.66 -6.52 C242.36,40.4 121.76,64.54 -0.04,64.5 C-121.84,64.46 -242.44,40.23 -354.74,-6.76 C-370.04,-13.16 -377.24,-30.73 -370.84,-46.02 C-364.44,-61.3 -346.94,-68.51 -331.64,-62.12 C-226.54,-18.18 -113.84,4.46 -0.04,4.5 C113.76,4.54 226.56,-18.02 331.56,-61.89 C346.86,-68.27 364.46,-61.05 370.86,-45.76 C377.26,-30.47 369.96,-12.9 354.66,-6.52c "/>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
diff --git a/core/res/res/drawable-w240dp/loader_horizontal_watch.xml b/core/res/res/drawable-w240dp/loader_horizontal_watch.xml
new file mode 100644
index 0000000..ad60bbd
--- /dev/null
+++ b/core/res/res/drawable-w240dp/loader_horizontal_watch.xml
@@ -0,0 +1,104 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:height="17dp" android:width="82dp" android:viewportHeight="17" android:viewportWidth="82">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_1_G" android:translateX="41" android:translateY="8.5">
+                    <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M41 -8.5 C41,-8.5 41,8.5 41,8.5 C41,8.5 -41,8.5 -41,8.5 C-41,8.5 -41,-8.5 -41,-8.5 C-41,-8.5 41,-8.5 41,-8.5c "/>
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="-362.5" android:translateY="-69" android:pivotX="403.5" android:pivotY="77.5" android:scaleX="0.1" android:scaleY="0.1">
+                    <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-291.64799999999997" android:translateY="-414.141" android:pivotX="695.448" android:pivotY="-412.359" android:rotation="28.7" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M244.28 442.36 C239.4,442.36 234.45,441.17 229.88,438.66 C79.87,356.38 -46.26,235.46 -134.87,88.96 C-179.66,14.91 -214.28,-64.74 -237.78,-147.79 C-262.02,-233.47 -274.31,-322.49 -274.31,-412.36 C-274.31,-428.93 -260.88,-442.36 -244.31,-442.36 C-227.74,-442.36 -214.31,-428.93 -214.31,-412.36 C-214.31,-328.01 -202.78,-244.49 -180.05,-164.12 C-158.01,-86.24 -125.54,-11.54 -83.53,57.91 C-0.38,195.38 117.97,308.85 258.73,386.05 C273.26,394.02 278.58,412.26 270.61,426.78 C265.15,436.73 254.88,442.36 244.28,442.36c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_5_G" android:translateX="923.0530000000001" android:translateY="-359.029" android:pivotX="-519.253" android:pivotY="-467.471" android:rotation="28.7">
+                        <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-421.02 497.47 C-436.2,497.47 -449.23,485.99 -450.83,470.56 C-452.53,454.08 -440.56,439.34 -424.08,437.63 C-201.54,414.57 4.18,310.19 155.19,143.73 C229.54,61.77 287.7,-31.75 328.04,-134.22 C369.81,-240.3 390.99,-352.42 390.99,-467.47 C390.99,-484.04 404.42,-497.47 420.99,-497.47 C437.56,-497.47 450.99,-484.04 450.99,-467.47 C450.99,-226.02 361.72,5.35 199.63,184.04 C38.67,361.47 -180.63,472.73 -417.89,497.31 C-418.94,497.42 -419.99,497.47 -421.02,497.47c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_2_G" android:translateX="222.54600000000002" android:translateY="77.21400000000006" android:pivotX="181.254" android:pivotY="-903.714" android:rotation="28.7" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M181.26 66.28 C181.25,66.28 181.25,66.28 181.25,66.28 C51.64,66.25 -74.22,41.06 -192.83,-8.6 C-208.12,-15 -215.32,-32.58 -208.92,-47.86 C-202.52,-63.15 -184.94,-70.35 -169.66,-63.95 C-58.42,-17.38 59.64,6.25 181.26,6.28 C197.83,6.29 211.26,19.72 211.26,36.29 C211.25,52.86 197.82,66.28 181.26,66.28c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_0_G" android:translateX="403.5" android:translateY="77.5">
+                        <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-630.5 -255.5 C-630.5,-255.5 -630.5,255.5 -630.5,255.5 C-630.5,255.5 630.5,255.5 630.5,255.5 C630.5,255.5 630.5,-255.5 630.5,-255.5 C630.5,-255.5 -630.5,-255.5 -630.5,-255.5c  M374 -8.88 C255.5,40.59 128.4,66.04 0,66 C-128.4,65.95 -255.6,40.42 -374,-9.14 C-389.3,-15.53 -396.5,-33.11 -390.1,-48.39 C-383.7,-63.68 -366.2,-70.88 -350.9,-64.49 C-239.7,-18 -120.5,5.96 0,6 C120.4,6.04 239.7,-17.84 350.9,-64.25 C366.2,-70.63 383.7,-63.41 390.1,-48.12 C396.5,-32.83 389.3,-15.26 374,-8.88c "/>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
+
diff --git a/core/res/res/drawable/accessibility_autoclick_scroll_down.xml b/core/res/res/drawable/accessibility_autoclick_scroll_down.xml
new file mode 100644
index 0000000..13f1ba0
--- /dev/null
+++ b/core/res/res/drawable/accessibility_autoclick_scroll_down.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@color/materialColorPrimary"
+        android:pathData="M12,20l8,-8h-5V4h-6v8H4z"/>
+</vector>
diff --git a/core/res/res/drawable/accessibility_autoclick_scroll_exit.xml b/core/res/res/drawable/accessibility_autoclick_scroll_exit.xml
new file mode 100644
index 0000000..e53301f
--- /dev/null
+++ b/core/res/res/drawable/accessibility_autoclick_scroll_exit.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@color/materialColorPrimary"
+        android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
diff --git a/core/res/res/drawable/accessibility_autoclick_scroll_left.xml b/core/res/res/drawable/accessibility_autoclick_scroll_left.xml
new file mode 100644
index 0000000..39475bc
--- /dev/null
+++ b/core/res/res/drawable/accessibility_autoclick_scroll_left.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@color/materialColorPrimary"
+        android:pathData="M4,12l8,8v-5h8v-6h-8V4z"/>
+</vector>
diff --git a/core/res/res/drawable/accessibility_autoclick_scroll_right.xml b/core/res/res/drawable/accessibility_autoclick_scroll_right.xml
new file mode 100644
index 0000000..bbd7b2a
--- /dev/null
+++ b/core/res/res/drawable/accessibility_autoclick_scroll_right.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@color/materialColorPrimary"
+        android:pathData="M20,12l-8,-8v5H4v6h8v5z"/>
+</vector>
diff --git a/core/res/res/drawable/accessibility_autoclick_scroll_up.xml b/core/res/res/drawable/accessibility_autoclick_scroll_up.xml
new file mode 100644
index 0000000..2e2c245
--- /dev/null
+++ b/core/res/res/drawable/accessibility_autoclick_scroll_up.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@color/materialColorPrimary"
+        android:pathData="M12,4L4,12h5v8h6v-8h5z"/>
+</vector>
diff --git a/core/res/res/drawable/close_button_bg.xml b/core/res/res/drawable/close_button_bg.xml
new file mode 100644
index 0000000..59ac7d4
--- /dev/null
+++ b/core/res/res/drawable/close_button_bg.xml
@@ -0,0 +1,30 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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:id="@+id/close_button_pill_colorized_layer">
+        <shape xmlns:android="http://schemas.android.com/apk/res/android">
+            <corners android:radius="@dimen/notification_close_button_size" />
+            <solid android:color="@android:color/white" />
+        </shape>
+    </item>
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android">
+            <corners android:radius="@dimen/notification_close_button_size" />
+            <solid android:color="@color/notification_close_button_state_tint" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/core/res/res/drawable/loader_horizontal_watch.xml b/core/res/res/drawable/loader_horizontal_watch.xml
new file mode 100644
index 0000000..6b86c63
--- /dev/null
+++ b/core/res/res/drawable/loader_horizontal_watch.xml
@@ -0,0 +1,103 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:height="14dp" android:width="76dp" android:viewportHeight="14" android:viewportWidth="76">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_1_G" android:translateX="39" android:translateY="8">
+                    <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M39 -8 C39,-8 39,8 39,8 C39,8 -39,8 -39,8 C-39,8 -39,-8 -39,-8 C-39,-8 39,-8 39,-8c "/>
+                </group>
+                <group android:name="_R_G_L_0_G" android:translateX="-345" android:translateY="-67" android:pivotX="384" android:pivotY="75" android:scaleX="0.1" android:scaleY="0.1">
+                    <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-274.19" android:translateY="-390.077" android:pivotX="658.448" android:pivotY="-390.423" android:rotation="28.7" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M231.28 420.42 C226.4,420.42 221.45,419.23 216.88,416.72 C74.61,338.68 -45.02,224 -129.06,85.06 C-171.54,14.82 -204.38,-60.73 -226.66,-139.5 C-249.65,-220.76 -261.31,-305.18 -261.31,-390.42 C-261.31,-406.99 -247.88,-420.42 -231.31,-420.42 C-214.74,-420.42 -201.31,-406.99 -201.31,-390.42 C-201.31,-310.71 -190.42,-231.78 -168.93,-155.83 C-148.11,-82.23 -117.42,-11.63 -77.72,54 C0.86,183.92 112.71,291.15 245.73,364.12 C260.26,372.08 265.58,390.32 257.61,404.85 C252.15,414.79 241.88,420.42 231.28,420.42c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_5_G" android:translateX="875.8979999999999" android:translateY="-337.894" android:pivotX="-491.64" android:pivotY="-442.606" android:rotation="28.7">
+                        <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-398.64 472.61 C-413.82,472.61 -426.84,461.13 -428.44,445.7 C-430.15,429.22 -418.17,414.47 -401.69,412.77 C-191.38,390.98 3.04,292.33 145.75,135.01 C289.46,-23.4 368.6,-228.54 368.6,-442.61 C368.6,-459.17 382.04,-472.61 398.6,-472.61 C415.17,-472.61 428.6,-459.17 428.6,-442.61 C428.6,-213.6 343.93,5.85 190.19,175.33 C37.53,343.61 -170.48,449.13 -395.51,472.44 C-396.56,472.55 -397.6,472.61 -398.64,472.61c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_2_G" android:translateX="212.64499999999998" android:translateY="75.14200000000005" android:pivotX="171.613" android:pivotY="-855.642" android:rotation="28.7" android:scaleY="0">
+                        <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M171.61 64.36 C171.61,64.36 171.61,64.36 171.61,64.36 C48.68,64.32 -70.7,40.42 -183.19,-6.68 C-198.47,-13.07 -205.68,-30.65 -199.28,-45.93 C-192.88,-61.22 -175.3,-68.42 -160.02,-62.02 C-54.9,-18.01 56.68,4.33 171.62,4.36 C188.19,4.36 201.62,17.8 201.61,34.36 C201.61,50.93 188.18,64.36 171.61,64.36c "/>
+                    </group>
+                    <group android:name="_R_G_L_0_G_L_0_G" android:translateX="384" android:translateY="75">
+                        <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-611 -259 C-611,-259 -611,259 -611,259 C-611,259 611,259 611,259 C611,259 611,-259 611,-259 C611,-259 -611,-259 -611,-259c  M354.66 -6.52 C242.36,40.4 121.76,64.54 -0.04,64.5 C-121.84,64.46 -242.44,40.23 -354.74,-6.76 C-370.04,-13.16 -377.24,-30.73 -370.84,-46.02 C-364.44,-61.3 -346.94,-68.51 -331.64,-62.12 C-226.54,-18.18 -113.84,4.46 -0.04,4.5 C113.76,4.54 226.56,-18.02 331.56,-61.89 C346.86,-68.27 364.46,-61.05 370.86,-45.76 C377.26,-30.47 369.96,-12.9 354.66,-6.52c "/>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_6_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_5_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_L_2_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
diff --git a/core/res/res/layout/accessibility_autoclick_scroll_panel.xml b/core/res/res/layout/accessibility_autoclick_scroll_panel.xml
new file mode 100644
index 0000000..1e093bb
--- /dev/null
+++ b/core/res/res/layout/accessibility_autoclick_scroll_panel.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/accessibility_autoclick_scroll_panel"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+    android:background="@drawable/accessibility_autoclick_type_panel_rounded_background"
+    android:orientation="vertical"
+    android:padding="16dp">
+
+    <!-- Up arrow -->
+    <LinearLayout
+        android:id="@+id/scroll_up_layout"
+        style="@style/AccessibilityAutoclickScrollPanelButtonLayoutStyle"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginBottom="@dimen/accessibility_autoclick_type_panel_button_spacing">
+        <ImageButton
+            android:id="@+id/scroll_up"
+            style="@style/AccessibilityAutoclickScrollPanelImageButtonStyle"
+            android:contentDescription="@string/accessibility_autoclick_scroll_up"
+            android:src="@drawable/accessibility_autoclick_scroll_up" />
+    </LinearLayout>
+
+    <!-- Middle row: Left, Exit, Right -->
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:id="@+id/scroll_left_layout"
+            style="@style/AccessibilityAutoclickScrollPanelButtonLayoutStyle"
+            android:layout_marginEnd="@dimen/accessibility_autoclick_type_panel_button_spacing">
+            <ImageButton
+                android:id="@+id/scroll_left"
+                style="@style/AccessibilityAutoclickScrollPanelImageButtonStyle"
+                android:contentDescription="@string/accessibility_autoclick_scroll_left"
+                android:src="@drawable/accessibility_autoclick_scroll_left" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/scroll_exit_layout"
+            style="@style/AccessibilityAutoclickScrollPanelButtonLayoutStyle"
+            android:layout_marginEnd="@dimen/accessibility_autoclick_type_panel_button_spacing">
+            <ImageButton
+                android:id="@+id/scroll_exit"
+                style="@style/AccessibilityAutoclickScrollPanelImageButtonStyle"
+                android:contentDescription="@string/accessibility_autoclick_scroll_exit"
+                android:src="@drawable/ic_close" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/scroll_right_layout"
+            style="@style/AccessibilityAutoclickScrollPanelButtonLayoutStyle">
+            <ImageButton
+                android:id="@+id/scroll_right"
+                style="@style/AccessibilityAutoclickScrollPanelImageButtonStyle"
+                android:contentDescription="@string/accessibility_autoclick_scroll_right"
+                android:src="@drawable/accessibility_autoclick_scroll_right" />
+        </LinearLayout>
+    </LinearLayout>
+
+    <!-- Down arrow -->
+    <LinearLayout
+        android:id="@+id/scroll_down_layout"
+        style="@style/AccessibilityAutoclickScrollPanelButtonLayoutStyle"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="@dimen/accessibility_autoclick_type_panel_button_spacing">
+        <ImageButton
+            android:id="@+id/scroll_down"
+            style="@style/AccessibilityAutoclickScrollPanelImageButtonStyle"
+            android:contentDescription="@string/accessibility_autoclick_scroll_down"
+            android:src="@drawable/accessibility_autoclick_scroll_down" />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/accessibility_autoclick_type_panel.xml b/core/res/res/layout/accessibility_autoclick_type_panel.xml
index 902ef7f..615af6f 100644
--- a/core/res/res/layout/accessibility_autoclick_type_panel.xml
+++ b/core/res/res/layout/accessibility_autoclick_type_panel.xml
@@ -49,7 +49,8 @@
                     android:id="@+id/accessibility_autoclick_drag_button"
                     style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                     android:contentDescription="@string/accessibility_autoclick_drag"
-                    android:src="@drawable/accessibility_autoclick_drag" />
+                    android:src="@drawable/accessibility_autoclick_drag"
+                    android:clickable="false" />
             </LinearLayout>
 
             <LinearLayout
@@ -60,7 +61,8 @@
                     android:id="@+id/accessibility_autoclick_double_click_button"
                     style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                     android:contentDescription="@string/accessibility_autoclick_double_click"
-                    android:src="@drawable/accessibility_autoclick_double_click" />
+                    android:src="@drawable/accessibility_autoclick_double_click"
+                    android:clickable="false" />
             </LinearLayout>
 
             <LinearLayout
@@ -71,7 +73,8 @@
                     android:id="@+id/accessibility_autoclick_right_click_button"
                     style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                     android:contentDescription="@string/accessibility_autoclick_right_click"
-                    android:src="@drawable/accessibility_autoclick_right_click" />
+                    android:src="@drawable/accessibility_autoclick_right_click"
+                    android:clickable="false" />
             </LinearLayout>
 
             <LinearLayout
@@ -82,7 +85,8 @@
                     android:id="@+id/accessibility_autoclick_scroll_button"
                     style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                     android:contentDescription="@string/accessibility_autoclick_scroll"
-                    android:src="@drawable/accessibility_autoclick_scroll" />
+                    android:src="@drawable/accessibility_autoclick_scroll"
+                    android:clickable="false" />
             </LinearLayout>
 
             <LinearLayout
@@ -93,7 +97,8 @@
                     android:id="@+id/accessibility_autoclick_left_click_button"
                     style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                     android:contentDescription="@string/accessibility_autoclick_left_click"
-                    android:src="@drawable/accessibility_autoclick_left_click" />
+                    android:src="@drawable/accessibility_autoclick_left_click"
+                    android:clickable="false" />
             </LinearLayout>
 
         </LinearLayout>
@@ -114,7 +119,8 @@
                 android:id="@+id/accessibility_autoclick_pause_button"
                 style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                 android:contentDescription="@string/accessibility_autoclick_pause"
-                android:src="@drawable/accessibility_autoclick_pause" />
+                android:src="@drawable/accessibility_autoclick_pause"
+                android:clickable="false" />
         </LinearLayout>
 
         <LinearLayout
@@ -125,7 +131,8 @@
                 android:id="@+id/accessibility_autoclick_position_button"
                 style="@style/AccessibilityAutoclickPanelImageButtonStyle"
                 android:contentDescription="@string/accessibility_autoclick_position"
-                android:src="@drawable/accessibility_autoclick_position" />
+                android:src="@drawable/accessibility_autoclick_position"
+                android:clickable="false" />
         </LinearLayout>
 
     </LinearLayout>
diff --git a/core/res/res/layout/chooser_az_label_row.xml b/core/res/res/layout/chooser_az_label_row.xml
index baf07ce..0e907c1 100644
--- a/core/res/res/layout/chooser_az_label_row.xml
+++ b/core/res/res/layout/chooser_az_label_row.xml
@@ -16,7 +16,7 @@
   -->
 
 <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
-          android:contentDescription="@string/chooser_all_apps_button_label"
+          android:importantForAccessibility="no"
           android:src="@drawable/chooser_row_layer_list"
           android:paddingTop="16dp"
           android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_2025_action_list.xml b/core/res/res/layout/notification_2025_action_list.xml
new file mode 100644
index 0000000..6c07ec1
--- /dev/null
+++ b/core/res/res/layout/notification_2025_action_list.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/actions_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="bottom"
+    android:layout_marginBottom="@dimen/notification_2025_action_list_margin_bottom"
+    android:minHeight="@dimen/notification_2025_action_list_min_height"
+    >
+
+    <LinearLayout
+        android:id="@+id/actions_container_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="end"
+        android:layout_gravity="bottom"
+        android:orientation="horizontal"
+        android:background="@color/notification_action_list_background_color"
+        >
+
+        <com.android.internal.widget.NotificationActionListLayout
+            android:id="@+id/actions"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:minHeight="@dimen/notification_2025_action_list_height"
+            android:orientation="horizontal"
+            android:gravity="center_vertical"
+            android:visibility="gone"
+            >
+            <!-- actions will be added here -->
+        </com.android.internal.widget.NotificationActionListLayout>
+
+        <!--
+        This nested linear layout exists to ensure that if the neither of the contained
+        actions is visible we have some minimum padding at the end of the actions is present,
+        then there will be 12dp of padding at the end of the actions list.
+
+        The end padding exists to match the bottom margin of the actions, for symmetry when the icon
+        is shown in the corner of the notification.
+        -->
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:orientation="horizontal"
+            android:paddingEnd="@dimen/notification_2025_action_list_margin_bottom"
+            android:minWidth="@dimen/snooze_and_bubble_gone_padding_end"
+            >
+            <ImageView
+                android:id="@+id/snooze_button"
+                android:layout_width="@dimen/notification_2025_actions_icon_size"
+                android:layout_height="@dimen/notification_2025_actions_icon_size"
+                android:layout_gravity="center_vertical|end"
+                android:visibility="gone"
+                android:scaleType="centerInside"
+                />
+
+            <ImageView
+                android:id="@+id/bubble_button"
+                android:layout_width="@dimen/notification_2025_actions_icon_size"
+                android:layout_height="@dimen/notification_2025_actions_icon_size"
+                android:layout_gravity="center_vertical|end"
+                android:visibility="gone"
+                android:scaleType="centerInside"
+                />
+        </LinearLayout>
+    </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_conversation_icon_container.xml b/core/res/res/layout/notification_2025_conversation_icon_container.xml
index 7ec2450..16c9500 100644
--- a/core/res/res/layout/notification_2025_conversation_icon_container.xml
+++ b/core/res/res/layout/notification_2025_conversation_icon_container.xml
@@ -29,7 +29,8 @@
     <FrameLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_margin="@dimen/notification_2025_margin"
+        android:layout_marginHorizontal="@dimen/notification_2025_margin"
+        android:layout_marginTop="@dimen/notification_2025_margin"
         android:clipChildren="false"
         android:clipToPadding="false"
         android:layout_gravity="top|center_horizontal"
diff --git a/core/res/res/layout/notification_2025_expand_button.xml b/core/res/res/layout/notification_2025_expand_button.xml
index 1c36754..8ba844a 100644
--- a/core/res/res/layout/notification_2025_expand_button.xml
+++ b/core/res/res/layout/notification_2025_expand_button.xml
@@ -15,6 +15,7 @@
   -->
 
 <!-- extends FrameLayout -->
+<!-- Note: The button's padding may be dynamically adjusted in code -->
 <com.android.internal.widget.NotificationExpandButton
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/expand_button"
diff --git a/core/res/res/layout/notification_2025_messaging_group.xml b/core/res/res/layout/notification_2025_messaging_group.xml
index ecaf0b9..ba0ce7b 100644
--- a/core/res/res/layout/notification_2025_messaging_group.xml
+++ b/core/res/res/layout/notification_2025_messaging_group.xml
@@ -53,7 +53,6 @@
             android:id="@+id/group_message_container"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/notification_text_margin_top"
             android:spacing="2dp" />
     </com.android.internal.widget.RemeasuringLinearLayout>
     <FrameLayout
diff --git a/core/res/res/layout/notification_2025_reply_history_container.xml b/core/res/res/layout/notification_2025_reply_history_container.xml
index 6923b59..256f7b8 100644
--- a/core/res/res/layout/notification_2025_reply_history_container.xml
+++ b/core/res/res/layout/notification_2025_reply_history_container.xml
@@ -28,16 +28,16 @@
             android:layout_width="match_parent"
             android:layout_height="1dip"
             android:id="@+id/action_divider"
-            android:layout_marginTop="@dimen/notification_content_margin"
-            android:layout_marginBottom="@dimen/notification_content_margin"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginTop="@dimen/notification_2025_margin"
+            android:layout_marginBottom="@dimen/notification_2025_margin"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:background="@drawable/notification_template_divider" />
 
     <TextView
             android:id="@+id/notification_material_reply_text_3"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:visibility="gone"
             android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
             android:singleLine="true" />
@@ -46,7 +46,7 @@
             android:id="@+id/notification_material_reply_text_2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:visibility="gone"
             android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
             android:singleLine="true" />
@@ -56,13 +56,13 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
-            android:layout_marginEnd="@dimen/notification_content_margin_end">
+            android:layout_marginEnd="@dimen/notification_2025_margin">
         <TextView
                 android:id="@+id/notification_material_reply_text_1"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_weight="1"
-                android:layout_marginEnd="@dimen/notification_content_margin_end"
+                android:layout_marginEnd="@dimen/notification_2025_margin"
                 android:layout_gravity="center"
                 android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply"
                 android:singleLine="true" />
diff --git a/core/res/res/layout/notification_2025_right_icon.xml b/core/res/res/layout/notification_2025_right_icon.xml
new file mode 100644
index 0000000..24d381d
--- /dev/null
+++ b/core/res/res/layout/notification_2025_right_icon.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<!-- Large icon to be used in expanded notification layouts. -->
+<com.android.internal.widget.CachingIconView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/right_icon"
+    android:layout_width="@dimen/notification_right_icon_size"
+    android:layout_height="@dimen/notification_right_icon_size"
+    android:layout_gravity="top|end"
+    android:layout_marginEnd="@dimen/notification_2025_right_icon_expanded_margin_end"
+    android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin"
+    android:background="@drawable/notification_large_icon_outline"
+    android:clipToOutline="true"
+    android:importantForAccessibility="no"
+    android:scaleType="centerCrop"
+    android:maxDrawableWidth="@dimen/notification_right_icon_size"
+    android:maxDrawableHeight="@dimen/notification_right_icon_size"
+    />
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index d29b7af..57c89b9 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -66,14 +66,16 @@
         android:orientation="horizontal"
         >
 
+        <!--
+        We use a smaller vertical margin than usual, to allow the content of custom views to
+        grow up to 48dp height when needed in collapsed notifications.
+        -->
         <LinearLayout
             android:id="@+id/notification_headerless_view_column"
             android:layout_width="0px"
             android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
             android:layout_weight="1"
-            android:layout_marginBottom="@dimen/notification_2025_margin"
-            android:layout_marginTop="@dimen/notification_2025_margin"
+            android:layout_marginVertical="@dimen/notification_2025_reduced_margin"
             android:orientation="vertical"
             >
 
@@ -81,6 +83,7 @@
                 android:id="@+id/notification_top_line"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/notification_2025_additional_margin"
                 android:minHeight="@dimen/notification_2025_content_min_height"
                 android:clipChildren="false"
                 android:theme="@style/Theme.DeviceDefault.Notification"
@@ -118,17 +121,10 @@
                 <com.android.internal.widget.NotificationVanishingFrameLayout
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:layout_marginBottom="@dimen/notification_2025_additional_margin"
                     android:minHeight="@dimen/notification_headerless_line_height"
                     >
-                    <!-- This is the simplest way to keep this text vertically centered without
-                     gravity="center_vertical" which causes jumpiness in expansion animations. -->
-                    <include
-                        layout="@layout/notification_2025_text"
-                        android:layout_width="match_parent"
-                        android:layout_height="@dimen/notification_text_height"
-                        android:layout_gravity="center_vertical"
-                        android:layout_marginTop="0dp"
-                        />
+                    <include layout="@layout/notification_2025_text" />
                 </com.android.internal.widget.NotificationVanishingFrameLayout>
 
                 <include
@@ -146,9 +142,8 @@
             android:layout_width="@dimen/notification_right_icon_size"
             android:layout_height="@dimen/notification_right_icon_size"
             android:layout_gravity="center_vertical|end"
-            android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
-            android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
-            android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+            android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin"
+            android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
             android:background="@drawable/notification_large_icon_outline"
             android:clipToOutline="true"
             android:importantForAccessibility="no"
@@ -161,7 +156,7 @@
             android:id="@+id/expand_button_touch_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:minWidth="@dimen/notification_content_margin_end"
+            android:minWidth="@dimen/notification_2025_margin"
             >
 
             <include layout="@layout/notification_2025_expand_button"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
index ee691e4..c57196e 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_call.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -32,11 +32,11 @@
         android:orientation="vertical"
         >
 
-        <com.android.internal.widget.NotificationMaxHeightFrameLayout
+        <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="@dimen/notification_2025_min_height"
             android:clipChildren="false"
+            android:layout_weight="1"
             >
 
             <ImageView
@@ -77,9 +77,7 @@
                     android:id="@+id/notification_headerless_view_column"
                     android:layout_width="0px"
                     android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical"
                     android:layout_weight="1"
-                    android:layout_marginBottom="@dimen/notification_2025_margin"
                     android:layout_marginTop="@dimen/notification_2025_margin"
                     android:clipChildren="false"
                     android:orientation="vertical"
@@ -128,15 +126,7 @@
                             android:layout_height="wrap_content"
                             android:minHeight="@dimen/notification_headerless_line_height"
                             >
-                            <!-- This is the simplest way to keep this text vertically centered without
-                             gravity="center_vertical" which causes jumpiness in expansion animations. -->
-                            <include
-                                layout="@layout/notification_2025_text"
-                                android:layout_width="match_parent"
-                                android:layout_height="@dimen/notification_text_height"
-                                android:layout_gravity="center_vertical"
-                                android:layout_marginTop="0dp"
-                                />
+                            <include layout="@layout/notification_2025_text" />
                         </com.android.internal.widget.NotificationVanishingFrameLayout>
                     </LinearLayout>
 
@@ -147,9 +137,8 @@
                     android:layout_width="@dimen/notification_right_icon_size"
                     android:layout_height="@dimen/notification_right_icon_size"
                     android:layout_gravity="center_vertical|end"
-                    android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
-                    android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
-                    android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+                    android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin"
+                    android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
                     android:background="@drawable/notification_large_icon_outline"
                     android:clipToOutline="true"
                     android:importantForAccessibility="no"
@@ -160,7 +149,7 @@
                     android:id="@+id/expand_button_touch_container"
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
-                    android:minWidth="@dimen/notification_content_margin_end"
+                    android:minWidth="@dimen/notification_2025_margin"
                     >
 
                     <include layout="@layout/notification_2025_expand_button"
@@ -179,23 +168,15 @@
                 android:layout_height="@dimen/notification_close_button_size"
                 android:layout_gravity="top|end" />
 
-        </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+        </FrameLayout>
 
-        <LinearLayout
-            android:id="@+id/notification_action_list_margin_target"
+        <include layout="@layout/notification_template_smart_reply_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="-20dp"
-            android:clipChildren="false"
-            android:orientation="vertical">
-            <include layout="@layout/notification_template_smart_reply_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/notification_content_margin"
-                android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end" />
-            <include layout="@layout/notification_material_action_list" />
-        </LinearLayout>
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+            android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+            android:layout_marginEnd="@dimen/notification_2025_margin" />
+        <include layout="@layout/notification_2025_action_list" />
     </LinearLayout>
 
 </com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
index f804111..1c6fcb5 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
@@ -32,12 +32,11 @@
         android:orientation="vertical"
         >
 
-
-        <com.android.internal.widget.NotificationMaxHeightFrameLayout
+        <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="@dimen/notification_2025_min_height"
             android:clipChildren="false"
+            android:layout_weight="1"
             >
 
             <ImageView
@@ -78,17 +77,11 @@
                 android:clipChildren="false"
                 >
 
-                <!--
-                  NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
-                  have the id/notification_headerless_view_column, as that is used for modifying
-                   vertical margins to accommodate the single-line state that base supports
-                  -->
                 <LinearLayout
+                    android:id="@+id/notification_headerless_view_column"
                     android:layout_width="0px"
                     android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical"
                     android:layout_weight="1"
-                    android:layout_marginBottom="@dimen/notification_2025_margin"
                     android:layout_marginTop="@dimen/notification_2025_margin"
                     android:layout_marginStart="@dimen/notification_2025_content_margin_start"
                     android:clipChildren="false"
@@ -149,9 +142,8 @@
                     android:layout_width="@dimen/notification_right_icon_size"
                     android:layout_height="@dimen/notification_right_icon_size"
                     android:layout_gravity="center_vertical|end"
-                    android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
-                    android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
-                    android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+                    android:layout_marginTop="@dimen/notification_2025_margin"
+                    android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
                     android:forceHasOverlappingRendering="false"
                     android:spacing="0dp"
                     android:clipChildren="false"
@@ -163,9 +155,8 @@
                     android:layout_width="@dimen/notification_right_icon_size"
                     android:layout_height="@dimen/notification_right_icon_size"
                     android:layout_gravity="center_vertical|end"
-                    android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
-                    android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
-                    android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+                    android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin"
+                    android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
                     android:background="@drawable/notification_large_icon_outline"
                     android:clipToOutline="true"
                     android:importantForAccessibility="no"
@@ -176,7 +167,7 @@
                     android:id="@+id/expand_button_touch_container"
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
-                    android:minWidth="@dimen/notification_content_margin_end"
+                    android:minWidth="@dimen/notification_2025_margin"
                     >
 
                     <include layout="@layout/notification_2025_expand_button"
@@ -195,22 +186,15 @@
                 android:layout_height="@dimen/notification_close_button_size"
                 android:layout_gravity="top|end" />
 
-        </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+        </FrameLayout>
 
-    <LinearLayout
-            android:id="@+id/notification_action_list_margin_target"
+        <include layout="@layout/notification_template_smart_reply_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="-20dp"
-            android:clipChildren="false"
-            android:orientation="vertical">
-        <include layout="@layout/notification_template_smart_reply_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/notification_content_margin"
-                android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end" />
-        <include layout="@layout/notification_material_action_list" />
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+            android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+            android:layout_marginEnd="@dimen/notification_2025_margin" />
+        <include layout="@layout/notification_2025_action_list" />
+
     </LinearLayout>
-</LinearLayout>
 </com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 5beab50..de82f9f 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -68,14 +68,16 @@
         android:orientation="horizontal"
         >
 
+        <!--
+        We use a smaller vertical margin than usual, to allow the content of custom views to
+        grow up to 48dp height when needed in collapsed notifications.
+        -->
         <LinearLayout
             android:id="@+id/notification_headerless_view_column"
             android:layout_width="0px"
             android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
             android:layout_weight="1"
-            android:layout_marginBottom="@dimen/notification_2025_margin"
-            android:layout_marginTop="@dimen/notification_2025_margin"
+            android:layout_marginVertical="@dimen/notification_2025_reduced_margin"
             android:orientation="vertical"
             >
 
@@ -83,6 +85,7 @@
                 android:id="@+id/notification_top_line"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/notification_2025_additional_margin"
                 android:minHeight="@dimen/notification_headerless_line_height"
                 android:clipChildren="false"
                 android:theme="@style/Theme.DeviceDefault.Notification"
@@ -119,17 +122,10 @@
 
                 <com.android.internal.widget.NotificationVanishingFrameLayout
                     android:layout_width="match_parent"
+                    android:layout_marginBottom="@dimen/notification_2025_additional_margin"
                     android:layout_height="@dimen/notification_headerless_line_height"
                     >
-                    <!-- This is the simplest way to keep this text vertically centered without
-                     gravity="center_vertical" which causes jumpiness in expansion animations. -->
-                    <include
-                        layout="@layout/notification_template_text"
-                        android:layout_width="match_parent"
-                        android:layout_height="@dimen/notification_text_height"
-                        android:layout_gravity="center_vertical"
-                        android:layout_marginTop="0dp"
-                        />
+                    <include layout="@layout/notification_2025_text" />
                 </com.android.internal.widget.NotificationVanishingFrameLayout>
 
                 <include
@@ -147,9 +143,8 @@
             android:layout_width="@dimen/notification_right_icon_size"
             android:layout_height="@dimen/notification_right_icon_size"
             android:layout_gravity="center_vertical|end"
-            android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
-            android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
-            android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+            android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin"
+            android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
             android:background="@drawable/notification_large_icon_outline"
             android:clipToOutline="true"
             android:importantForAccessibility="no"
@@ -182,7 +177,7 @@
             android:id="@+id/expand_button_touch_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:minWidth="@dimen/notification_content_margin_end"
+            android:minWidth="@dimen/notification_2025_margin"
             >
 
             <include layout="@layout/notification_2025_expand_button"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index d7c3263..8e2cb23 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -35,11 +35,11 @@
         >
 
 
-        <com.android.internal.widget.NotificationMaxHeightFrameLayout
+        <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="@dimen/notification_2025_min_height"
             android:clipChildren="false"
+            android:layout_weight="1"
             >
 
             <ImageView
@@ -60,7 +60,8 @@
                 android:layout_width="@dimen/notification_2025_icon_circle_size"
                 android:layout_height="@dimen/notification_2025_icon_circle_size"
                 android:layout_alignParentStart="true"
-                android:layout_margin="@dimen/notification_2025_margin"
+                android:layout_marginHorizontal="@dimen/notification_2025_margin"
+                android:layout_marginTop="@dimen/notification_2025_margin"
                 android:background="@drawable/notification_icon_circle"
                 android:padding="@dimen/notification_2025_icon_circle_padding"
                 />
@@ -88,17 +89,11 @@
                 android:clipChildren="false"
                 >
 
-                <!--
-                  NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
-                  have the id/notification_headerless_view_column, as that is used for modifying
-                   vertical margins to accommodate the single-line state that base supports
-                  -->
                 <LinearLayout
+                    android:id="@+id/notification_headerless_view_column"
                     android:layout_width="0px"
                     android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical"
                     android:layout_weight="1"
-                    android:layout_marginBottom="@dimen/notification_2025_margin"
                     android:layout_marginTop="@dimen/notification_2025_margin"
                     android:layout_marginStart="@dimen/notification_2025_content_margin_start"
                     android:clipChildren="false"
@@ -159,9 +154,8 @@
                     android:layout_width="@dimen/notification_right_icon_size"
                     android:layout_height="@dimen/notification_right_icon_size"
                     android:layout_gravity="center_vertical|end"
-                    android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
-                    android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
-                    android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+                    android:layout_marginTop="@dimen/notification_2025_margin"
+                    android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
                     android:forceHasOverlappingRendering="false"
                     android:spacing="0dp"
                     android:clipChildren="false"
@@ -173,9 +167,8 @@
                     android:layout_width="@dimen/notification_right_icon_size"
                     android:layout_height="@dimen/notification_right_icon_size"
                     android:layout_gravity="center_vertical|end"
-                    android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
-                    android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
-                    android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+                    android:layout_marginVertical="@dimen/notification_2025_right_icon_vertical_margin"
+                    android:layout_marginStart="@dimen/notification_2025_right_icon_content_margin"
                     android:background="@drawable/notification_large_icon_outline"
                     android:clipToOutline="true"
                     android:importantForAccessibility="no"
@@ -186,7 +179,7 @@
                     android:id="@+id/expand_button_touch_container"
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
-                    android:minWidth="@dimen/notification_content_margin_end"
+                    android:minWidth="@dimen/notification_2025_margin"
                     >
 
                     <include layout="@layout/notification_2025_expand_button"
@@ -205,22 +198,15 @@
                 android:layout_height="@dimen/notification_close_button_size"
                 android:layout_gravity="top|end" />
 
-        </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+        </FrameLayout>
 
-    <LinearLayout
-            android:id="@+id/notification_action_list_margin_target"
+        <include layout="@layout/notification_template_smart_reply_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="-20dp"
-            android:clipChildren="false"
-            android:orientation="vertical">
-        <include layout="@layout/notification_template_smart_reply_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/notification_content_margin"
-                android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end" />
-        <include layout="@layout/notification_material_action_list" />
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
+            android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+            android:layout_marginEnd="@dimen/notification_2025_margin" />
+        <include layout="@layout/notification_2025_action_list" />
+
     </LinearLayout>
-</LinearLayout>
 </com.android.internal.widget.MessagingLayout>
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
index 52bc7b8..b32a778 100644
--- a/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
@@ -76,7 +76,7 @@
             android:id="@+id/expand_button_touch_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:minWidth="@dimen/notification_content_margin_end"
+            android:minWidth="@dimen/notification_2025_margin"
             >
             <include layout="@layout/notification_2025_expand_button"
                 android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
index cf9ff6b..268396f 100644
--- a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
@@ -96,14 +96,14 @@
         <FrameLayout
             android:id="@+id/reply_action_container"
             android:layout_width="wrap_content"
-            android:layout_height="@dimen/notification_action_list_height"
+            android:layout_height="@dimen/notification_2025_action_list_height"
             android:gravity="center_vertical"
             android:orientation="horizontal" />
         <FrameLayout
             android:id="@+id/expand_button_touch_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:minWidth="@dimen/notification_content_margin_end"
+            android:minWidth="@dimen/notification_2025_margin"
             >
             <include layout="@layout/notification_2025_expand_button"
                 android:layout_width="wrap_content"
diff --git a/core/res/res/layout/notification_2025_template_expanded_base.xml b/core/res/res/layout/notification_2025_template_expanded_base.xml
index e12db27..58b26c9 100644
--- a/core/res/res/layout/notification_2025_template_expanded_base.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_base.xml
@@ -24,10 +24,8 @@
     >
 
     <LinearLayout
-        android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/notification_content_margin"
         android:orientation="vertical"
         >
 
@@ -47,11 +45,11 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end"
+                android:layout_marginEnd="@dimen/notification_2025_margin"
                 android:orientation="vertical"
                 >
 
-                <include layout="@layout/notification_template_part_line1" />
+                <include layout="@layout/notification_2025_title" />
 
                 <include layout="@layout/notification_template_text_multiline" />
 
@@ -63,7 +61,7 @@
                     />
             </LinearLayout>
 
-            <include layout="@layout/notification_template_right_icon" />
+            <include layout="@layout/notification_2025_right_icon" />
         </FrameLayout>
 
         <ViewStub
@@ -78,10 +76,10 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
-            android:layout_marginTop="@dimen/notification_content_margin"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
             />
 
-        <include layout="@layout/notification_material_action_list" />
+        <include layout="@layout/notification_2025_action_list" />
     </LinearLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
index fac9d1c4..f6a17ef 100644
--- a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
@@ -25,10 +25,9 @@
 
     <include layout="@layout/notification_2025_template_header" />
 
-    <include layout="@layout/notification_template_right_icon" />
+    <include layout="@layout/notification_2025_right_icon" />
 
     <LinearLayout
-        android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="top"
@@ -43,11 +42,11 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:orientation="vertical"
             >
 
-            <include layout="@layout/notification_template_part_line1" />
+            <include layout="@layout/notification_2025_title" />
 
             <include
                 layout="@layout/notification_template_progress"
@@ -67,7 +66,7 @@
             android:layout_weight="1"
             android:layout_marginTop="13dp"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:background="@drawable/notification_big_picture_outline"
             android:clipToOutline="true"
             android:scaleType="centerCrop"
@@ -85,10 +84,10 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
-            android:layout_marginTop="@dimen/notification_content_margin"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
             />
 
-        <include layout="@layout/notification_material_action_list" />
+        <include layout="@layout/notification_2025_action_list" />
     </LinearLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_text.xml b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
index 4a807cb..540444e 100644
--- a/core/res/res/layout/notification_2025_template_expanded_big_text.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
@@ -26,11 +26,9 @@
     <include layout="@layout/notification_2025_template_header" />
 
     <com.android.internal.widget.RemeasuringLinearLayout
-        android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
-        android:layout_marginBottom="@dimen/notification_content_margin"
         android:clipToPadding="false"
         android:orientation="vertical"
         >
@@ -43,13 +41,13 @@
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:paddingStart="@dimen/notification_2025_content_margin_start"
-            android:paddingEnd="@dimen/notification_content_margin_end"
+            android:paddingEnd="@dimen/notification_2025_margin"
             android:clipToPadding="false"
             android:orientation="vertical"
             android:layout_weight="1"
             >
 
-            <include layout="@layout/notification_template_part_line1" />
+            <include layout="@layout/notification_2025_title" />
 
             <include
                 layout="@layout/notification_template_progress"
@@ -84,12 +82,12 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
-            android:layout_marginTop="@dimen/notification_content_margin"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
             />
 
-        <include layout="@layout/notification_material_action_list" />
+        <include layout="@layout/notification_2025_action_list" />
     </com.android.internal.widget.RemeasuringLinearLayout>
 
-    <include layout="@layout/notification_template_right_icon" />
+    <include layout="@layout/notification_2025_right_icon" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml
index bbc2966..60b3200 100644
--- a/core/res/res/layout/notification_2025_template_expanded_call.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_call.xml
@@ -30,7 +30,6 @@
     <include layout="@layout/notification_2025_conversation_header"/>
 
     <com.android.internal.widget.RemeasuringLinearLayout
-        android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
@@ -46,12 +45,12 @@
             android:layout_gravity="top"
             android:layout_weight="1"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:orientation="vertical"
             android:clipChildren="false"
             >
 
-            <include layout="@layout/notification_template_part_line1"/>
+            <include layout="@layout/notification_2025_title"/>
 
             <include layout="@layout/notification_template_text_multiline" />
 
@@ -60,14 +59,14 @@
         <include layout="@layout/notification_template_smart_reply_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/notification_content_margin"
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end" />
+            android:layout_marginEnd="@dimen/notification_2025_margin" />
 
-        <include layout="@layout/notification_material_action_list" />
+        <include layout="@layout/notification_2025_action_list" />
 
     </com.android.internal.widget.RemeasuringLinearLayout>
 
-    <include layout="@layout/notification_template_right_icon" />
+    <include layout="@layout/notification_2025_right_icon" />
 
 </com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_conversation.xml b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
index d7e8bb3..e085d47 100644
--- a/core/res/res/layout/notification_2025_template_expanded_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
@@ -29,7 +29,6 @@
     <include layout="@layout/notification_2025_conversation_header"/>
 
     <com.android.internal.widget.RemeasuringLinearLayout
-            android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="top"
@@ -44,32 +43,33 @@
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:layout_weight="1"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:orientation="vertical"
             android:clipChildren="false"
             >
 
-            <include layout="@layout/notification_template_part_line1"/>
+            <include layout="@layout/notification_2025_title"/>
 
             <com.android.internal.widget.MessagingLinearLayout
                 android:id="@+id/notification_messaging"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/notification_2025_margin"
                 android:clipChildren="false"
-                android:spacing="@dimen/notification_messaging_spacing" />
+                android:spacing="@dimen/notification_2025_messaging_spacing" />
         </com.android.internal.widget.RemeasuringLinearLayout>
 
         <include layout="@layout/notification_template_smart_reply_container"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/notification_content_margin"
+                android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
                 android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end" />
+                android:layout_marginEnd="@dimen/notification_2025_margin" />
 
-        <include layout="@layout/notification_material_action_list" />
+        <include layout="@layout/notification_2025_action_list" />
 
     </com.android.internal.widget.RemeasuringLinearLayout>
 
-    <include layout="@layout/notification_template_right_icon" />
+    <include layout="@layout/notification_2025_right_icon" />
 
 </com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_inbox.xml b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
index ccab02e..55253d0 100644
--- a/core/res/res/layout/notification_2025_template_expanded_inbox.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
@@ -24,7 +24,6 @@
     >
     <include layout="@layout/notification_2025_template_header" />
     <LinearLayout
-            android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_gravity="top"
@@ -39,12 +38,12 @@
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:paddingStart="@dimen/notification_2025_content_margin_start"
-            android:paddingEnd="@dimen/notification_content_margin_end"
+            android:paddingEnd="@dimen/notification_2025_margin"
             android:layout_weight="1"
             android:clipToPadding="false"
             android:orientation="vertical"
             >
-            <include layout="@layout/notification_template_part_line1"
+            <include layout="@layout/notification_2025_title"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content" />
             <include layout="@layout/notification_template_progress"
@@ -126,9 +125,9 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
-            android:layout_marginTop="@dimen/notification_content_margin" />
-        <include layout="@layout/notification_material_action_list" />
+            android:layout_marginEnd="@dimen/notification_2025_margin"
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin" />
+        <include layout="@layout/notification_2025_action_list" />
     </LinearLayout>
-    <include layout="@layout/notification_template_right_icon" />
+    <include layout="@layout/notification_2025_right_icon" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_media.xml b/core/res/res/layout/notification_2025_template_expanded_media.xml
index e90ab79..5e97607 100644
--- a/core/res/res/layout/notification_2025_template_expanded_media.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_media.xml
@@ -41,10 +41,10 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:orientation="vertical"
             >
-            <include layout="@layout/notification_template_part_line1"/>
+            <include layout="@layout/notification_2025_title"/>
             <include layout="@layout/notification_template_text"/>
         </LinearLayout>
 
@@ -53,7 +53,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_media_actions_margin_start"
-            android:minHeight="@dimen/notification_content_margin"
+            android:minHeight="@dimen/notification_2025_margin"
             >
 
             <!-- Nesting in FrameLayout is required to ensure that the marginStart actually applies
@@ -99,6 +99,6 @@
 
     </LinearLayout>
 
-    <include layout="@layout/notification_template_right_icon" />
+    <include layout="@layout/notification_2025_right_icon" />
 
 </com.android.internal.widget.MediaNotificationView>
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 20abfee..14ed536 100644
--- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
@@ -29,7 +29,6 @@
     <include layout="@layout/notification_2025_template_header"/>
 
     <com.android.internal.widget.RemeasuringLinearLayout
-            android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="top"
@@ -44,32 +43,33 @@
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:layout_weight="1"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
             android:orientation="vertical"
             android:clipChildren="false"
             >
 
-            <include layout="@layout/notification_template_part_line1"/>
+            <include layout="@layout/notification_2025_title"/>
 
             <com.android.internal.widget.MessagingLinearLayout
                 android:id="@+id/notification_messaging"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/notification_2025_margin"
                 android:clipChildren="false"
-                android:spacing="@dimen/notification_messaging_spacing" />
+                android:spacing="@dimen/notification_2025_messaging_spacing" />
         </com.android.internal.widget.RemeasuringLinearLayout>
 
         <include layout="@layout/notification_template_smart_reply_container"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/notification_content_margin"
+                android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
                 android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end" />
+                android:layout_marginEnd="@dimen/notification_2025_margin" />
 
-        <include layout="@layout/notification_material_action_list" />
+        <include layout="@layout/notification_2025_action_list" />
 
     </com.android.internal.widget.RemeasuringLinearLayout>
 
-    <include layout="@layout/notification_template_right_icon" />
+    <include layout="@layout/notification_2025_right_icon" />
 
 </com.android.internal.widget.MessagingLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_progress.xml b/core/res/res/layout/notification_2025_template_expanded_progress.xml
index 87ded89..1315481 100644
--- a/core/res/res/layout/notification_2025_template_expanded_progress.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_progress.xml
@@ -25,10 +25,8 @@
     >
 
     <LinearLayout
-        android:id="@+id/notification_action_list_margin_target"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/notification_content_margin"
         android:orientation="vertical"
         >
 
@@ -48,11 +46,11 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end"
+                android:layout_marginEnd="@dimen/notification_2025_margin"
                 android:orientation="vertical"
                 >
 
-                <include layout="@layout/notification_template_part_line1" />
+                <include layout="@layout/notification_2025_title" />
 
                 <include layout="@layout/notification_template_text_multiline" />
 
@@ -99,7 +97,7 @@
                 </LinearLayout>
             </LinearLayout>
 
-            <include layout="@layout/notification_template_right_icon" />
+            <include layout="@layout/notification_2025_right_icon" />
         </FrameLayout>
 
         <ViewStub
@@ -114,10 +112,10 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
-            android:layout_marginTop="@dimen/notification_content_margin"
+            android:layout_marginEnd="@dimen/notification_2025_margin"
+            android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
             />
 
-        <include layout="@layout/notification_material_action_list" />
+        <include layout="@layout/notification_2025_action_list" />
     </LinearLayout>
 </FrameLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_2025_template_heads_up_base.xml b/core/res/res/layout/notification_2025_template_heads_up_base.xml
index 084ec7d..e416c50 100644
--- a/core/res/res/layout/notification_2025_template_heads_up_base.xml
+++ b/core/res/res/layout/notification_2025_template_heads_up_base.xml
@@ -56,11 +56,11 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginStart="@dimen/notification_2025_content_margin_start"
-                android:layout_marginEnd="@dimen/notification_content_margin_end"
-                android:layout_marginTop="@dimen/notification_content_margin"
+                android:layout_marginEnd="@dimen/notification_2025_margin"
+                android:layout_marginTop="@dimen/notification_2025_smart_reply_container_margin"
                 />
 
-            <include layout="@layout/notification_material_action_list" />
+            <include layout="@layout/notification_2025_action_list" />
         </LinearLayout>
     </LinearLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_2025_text.xml b/core/res/res/layout/notification_2025_text.xml
index 474f6d2..a725de4 100644
--- a/core/res/res/layout/notification_2025_text.xml
+++ b/core/res/res/layout/notification_2025_text.xml
@@ -13,14 +13,16 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
+<!-- Note that we prefer layout_gravity="center_vertical" over gravity="center_vertical", since the
+ latter can cause jumpiness in expansion animations. -->
 <com.android.internal.widget.ImageFloatingTextView
     xmlns:android="http://schemas.android.com/apk/res/android"
     style="@style/Widget.DeviceDefault.Notification.Text"
     android:id="@+id/text"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/notification_text_height"
-    android:layout_gravity="top"
-    android:layout_marginTop="@dimen/notification_text_margin_top"
+    android:layout_height="wrap_content"
+    android:minHeight="@dimen/notification_text_height"
+    android:layout_gravity="center_vertical"
     android:ellipsize="end"
     android:fadingEdge="horizontal"
     android:gravity="top"
diff --git a/core/res/res/layout/notification_2025_title.xml b/core/res/res/layout/notification_2025_title.xml
new file mode 100644
index 0000000..7cea5ea
--- /dev/null
+++ b/core/res/res/layout/notification_2025_title.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/title"
+    android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:maxLines="2"
+    android:ellipsize="end"
+    android:textAlignment="viewStart"
+    />
diff --git a/core/res/res/layout/notification_close_button.xml b/core/res/res/layout/notification_close_button.xml
index 5eff84e..9656d39 100644
--- a/core/res/res/layout/notification_close_button.xml
+++ b/core/res/res/layout/notification_close_button.xml
@@ -17,13 +17,14 @@
 <com.android.internal.widget.NotificationCloseButton
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/close_button"
+    android:background="@drawable/close_button_bg"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="top|end"
     android:contentDescription="@string/close_button_text"
     android:visibility="gone"
     android:src="@drawable/notification_close_button_icon"
-    android:padding="2dp"
+    android:padding="@dimen/notification_close_button_padding"
     android:scaleType="fitCenter"
     android:importantForAccessibility="no"
     >
diff --git a/core/res/res/layout/notification_template_notification_progress_bar.xml b/core/res/res/layout/notification_template_notification_progress_bar.xml
index 3574896..8511d38 100644
--- a/core/res/res/layout/notification_template_notification_progress_bar.xml
+++ b/core/res/res/layout/notification_template_notification_progress_bar.xml
@@ -20,4 +20,5 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/notification_progress_tracker_height"
     style="@style/Widget.Material.Notification.NotificationProgressBar"
+    android:accessibilityLiveRegion="polite"
     />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index ae05666..568468b5 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Eenhandmodus"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra donker"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Gehoortoestelle"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Outoklik"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Ontkoppel"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Gekoppel"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktief"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Vra ontsluitpatroon voordat jy ontspeld"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Vra wagwoord voordat jy ontspeld"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Deur jou admin geïnstalleer.\nGaan na instellings om toegestaande toestemmings te sien"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Opgedateer deur jou administrateur"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Deur jou admin opgedateer.\nGaan na instellings om toegestaande toestemmings te sien"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Uitgevee deur jou administrateur"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Batterybespaarder skakel Donkertema aan en beperk of skakel agtergrondaktiwiteit, sommige visuele effekte, sekere kenmerke en sommige netwerkverbindings af"</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rollees"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Onderbreek"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisie"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> is in die BEPERK-groep geplaas"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"het \'n prent gestuur"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Toets"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Gemeenskaplik"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Werkprofiel"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaat ruimte"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 19be04e..20d5336 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"የአንድ እጅ ሁነታ"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ተጨማሪ ደብዛዛ"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"የመስሚያ መሣሪያዎች"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"ራስ-ሰር ጠቅ ማድረግ"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"ግንኙነት ተቋርጧል"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"ተገናኝቷል"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"ገቢር"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ከመንቀል በፊት የማስከፈቻ ሥርዓተ-ጥለት ጠይቅ"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ከመንቀል በፊት የይለፍ ቃል ጠይቅ"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"በአስተዳዳሪዎ ተጭኗል።\nየተፈቀዱ ፍቃዶችን ለማየት ወደ ቅንብሮች ይሂዱ"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"በእርስዎ አስተዳዳሪ ተዘምኗል"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"በአስተዳዳሪዎ ተዘምኗል።\nየተፈቀዱ ፍቃዶችን ለማየት ወደ ቅንብሮች ይሂዱ"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"በእርስዎ አስተዳዳሪ ተሰርዟል"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"እሺ"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"ባትሪ ቆጣቢ ጠቆር ያለ ገጽታን ያበራል እና የጀርባ እንቅስቃሴን፣ አንዳንድ ዕይታዊ ውጤቶችን፣ አንዳንድ ባህሪዎችን፣ እና አንዳንድ የአውታረ መረብ ግንኙነቶችን ይገድባል ወይም ያጠፋል።"</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ሸብልል"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ባለበት አቁም"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"አቀማመጥ"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ወደ የRESTRICTED ባልዲ ተከትቷል"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>፦"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"አንድ ምስል ልከዋል"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ሥራ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ሙከራ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"የጋራ"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"የሥራ መገለጫ"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"የግል ቦታ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"አባዛ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 66192cb..ffc1444 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1807,8 +1807,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"وضع \"التصفح بيد واحدة\""</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"زيادة تعتيم الشاشة"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"سماعات الأذن الطبية"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"النقر التلقائي"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"غير متّصل"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"متّصل"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"متّصل حاليًا"</string>
@@ -1970,7 +1969,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"طلب إدخال نقش فتح القفل قبل إزالة التثبيت"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"طلب إدخال كلمة المرور قبل إزالة التثبيت"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"تم التثبيت من قِبل المشرف.\nانتقِل إلى الإعدادات للاطّلاع على الأذونات الممنوحة"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"تم التحديث بواسطة المشرف"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"تم التحديث من قِبل المشرف.\nيُرجى الانتقال إلى الإعدادات للاطّلاع على الأذونات الممنوحة"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"تم الحذف بواسطة المشرف"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"حسنًا"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"يؤدي استخدام ميزة \"توفير شحن البطارية\" إلى تفعيل وضع \"المظهر الداكن\" وتقييد أو إيقاف الأنشطة في الخلفية وبعض التأثيرات المرئية وميزات معيّنة وبعض اتصالات الشبكات."</string>
@@ -2278,6 +2277,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"الانتقال للأسفل أو للأعلى"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"إيقاف مؤقت"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"تعديل الموضع"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"الانتقال للأعلى"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"الانتقال للأسفل"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"الانتقال لليمين"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"الانتقال لليسار"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"الخروج من وضع الانتقال"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"لوحة الانتقال"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"تم وضع <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> في الحزمة \"محظورة\"."</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"هذا المستخدم أرسل صورة"</string>
@@ -2485,6 +2490,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ملف العمل 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ملف شخصي تجريبي"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ملف شخصي مشترك"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ملف العمل"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"المساحة الخاصة"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"نسخة طبق الأصل"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index b85be47..5c78cde 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"এখন হাতেৰে ব্যৱহাৰ কৰাৰ ম’ড"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিৰিক্তভাৱে পোহৰ কমোৱাৰ সুবিধা"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"শুনাৰ ডিভাইচ"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"স্বয়ংক্ৰিয়ভাৱে ক্লিক কৰাৰ সুবিধা"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"সংযোগ বিচ্ছিন্ন হ’ল"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"সংযোগ কৰা হ’ল"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"সক্ৰিয় হৈ আছে"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"আনপিন কৰাৰ পূৰ্বে আনলক আৰ্হি দিবলৈ কওক"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"আনপিন কৰাৰ পূৰ্বে পাছৱৰ্ড দিবলৈ কওক"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"আপোনাৰ প্ৰশাসকে ইনষ্টল কৰিছে।\nপ্ৰদান কৰা অনুমতিসমূহ চাবলৈ ছেটিঙলৈ যাওক"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"আপোনাৰ প্ৰশাসকে আপেডট কৰিছে"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"আপোনাৰ প্ৰশাসকে আপডে’ট কৰিছে।\nপ্ৰদান কৰা অনুমতিসমূহ চাবলৈ ছেটিঙলৈ যাওক"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"আপোনাৰ প্ৰশাসকে মচিছে"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ঠিক আছে"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"বেটাৰী সঞ্চয়কাৰীয়ে গাঢ় ৰঙৰ থীম অন কৰে আৰু নেপথ্যৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল ইফেক্ট, নিৰ্দিষ্ট কিছুমান সুবিধা আৰু নেটৱৰ্কৰ সংযোগ সীমিত অথবা অফ কৰে।"</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"স্ক্ৰ’ল কৰক"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"পজ কৰক"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"স্থান"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>ক সীমাবদ্ধ বাকেটটোত ৰখা হৈছে"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"কৰ্মস্থান ৩"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"পৰীক্ষা"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"শ্বেয়াৰ কৰা"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"কৰ্মস্থানৰ প্ৰ’ফাইল"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্ৰাইভেট স্পে’চ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্ল’ন"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index b9bd543..c8b500b 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Birəlli rejim"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Əlavə tündləşmə"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Eşitmə cihazları"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Avtomatik klikləmə"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Bağlantı kəsildi"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Qoşuldu"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktivdir"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Qrafik açar istənilsin"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Ayırmadan öncə parol istənilsin"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Admin quraşdırıb.\nVerilən icazələrə baxmaq üçün ayarlara keçin"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Admin tərəfindən yeniləndi"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Admin güncəlləyib.\nTəmin edilən icazələrə baxmaq üçün ayarlara keçin"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Admin tərəfindən silindi"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Enerjiyə Qənaət rejimi Tünd temanı aktivləşdirir, habelə arxa fon fəaliyyətini, bəzi vizual effektləri, müəyyən xüsusiyyətləri və bəzi şəbəkə bağlantılarını məhdudlaşdırır, yaxud söndürür."</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Sürüşdürün"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Durdurun"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Mövqe"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Yuxarı sürüşdürün"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Aşağı sürüşdürün"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Sola sürüşdürün"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Sağa sürüşdürün"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Sürüşdürmə rejimindən çıxın"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Sürüşdürmə paneli"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> MƏHDUDLAŞDIRILMIŞ səbətinə yerləşdirilib"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"şəkil göndərdi"</string>
@@ -2481,6 +2486,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"İş 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Kommunal"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"İş profili"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Məxfi sahə"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index a558de3..d1889b8 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1966,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Traži šablon za otključavanje pre otkačinjanja"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Traži lozinku pre otkačinjanja"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instalirao je administrator.\nIdite u podešavanja da biste videli odobrene dozvole"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je administrator"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Ažurirao je administrator.\nIdite u podešavanja da biste videli odobrene dozvole"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je administrator"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Potvrdi"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Ušteda baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte, određene funkcije i neke mrežne veze."</string>
@@ -2274,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Skrolujte"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicija"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je dodat u segment OGRANIČENO"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslao/la sliku"</string>
@@ -2481,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Poslovni profil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatan prostor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonirano"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 113be9d..68fe703 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1805,8 +1805,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Рэжым кіравання адной рукой"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дадатковае памяншэнне яркасці"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слыхавыя апараты"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Аўтанацісканне"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Адключана"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Падключана"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Актыўная"</string>
@@ -1968,7 +1967,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Запытваць узор разблакіроўкі перад адмацаваннем"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Запытваць пароль перад адмацаваннем"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Усталявана адміністратарам.\nКаб паглядзець дадзеныя дазволы, перайдзіце ў налады"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Абноўлены вашым адміністратарам"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Абноўлена адміністратарам.\nКаб праглядзець дадзеныя дазволы, перайдзіце ў налады"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Выдалены вашым адміністратарам"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"У рэжыме энергазберажэння ўключаецца цёмная тэма і выключаюцца ці абмяжоўваюцца дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты, пэўныя функцыі і падключэнні да сетак."</string>
@@ -2276,6 +2275,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Гартанне"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Прыпыніць"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Пазіцыя"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" дададзены ў АБМЕЖАВАНУЮ групу"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"адпраўлены відарыс"</string>
@@ -2483,6 +2494,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Працоўны 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Тэставы"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Супольны"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Працоўны профіль"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Прыватная прастора"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index c36940f..67e711f1 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Работа с една ръка"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Допълнително затъмняване"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слухови апарати"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Автоматично кликване"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Няма връзка"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Свързано"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Активно"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Запитване за фигура за отключване преди освобождаване"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Запитване за парола преди освобождаване"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Инсталирано от администратора ви.\nОтворете настройките, за да прегледате предоставените разрешения"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Актуализирано от администратора ви"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Актуализирано от администратора ви.\nОтворете настройките, за да прегледате предоставените разрешения"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Изтрито от администратора ви"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Режимът за запазване на батерията включва тъмната тема и ограничава или изключва активността на заден план, някои визуални ефекти, определени функции и някои връзки с мрежата."</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Превъртане"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Пауза"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Позиция"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Превъртане нагоре"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Превъртане надолу"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Превъртане наляво"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Превъртане надясно"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Изход от режима за превъртане"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Панел за превъртане"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакетът <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> е поставен в ОГРАНИЧЕНИЯ контейнер"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"изпратено изображение"</string>
@@ -2481,6 +2486,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Служебни 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Тестване"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Общи"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Служебен потребителски профил"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частно пространство"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониране"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index b79c1d7..12bf5da 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"এক হাতে ব্যবহার করার মোড"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিরিক্ত কম উজ্জ্বলতা"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"হিয়ারিং ডিভাইস"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"অটোক্লিক"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"ডিসকানেক্ট হয়ে গেছে"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"কানেক্ট করা হয়েছে"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"অ্যাক্টিভ"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"আনপিন করার আগে আনলক প্যাটার্ন চান"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"আনপিন করার আগে পাসওয়ার্ড চান"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"আপনার অ্যাডমিন ইনস্টল করেছেন।\nঅনুমোদন করা অনুমতি দেখতে সেটিংসে যান"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"আপনার প্রশাসক আপডেট করেছেন"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"আপনার অ্যাডমিন আপডেট করেছেন।\nঅনুমোদন করা অনুমতি দেখতে সেটিংসে যান"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"আপনার প্রশাসক মুছে দিয়েছেন"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ঠিক আছে"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"ব্যাটারি সেভার ডার্ক থিম চালু করে এবং ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট, নির্দিষ্ট ফিচার ও কয়েকটি নেটওয়ার্ক কানেকশনের ব্যবহার সীমিত করে বা বন্ধ করে দেয়।"</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"স্ক্রল করুন"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"পজ করুন"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"পজিশন"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> সীমাবদ্ধ গ্রুপে অন্তর্ভুক্ত করা হয়েছে"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"একটি ছবি পাঠানো হয়েছে"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"৩য় অফিস"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"পরীক্ষা"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"কমিউনাল"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"অফিস প্রোফাইল"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্রাইভেট স্পেস"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্লোন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index a995746..441d514 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Način rada jednom rukom"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatno zatamnjenje"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Slušni aparati"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatski klik"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Nije povezano"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Povezano"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktivno"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Traži uzorak za otključavanje prije poništavanja kačenja"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Traži lozinku prije nego se otkači"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instalirao je vaš administrator.\nIdite u postavke da pregledate data odobrenja"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je vaš administrator"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Ažurirao je vaš administrator.\nIdite u postavke da pregledate data odobrenja"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je vaš administrator"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Uredu"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Ušteda baterije uključuje tamnu temu i ograničava ili isključuje aktivnost u pozadini, određene vizuelne efekte i funkcije te neke mrežne veze."</string>
@@ -2275,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Klizanje"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Položaj"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je stavljen u odjeljak OGRANIČENO"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslao/la sliku"</string>
@@ -2482,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"3. poslovno"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testno"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Opće"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Radni profil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 62f59f2..efde150 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode d\'una mà"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuació extra"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Audiòfons"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Clic automàtic"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Desconnectat"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Connectat"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Actiu"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Sol·licita el patró de desbloqueig per deixar de fixar"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Demana la contrasenya per deixar de fixar"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instal·lat per l\'administrador.\nVes a la configuració per veure els permisos concedits."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualitzat per l\'administrador"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Actualitzat per l\'administrador.\nVes a la configuració per veure els permisos concedits."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Suprimit per l\'administrador"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"D\'acord"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions de xarxa."</string>
@@ -2275,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desplaça"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Posa en pausa"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posició"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> s\'ha transferit al segment RESTRINGIT"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha enviat una imatge"</string>
@@ -2482,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Treball 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Prova"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Compartit"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de treball"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espai privat"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 5e7a8aa..59de937 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1805,8 +1805,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Režim jedné ruky"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Velmi tmavé zobrazení"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Naslouchátka"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatické kliknutí"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Odpojeno"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Připojeno"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktivní"</string>
@@ -1968,7 +1967,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Před uvolněním požádat o bezpečnostní gesto"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Před odepnutím požádat o heslo"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Nainstalováno administrátorem.\nUdělená oprávnění si můžete prohlédnout v nastavení."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Aktualizováno administrátorem"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Aktualizováno administrátorem.\nUdělená oprávnění si můžete prohlédnout v nastavení."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Smazáno administrátorem"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Spořič baterie zapíná tmavý motiv a omezuje či vypíná aktivitu na pozadí, některé vizuální efekty, některé funkce a připojení k některým sítím."</string>
@@ -2276,6 +2275,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Posunutí"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pozastavit"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozice"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Balíček <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> byl vložen do sekce OMEZENO"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"posílá obrázek"</string>
@@ -2483,6 +2494,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Práce 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Komunální"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Pracovní profil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Soukromý prostor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index ab73394..1038ff0 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhåndstilstand"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra dæmpet belysning"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Høreapparater"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Autoklik"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Ikke forbundet"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Forbundet"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktiv"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Bed om oplåsningsmønster ved deaktivering"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Bed om adgangskode inden frigørelse"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Installeret af din administrator.\nGå til Indstillinger for at se de tilladelser, der er blevet givet"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Opdateret af din administrator"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Opdateret af din administrator.\nGå til Indstillinger for at se de tilladelser, der er blevet givet"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Slettet af din administrator"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Batterisparefunktionen aktiverer Mørkt tema og begrænser eller deaktiverer aktivitet i baggrunden og visse visuelle effekter, funktioner og netværksforbindelser."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rul"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sæt på pause"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Placering"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> er blevet placeret i samlingen BEGRÆNSET"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sendte et billede"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Arbejde 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Fælles"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Arbejdsprofil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 8bff350..c13daca 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Einhandmodus"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extradunkel"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hörgeräte"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatischer Klick"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Nicht verbunden"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Verbunden"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktiv"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Vor dem Beenden nach Entsperrungsmuster fragen"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Vor dem Beenden nach Passwort fragen"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Von deinem Administrator installiert.\nRufe die Einstellungen auf, um gewährte Berechtigungen anzusehen."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Von deinem Administrator aktualisiert.\nRufe die Einstellungen auf, um gewährte Berechtigungen anzusehen."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Der Energiesparmodus aktiviert das dunkle Design. Hintergrundaktivitäten, einige Funktionen und optische Effekte und manche Netzwerkverbindungen werden eingeschränkt oder deaktiviert."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrollen"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausieren"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> wurde in den BESCHRÄNKT-Bucket gelegt"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"hat ein Bild gesendet"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Geschäftlich 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Gemeinsam genutzt"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Arbeitsprofil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Vertrauliches Profil"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 0e968bd..50b06df 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Λειτουργία ενός χεριού"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Επιπλέον μείωση φωτεινότητας"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Συσκευές ακοής"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Αυτόματο κλικ"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Αποσυνδέθηκε"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Συνδέθηκε"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Ενεργή"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Να γίνεται ερώτηση για το μοτίβο ξεκλειδώματος, πριν από το ξεκαρφίτσωμα"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Να γίνεται ερώτηση για τον κωδικό πρόσβασης, πριν από το ξεκαρφίτσωμα"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Εγκαταστάθηκε από τον διαχειριστή σας.\nΜεταβείτε στις ρυθμίσεις για να δείτε τις άδειες που έχουν εκχωρηθεί"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Ενημερώθηκε από τον διαχειριστή σας"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Ενημερώθηκε από τον διαχειριστή σας.\nΜεταβείτε στις ρυθμίσεις για να δείτε τις άδειες που έχουν εκχωρηθεί"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Διαγράφηκε από τον διαχειριστή σας"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Η Εξοικονόμηση μπαταρίας ενεργοποιεί το Σκούρο θέμα και περιορίζει ή απενεργοποιεί τη δραστηριότητα στο παρασκήνιο, ορισμένα οπτικά εφέ, συγκεκριμένες λειτουργίες και κάποιες συνδέσεις δικτύου."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Κύλιση"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Παύση"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Θέση"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Το πακέτο <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> τοποθετήθηκε στον κάδο ΠΕΡΙΟΡΙΣΜΕΝΗΣ ΠΡΟΣΒΑΣΗΣ."</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"έστειλε μια εικόνα"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Εργασία 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Δοκιμή"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Κοινόχρηστο"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Προφίλ εργασίας"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ιδιωτικός χώρος"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Κλώνος"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 6c43f9a..74462e9 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-handed mode"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hearing devices"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Autoclick"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Disconnected"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Connected"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Active"</string>
@@ -1821,8 +1820,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Magnification"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Switch to phone mic?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Switch to hearing aid mic?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"For better sound or if your hearing aid battery is low. This only switches your mic during the call."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"You can use your hearing aid microphone for hands-free calling. This only switches your mic during the call."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"For better sound or if your hearing-aid battery is low. This only switches your mic during the call."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"You can use your hearing-aid microphone for hands-free calling. This only switches your mic during the call."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Switch"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Settings"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Switching to <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Ask for unlock pattern before unpinning"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Ask for password before unpinning"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Installed by your admin.\nGo to Settings to view granted permissions"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Updated by your admin.\nGo to settings to view granted permissions"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, certain features and some network connections."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Work profile"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 99abe8b..eb6f281 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1965,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Ask for unlock pattern before unpinning"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Ask for password before unpinning"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Installed by your admin.\nGo to settings to view granted permissions"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Updated by your admin.\nGo to settings to view granted permissions"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, certain features, and some network connections."</string>
@@ -2273,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll Up"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scroll Down"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scroll Left"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scroll Right"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Exit Scroll Mode"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scroll Panel"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string>
@@ -2480,6 +2486,7 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+    <string name="profile_label_supervising" msgid="5649312778545745371">"Supervising"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Work profile"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 99040f3..9fa2683 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-handed mode"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hearing devices"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Autoclick"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Disconnected"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Connected"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Active"</string>
@@ -1821,8 +1820,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Magnification"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Switch to phone mic?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Switch to hearing aid mic?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"For better sound or if your hearing aid battery is low. This only switches your mic during the call."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"You can use your hearing aid microphone for hands-free calling. This only switches your mic during the call."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"For better sound or if your hearing-aid battery is low. This only switches your mic during the call."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"You can use your hearing-aid microphone for hands-free calling. This only switches your mic during the call."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Switch"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Settings"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Switching to <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Ask for unlock pattern before unpinning"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Ask for password before unpinning"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Installed by your admin.\nGo to Settings to view granted permissions"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Updated by your admin.\nGo to settings to view granted permissions"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, certain features and some network connections."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Work profile"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index cb1e92ed..e162a60 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-handed mode"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hearing devices"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Autoclick"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Disconnected"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Connected"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Active"</string>
@@ -1821,8 +1820,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Magnification"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Switch to phone mic?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Switch to hearing aid mic?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"For better sound or if your hearing aid battery is low. This only switches your mic during the call."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"You can use your hearing aid microphone for hands-free calling. This only switches your mic during the call."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"For better sound or if your hearing-aid battery is low. This only switches your mic during the call."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"You can use your hearing-aid microphone for hands-free calling. This only switches your mic during the call."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Switch"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Settings"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Switching to <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Ask for unlock pattern before unpinning"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Ask for password before unpinning"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Installed by your admin.\nGo to Settings to view granted permissions"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Updated by your admin.\nGo to settings to view granted permissions"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, certain features and some network connections."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Work profile"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 18cbfaa..3870869 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo de una mano"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuación extra"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Dispositivos auditivos"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Clic automático"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Desconectado"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Conectado"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Activo"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Solicitar desbloqueo para quitar fijación"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Solicitar contraseña para quitar fijación"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Tu administrador realizó la instalación.\nVe a la configuración para ver los permisos otorgados"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Tu administrador actualizó este paquete"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Tu administrador realizó la actualización.\nVe a la configuración para ver los permisos otorgados"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Tu administrador borró este paquete"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"El Ahorro de batería activa el Tema oscuro y desactiva o restringe la actividad en segundo plano, algunos efectos visuales, algunas conexiones de red y otras funciones determinadas."</string>
@@ -2275,6 +2274,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desplazamiento"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posición"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desplazarse hacia arriba"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Desplazarse hacia abajo"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Desplazarse hacia la izquierda"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Desplazarse hacia la derecha"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Salir del modo de desplazamiento"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panel de desplazamiento"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Se colocó <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> en el bucket RESTRICTED"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"envió una imagen"</string>
@@ -2482,6 +2487,7 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabajo 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Probar"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string>
+    <string name="profile_label_supervising" msgid="5649312778545745371">"Supervisando"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabajo"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 8523473..3300397 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo Una mano"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuación extra"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Audífonos"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Clic automático"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Desconectado"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Conectado"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Activo"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pedir patrón de desbloqueo para dejar de fijar"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Solicitar contraseña para desactivar"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instalado por tu administrador.\nVe a Ajustes para ver los permisos concedidos."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado por el administrador"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Actualizado por tu administrador.\nVe a Ajustes para ver los permisos concedidos."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado por el administrador"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red."</string>
@@ -2275,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desplazarse"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posición"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> se ha incluido en el grupo de restringidos"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha enviado una imagen"</string>
@@ -2482,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabajo 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Prueba"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Común"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabajo"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index a1ddf81..c172de8 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Ühekäerežiim"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Eriti tume"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Kuuldeseadmed"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automaatklikk"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Ühendus katkestatud"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Ühendatud"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktiivne"</string>
@@ -1821,8 +1820,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Suurendus"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Kas vahetada telefoni mikrofonile?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Kas vahetada kuuldeaparaadi mikrofonile?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Parema heli tagamiseks või siis, kui tele kuuldeaparaadi aku on tühi. See vahetab teie mikrofoni ainult kõne ajal."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Saate kasutada oma kuuldeaparaadi mikrofon vabakäerežiimis helistamiseks. See vahetab teie mikrofoni ainult kõne ajal."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Parema heli tagamiseks või siis, kui teie kuuldeaparaadi aku on tühi. See vahetab teie mikrofoni ainult kõne ajal."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Saate kasutada oma kuuldeaparaadi mikrofoni vabakäerežiimis helistamiseks. See vahetab teie mikrofoni ainult kõne ajal."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Vaheta"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Seaded"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Üleminek kasutajale <xliff:g id="NAME">%1$s</xliff:g> ..."</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Enne vabastamist küsi avamismustrit"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Enne vabastamist küsi parooli"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Installis teie administraator.\nAntud õiguste vaatamiseks avage seaded"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Administraator on seda värskendanud"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Värskendas teie administraator.\nAntud õiguste vaatamiseks avage seaded"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administraator on selle kustutanud"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Akusäästja lülitab sisse tumeda teema ja lülitab välja taustategevused, mõned visuaalsed efektid, teatud funktsioonid ja võrguühendused või piirab neid."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Keri"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Peata"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Asukoht"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> on lisatud salve PIIRANGUTEGA"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"saatis kujutise"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Töö 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Jagatud"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Tööprofiil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaatne ruum"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index c63cfe6..24dbe4b 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Esku bakarreko modua"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Are ilunago"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Entzumen-gailuak"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatikoki klik egiteko eginbidea"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Deskonektatuta"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Konektatuta"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktibo"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Eskatu desblokeatzeko eredua aingura kendu aurretik"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Eskatu pasahitza aingura kendu aurretik"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Administratzaileak instalatu du.\nEmandako baimenak ikusteko, joan ezarpenetara."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Administratzaileak eguneratu du"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Administratzaileak eguneratu du.\nEmandako baimenak ikusteko, joan ezarpenetara."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administratzaileak ezabatu du"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Ados"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Bateria-aurreztaileak gai iluna aktibatzen du, eta atzeko planoko jarduerak, zenbait efektu bisual, eta eginbide jakin eta sareko konexio batzuk mugatzen edo desaktibatzen ditu."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Egin gora eta behera"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausatu"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Ezarri posizioan"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Murriztuen edukiontzian ezarri da <xliff:g id="PACKAGE_NAME">%1$s</xliff:g>"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"erabiltzaileak irudi bat bidali du"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Lanekoa 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Probakoa"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Partekatua"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Laneko profila"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Eremu pribatua"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 5e1caad..f31bb10 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"حالت یک‌دستی"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"بسیار کم‌نور"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"دستگاه‌های کمک‌شنوایی"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"کلیک خودکار"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"متصل نیست"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"وصل شد"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"فعال"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"درخواست الگوی بازگشایی قفل قبل‌از برداشتن سنجاق"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"درخواست گذرواژه قبل از برداشتن سنجاق"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"سرپرست شما آن را نصب کرده است.\nبرای مشاهده اجازه‌های اعطاشده به تنظیمات بروید"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"توسط سرپرست سیستم به‌روزرسانی شد"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"سرپرستتان آن را به‌روز کرده است.\nبرای مشاهده اجازه‌های اعطاشده به تنظیمات بروید"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"توسط سرپرست سیستم حذف شد"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"تأیید"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"«بهینه‌سازی باتری» «زمینه تاریک» را روشن می‌کند و فعالیت پس‌زمینه، برخی از جلوه‌های بصری، ویژگی‌هایی خاص، و برخی از اتصال‌های شبکه را محدود یا خاموش می‌کند."</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"پیمایش"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"توقف موقت"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"موقعیت"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"پیمایش به بالا"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"پیمایش به پایین"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"پیمایش به چپ"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"پیمایش به راست"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"خارج شدن از حالت پیمایش"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"پانل پیمایش"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> در سطل «محدودشده» قرار گرفت"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"تصویری ارسال کرد"</string>
@@ -2481,6 +2486,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"کار ۳"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"آزمایش"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"عمومی"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"نمایه کاری"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"فضای خصوصی"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"همسانه‌سازی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index f7115eb..f1a5fbc 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Yhden käden moodi"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Erittäin himmeä"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Kuulolaitteet"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automaattinen klikkaus"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Yhteys katkaistu"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Yhdistetty"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktiivinen"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pyydä lukituksenpoistokuvio ennen irrotusta"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Pyydä salasana ennen irrotusta"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Järjestelmänvalvojan asentama.\nTarkista myönnetyt luvat asetuksista."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Järjestelmänvalvoja päivitti tämän."</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Järjestelmänvalvojan päivittämä.\nTarkista myönnetyt luvat asetuksista"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Järjestelmänvalvoja poisti tämän."</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, tiettyjä ominaisuuksia sekä joitakin visuaalisia tehosteita ja verkkoyhteyksiä."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Vieritä"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Keskeytä"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Sijainti"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> on nyt rajoitettujen ryhmässä"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"lähetti kuvan"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Työ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testi"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Jaettu"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Työprofiili"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Yksityinen tila"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klooni"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 56623a2..a7b1289 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode Une main"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Très sombre"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Appareils auditifs"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Clic automatique"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Déconnecté"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Connecté"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Actif"</string>
@@ -1821,9 +1820,9 @@
     <string name="accessibility_gesture_3finger_instructional_text" msgid="1124458279366968154">"La fonctionnalité s\'ouvrira la prochaine fois que vous utiliserez ce raccourci. Balayez l\'écran du bas vers le haut avec trois doigts et relâchez rapidement."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Agrandissement"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Passer au micro du téléphone?"</string>
-    <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Passer au micro pour la prothèse auditive?"</string>
+    <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Passer au micro de la prothèse auditive?"</string>
     <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Pour profiter d\'un meilleur son ou si la pile de votre prothèse auditive est faible. Cette option ne change votre micro que pendant l\'appel."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Vous pouvez utiliser votre microphone pour prothèse auditive pour faire des appels en mode mains libres. Cette option ne change votre micro que pendant l\'appel."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Vous pouvez utiliser le microphone de votre prothèse auditive pour faire des appels en mode mains libres. Cette option ne change votre micro que pendant l\'appel."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Changer"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Paramètres"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Passage au profil : <xliff:g id="NAME">%1$s</xliff:g> en cours…"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Demander le schéma de déverrouillage avant d\'annuler l\'épinglage"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Demander le mot de passe avant d\'annuler l\'épinglage"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Installé par votre administrateur.\nAccédez aux paramètres pour consulter les autorisations accordées"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Mise à jour par votre administrateur"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Installé par votre administrateur.\nAccédez aux paramètres pour consulter les autorisations accordées"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Le mode Économiseur de pile active le thème sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels, certaines fonctionnalités et certaines connexions réseau."</string>
@@ -2275,6 +2274,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Faire défiler"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Faire défiler vers le haut"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Faire défiler vers le bas"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Faire défiler vers la gauche"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Faire défiler vers la droite"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Sortir du mode de défilement"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panneau de défilement"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> a été placé dans le compartiment RESTREINT"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g> :"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"a envoyé une image"</string>
@@ -2482,6 +2487,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil professionnel"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 90a0f44..213c256 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode une main"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminosité ultra-réduite"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Appareils auditifs"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Clic automatique"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Déconnecté"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Connecté"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Actif"</string>
@@ -1822,8 +1821,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Agrandissement"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Passer au micro du téléphone ?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Passer au micro de l\'appareil auditif ?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Pour améliorer le son ou si la batterie de votre appareil auditif est faible. Cette option change uniquement le micro pendant l\'appel."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Vous pouvez utiliser le micro de votre appareil auditif pour passer des appels en mode mains-libres. Cette option change uniquement le micro pendant l\'appel."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Pour un meilleur son ou si la batterie de votre appareil auditif est faible. Cette option ne change le micro que pendant l\'appel."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Vous pouvez utiliser le micro de votre appareil auditif pour passer des appels en mode mains-libres. Cette option ne change le micro que pendant l\'appel."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Changer"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Paramètres"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Passage à <xliff:g id="NAME">%1$s</xliff:g>..."</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Demander le schéma de déverrouillage avant de retirer l\'épingle"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Demander le mot de passe avant de retirer l\'épingle"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Installé par votre administrateur.\nAllez dans les paramètres pour consulter les autorisations accordées."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Mis à jour par votre administrateur"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Mis à jour par votre administrateur.\nAllez dans les paramètres pour consulter les autorisations accordées."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"L\'économiseur de batterie active le thème sombre et limite ou désactive l\'activité en arrière-plan ainsi que certains effets visuels, fonctionnalités et connexions réseau."</string>
@@ -2275,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Faire défiler"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> a été placé dans le bucket RESTRICTED"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g> :"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"a envoyé une image"</string>
@@ -2482,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil professionnel"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index a2b5381..8c265b0 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo dunha soa man"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuación extra"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Dispositivos auditivos"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Clic automático"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Desconectado"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Conectado"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Activo"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pedir padrón de desbloqueo antes de soltar a fixación"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Pedir contrasinal antes de soltar a fixación"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instalado pola persoa administradora.\nVai á configuración para ver os permisos concedidos"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado polo teu administrador"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Actualizado pola persoa administradora.\nVai á configuración para ver os permisos concedidos"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado polo teu administrador"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Coa función Aforro de batería, actívase o tema escuro e restrínxense ou desactívanse a actividade en segundo plano, algúns efectos visuais e determinadas funcións e conexións de rede."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desprazar"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausa"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posición"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> incluíuse no grupo RESTRINXIDO"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviouse unha imaxe"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Traballo 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Proba"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de traballo"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espazo privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonado"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 9d57bde..c4da349 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1966,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"અનપિન કરતા પહેલાં અનલૉક પૅટર્ન માટે પૂછો"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"અનપિન કરતાં પહેલાં પાસવર્ડ માટે પૂછો"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"તમારા ઍડમિન દ્વારા ઇન્સ્ટૉલ કરવામાં આવ્યું છે.\nઆપેલી પરવાનગીઓ જોવા માટે સેટિંગ પર જાઓ"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"તમારા વ્યવસ્થાપક દ્વારા અપડેટ કરવામાં આવેલ છે"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"તમારા ઍડમિન દ્વારા અપડેટ કરવામાં આવ્યું છે.\nઆપેલી પરવાનગીઓ જોવા માટે સેટિંગ પર જાઓ"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"તમારા વ્યવસ્થાપક દ્વારા કાઢી નાખવામાં આવેલ છે"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ઓકે"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"બૅટરી સેવર ઘેરી થીમની સુવિધા ચાલુ કરે છે અને બૅકગ્રાઉન્ડ પ્રવૃત્તિ, અમુક વિઝ્યુઅલ ઇફેક્ટ, અમુક સુવિધાઓ અને કેટલાક નેટવર્ક કનેક્શન મર્યાદિત કે બંધ કરે છે."</string>
@@ -2274,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"સ્ક્રોલ કરો"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"થોભાવો"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"સ્થિતિ"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>ને પ્રતિબંધિત સમૂહમાં મૂકવામાં આવ્યું છે"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"છબી મોકલી"</string>
@@ -2481,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ઑફિસ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"પરીક્ષણ કરો"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"કૉમ્યુનલ"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ઑફિસની પ્રોફાઇલ"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ખાનગી સ્પેસ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ક્લોન"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 563b724..01e5c77 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"वन-हैंडेड मोड"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"कान की मशीन"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"अपने-आप क्लिक होने की सुविधा"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"डिसकनेक्ट हो गया"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"कनेक्ट हो गया"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"चालू है"</string>
@@ -1821,8 +1820,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ज़ूम करने की सुविधा"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"क्या आपको फ़ोन के माइक्रोफ़ोन पर स्विच करना है?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"क्या आपको कान की मशीन के माइक पर स्विच करना है?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"बेहतर आवाज़ के लिए या कान की मशीन की बैटरी कम होने पर. यह सिर्फ़ कॉल के दौरान आपका माइक चालू करता है."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"बोलकर कॉल का जवाब देने के लिए, \'कान की मशीन का माइक्रोफ़ोन\' इस्तेमाल किया जा सकता है. इससे, कॉल के दौरान सिर्फ़ आपका माइक चालू या बंद होता है."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"बेहतर आवाज़ के लिए या कान की मशीन की बैटरी कम होने पर, इसका इस्तेमाल करें. इससे सिर्फ़ कॉल के दौरान आपका माइक चालू होता है."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"बोलकर कॉल का जवाब देने के लिए, \'कान की मशीन का माइक्रोफ़ोन\' इस्तेमाल किया जा सकता है. इससे सिर्फ़ कॉल के दौरान आपका माइक चालू होता है."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"स्विच करें"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"सेटिंग"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> पर स्विच किया जा रहा है…"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"अनपिन करने से पहले लॉक खोलने के पैटर्न के लिए पूछें"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"अनपिन करने से पहले पासवर्ड के लिए पूछें"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"इसे आपके एडमिन ने इंस्टॉल किया है.\nजिन अनुमतियों को मंज़ूरी मिली है उन्हें देखने के लिए, सेटिंग में जाएं"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"आपके व्यवस्थापक ने अपडेट किया है"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"इसे आपके एडमिन ने अपडेट किया है.\nजिन अनुमतियों को मंज़ूरी मिली है उन्हें देखने के लिए, सेटिंग में जाएं"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"आपके व्यवस्थापक ने हटा दिया है"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ठीक है"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, इस मोड में बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और कुछ खास सुविधाएं कम या बंद हो जाती हैं. कुछ इंटरनेट कनेक्शन भी पूरी तरह काम नहीं करते."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल करें"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"रोकें"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"पोज़िशन"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> को प्रतिबंधित बकेट में रखा गया है"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"एक इमेज भेजी गई"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ऑफ़िस 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"टेस्ट"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"कम्यूनिटी"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"वर्क प्रोफ़ाइल"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"प्राइवेट स्पेस"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index fec624d..826b860 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Način rada jednom rukom"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Još tamnije"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Slušna pomagala"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatski klik"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Nije povezano"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Povezano"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktivno"</string>
@@ -1822,8 +1821,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Povećavanje"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Želite li se prebaciti na mikrofon telefona?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Želite li se prebaciti na mikrofon slušnog pomagala?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Za bolji zvuk ili ako je razina baterije slušnog pomagala niska. Time se mikrofon prebacuje samo tijekom poziva."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Mikrofon slušnog pomagala možete koristiti za pozivanje bez upotrebe ruku. Time se mikrofon prebacuje samo tijekom poziva."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Da biste se bolje čuli ili ako je razina baterije slušnog pomagala niska. Mikrofon se koristi samo tijekom poziva."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Mikrofon slušnog pomagala možete koristiti za pozivanje bez upotrebe ruku. Mikrofon se koristi samo tijekom poziva."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Prebaci"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Postavke"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Prebacivanje na korisnika <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Traži uzorak za otključavanje radi otkvačivanja"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Traži zaporku radi otkvačivanja"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instalirao vaš administrator.\nOtvorite postavke da biste pregledali dodijeljena dopuštenja"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao administrator"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Ažurirao je vaš administrator.\nOtvorite postavke da biste pregledali dodijeljena dopuštenja"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao administrator"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"U redu"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Štednja baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizualne efekte, određene značajke i neke mrežne veze."</string>
@@ -2275,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Pomakni se"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicija"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> premješten je u spremnik OGRANIČENO"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"šalje sliku"</string>
@@ -2482,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Radni profil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 7871aba..1519326 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Egykezes mód"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extrasötét"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hallásjavító eszközök"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatikus kattintás"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Leválasztva"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Csatlakozva"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktív"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Feloldási minta kérése a kitűzés feloldásához"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Jelszó kérése a rögzítés feloldásához"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"A rendszergazda által telepítve.\nLépjen a beállításokhoz a megadott engedélyek megtekintéséhez."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"A rendszergazda által frissítve"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"A rendszergazda által frissítve.\nLépjen a beállításokhoz a megadott engedélyek megtekintéséhez."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"A rendszergazda által törölve"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Az Akkumulátorkímélő mód bekapcsolja a Sötét témát, és korlátozza vagy kikapcsolja a háttérbeli tevékenységeket, valamint bizonyos vizuális effekteket, funkciókat és hálózati kapcsolatokat."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Görgetés"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Szüneteltetés"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozíció"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"A következő csomag a KORLÁTOZOTT csoportba került: <xliff:g id="PACKAGE_NAME">%1$s</xliff:g>"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"képet küldött"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"3. munkahelyi"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Teszt"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Közös"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Munkaprofil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privát terület"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klón"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 01e5110..2fd68c1 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Մեկ ձեռքի ռեժիմ"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Հավելյալ խամրեցում"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Լսողական սարքեր"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Ավտոմատ սեղմում"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Անջատված է"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Միացված է"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Ակտիվ է"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Հարցնել ապակողպող նախշը"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Հարցնել գաղտնաբառը"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Տեղադրվել է ադմինիստրատորի կողմից։\nԱնցեք կարգավորումներ՝ տրամադրված թույլտվությունները դիտելու համար"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Թարմացվել է ձեր ադմինիստրատորի կողմից"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Թարմացվել է ձեր ադմինիստրատորի կողմից։\nԱնցեք կարգավորումներ՝ տրամադրված թույլտվությունները դիտելու համար"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Ջնջվել է ձեր ադմինիստրատորի կողմից"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Եղավ"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"«Մարտկոցի տնտեսում» գործառույթը միացնում է մուգ թեման և անջատում կամ սահմանափակում է աշխատանքը ֆոնային ռեժիմում, որոշ վիզուալ էֆեկտներ, ցանցային միացումներ և այլ գործառույթներ։"</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Ոլորել"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Դադարեցնել"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Դիրքը"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Թերթել վերև"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Թերթել ներքև"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Թերթել ձախ"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Թերթել աջ"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Դուրս գալ թերթելու ռեժիմից"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Թերթելու վահանակ"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> փաթեթը գցվեց ՍԱՀՄԱՆԱՓԱԿՎԱԾ զամբյուղի մեջ"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>՝"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"օգտատերը պատկեր է ուղարկել"</string>
@@ -2481,6 +2486,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Աշխատանքային 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Փորձնական"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Ընդհանուր"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Աշխատանքային պրոֆիլ"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Մասնավոր տարածք"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Կլոն"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index df42e47..6e686b4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode satu tangan"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra redup"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Alat bantu dengar"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Klik otomatis"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Tidak terhubung"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Terhubung"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktif"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Meminta pola pembukaan kunci sebelum melepas sematan"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Meminta sandi sebelum melepas sematan"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Diinstal oleh admin Anda.\nBuka setelan untuk melihat izin yang diberikan"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Diupdate oleh admin Anda"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Diperbarui oleh admin Anda.\nBuka setelan untuk melihat izin yang diberikan"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Dihapus oleh admin Anda"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Oke"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Penghemat Baterai akan mengaktifkan Tema gelap dan membatasi atau menonaktifkan aktivitas latar belakang, beberapa efek visual, fitur tertentu, dan beberapa koneksi jaringan."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Jeda"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisi"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> telah dimasukkan ke dalam bucket DIBATASI"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"mengirim gambar"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Kerja 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Pengujian"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil kerja"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang privasi"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 40d642d..35b102e 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Einhent stilling"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mjög dökkt"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Heyrnartæki"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Sjálfvirkur smellur"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Aftengt"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Tengt"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Virkt"</string>
@@ -1821,8 +1820,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Stækkun"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Skipta yfir í hljóðnema símans?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Skipta yfir í hljóðnema heyrnartækis?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Fyrir betri hljómgæði eða ef lítil hleðsla er á heyrnartækinu. Aðeins verður skipt um hljóðnema á meðan á símtali stendur."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Þú getur notað hljóðnema heyrnartækisins þíns til að hringja eða svara símtölum handfrjálst. Aðeins verður skipt um hljóðnema á meðan á símtali stendur."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Til að auka hljómgæði eða ef hleðsla heyrnartækisins er lág. Aðeins verður skipt um hljóðnema meðan á símtali stendur."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Þú getur notað hljóðnema heyrnartækisins þíns til að hringja eða svara símtölum handfrjálst. Aðeins verður skipt um hljóðnema meðan á símtali stendur."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Skipta"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Stillingar"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Skiptir yfir á <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Biðja um opnunarmynstur til að losa"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Biðja um aðgangsorð til að losa"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Sett upp af stjórnanda.\nFarðu í stillingar til að sjá heimildir"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Kerfisstjóri uppfærði"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Uppfært af stjórnanda.\nFarðu í stillingarnar þínar til að sjá þær heimildir sem hafa verið veittar"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Kerfisstjóri eyddi"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Í lagi"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Rafhlöðusparnaður kveikir á dökku þema og takmarkar eða slekkur á bakgrunnsvirkni, sumum myndáhrifum, tilteknum eiginleikum og sumum nettengingum."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Fletta"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Hlé"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Staðsetning"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> var sett í flokkinn TAKMARKAÐ"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sendi mynd"</string>
@@ -2481,6 +2492,7 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Vinna 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Prófun"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Sameiginlegt"</string>
+    <string name="profile_label_supervising" msgid="5649312778545745371">"Eftirlit virkt"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Vinnusnið"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Leynirými"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Afrit"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index e501c57..28aee0f 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1821,8 +1821,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ingrandimento"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Passare al microfono dello smartphone?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Passare al microfono dell\'apparecchio acustico?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Per una migliore resa audio o se la batteria dell\'apparecchio acustico è scarica. In questo modo, il microfono viene attivato solo durante la chiamata."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Puoi usare il microfono dell\'apparecchio acustico per le chiamate in vivavoce. In questo modo, il microfono viene attivato solo durante la chiamata."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Per una migliore resa audio o se la batteria dell\'apparecchio acustico è scarica. Il cambio del microfono avviene solo durante la chiamata."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Puoi usare il microfono dell\'apparecchio acustico per le chiamate in vivavoce. Il cambio del microfono avviene solo durante la chiamata."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Cambia"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Impostazioni"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Passaggio a <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1966,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Richiedi sequenza di sblocco prima di sbloccare"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Richiedi password prima di sbloccare"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Installato dall\'amministratore.\nVai alle impostazioni per visualizzare le autorizzazioni concesse"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Aggiornato dall\'amministratore"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Aggiornato dall\'amministratore.\nVai alle impostazioni per visualizzare le autorizzazioni concesse"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminato dall\'amministratore"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Ok"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Il risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string>
@@ -2274,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scorri"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Metti in pausa"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posizione"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> è stato inserito nel bucket RESTRICTED"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha inviato un\'immagine"</string>
@@ -2481,6 +2493,7 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Lavoro 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Condiviso"</string>
+    <string name="profile_label_supervising" msgid="5649312778545745371">"Supervisione"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profilo di lavoro"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spazio privato"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index c9984e2..5eadbd4 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"מצב שימוש ביד אחת"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"מעומעם במיוחד"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"מכשירי שמיעה"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"קליק אוטומטי"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"מנותק"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"מחובר"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"מצב פעיל"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"צריך לבקש קו ביטול נעילה לפני ביטול הצמדה"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"יש לבקש סיסמה לפני ביטול הצמדה"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"החבילה הותקנה על ידי האדמין.\nצריך לעבור להגדרות כדי לראות את ההרשאות שניתנו"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"עודכנה על ידי מנהל המערכת"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"החבילה עודכנה על ידי האדמין.\nצריך לעבור להגדרות כדי לראות את ההרשאות שניתנו"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"נמחקה על ידי מנהל המערכת"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"אישור"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"התכונה \'חיסכון בסוללה\' מפעילה עיצוב כהה ומגבילה או מכבה פעילות ברקע, חלק מהאפקטים החזותיים, תכונות מסוימות וחלק מהחיבורים לרשתות."</string>
@@ -2275,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"גלילה"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"השהיה"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"מיקום"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> התווספה לקטגוריה \'מוגבל\'"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"נשלחה תמונה"</string>
@@ -2482,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"פרופיל עבודה 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"בדיקה"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"שיתופי"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"פרופיל העבודה"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"המרחב הפרטי"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"שכפול"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 7ab896e..207e030 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1965,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"画面固定を解除する前にロック解除パターンの入力を求める"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"オフライン再生を解除する前にパスワードの入力を求める"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"管理者によりインストールされています。\n付与された権限を確認するには、設定に移動してください"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"管理者により更新されています"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"管理者により更新されています。\n付与された権限を確認するには、設定に移動してください"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"管理者により削除されています"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"バッテリー セーバーを ON にすると、ダークモードが ON になります。また、バックグラウンド アクティビティ、一部の視覚効果、特定の機能、一部のネットワーク接続が制限されるか OFF になります。"</string>
@@ -2273,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"スクロール"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"一時停止"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"上にスクロール"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"下にスクロール"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"左にスクロール"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"右にスクロール"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"スクロール モードを終了"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"スクロール パネル"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> は RESTRICTED バケットに移動しました。"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"画像を送信しました"</string>
@@ -2480,6 +2486,7 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"仕事用 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"テスト"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
+    <string name="profile_label_supervising" msgid="5649312778545745371">"管理者"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"仕事用プロファイル"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"プライベート スペース"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 4d634d4..a1a4f72 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ცალი ხელის რეჟიმი"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"დამატებითი დაბინდვა"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"სმენის აპარატები"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"ავტოდაწკაპუნება"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"კავშირი გაწყვეტილია"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"დაკავშირებული"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"აქტიური"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ფიქსაციის მოხსნამდე განბლოკვის ნიმუშის მოთხოვნა"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ფიქსაციის მოხსნამდე პაროლის მოთხოვნა"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"დაინსტალირებულია თქვენი ადმინისტრატორის მიერ.\nდაშვებული ნებართვების სანახავად გადადით პარამეტრებზე"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"განახლებულია თქვენი ადმინისტრატორის მიერ"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"განახლებულია თქვენი ადმინისტრატორის მიერ.\nდაშვებული ნებართვების სანახავად გადადით პარამეტრებზე"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"წაიშალა თქვენი ადმინისტრატორის მიერ"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"კარგი"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"ბატარეის დამზოგი ჩართავს მუქ თემას და შეზღუდავს ან გამორთავს ფონურ აქტივობას, ზოგიერთ ვიზუალურ ეფექტს, გარკვეულ ფუნქციებსა და ზოგიერთ ქსელთან კავშირს."</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"გადაადგილება"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"პაუზა"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"პოზიცია"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ზემოთ გადაადგილება"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"ქვემოთ გადაადგილება"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"მარცხნივ გადაადგილება"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"მარჯვნივ გადაადგილება"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"გადაადგილების რეჟიმიდან გასვლა"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"გადაადგილების არე"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> მოთავსდა კალათაში „შეზღუდული“"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"გაიგზავნა სურათი"</string>
@@ -2481,6 +2486,7 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"სამსახური 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"სატესტო"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"საერთო"</string>
+    <string name="profile_label_supervising" msgid="5649312778545745371">"ზედამხედველობა"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"სამსახურის პროფილი"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"კერძო სივრცე"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"კლონის შექმნა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 8d54a32..24e45df 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Бір қолмен басқару режимі"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Экранды қарайту"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Есту аппараттары"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Автоматты басу"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Ажыратылды"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Қосылды"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Белсенді"</string>
@@ -1821,8 +1820,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ұлғайту"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Телефонның микрофонына ауысу керек пе?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Есту аппаратының микрофонына ауысу керек пе?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Дауыс жақсы шығу үшін немесе есту аппаратының батарея заряды аз болған жағдайда пайдалануға болады. Микрофонға тек қоңырау кезінде ауысады."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Дауыспен басқару арқылы қоңырау шалу үшін есту аппаратының микрофонын пайдалана аласыз. Микрофонға тек қоңырау кезінде ауысады."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Дыбыс дұрыс естілмей тұрғанда немесе есту аппаратының батарея заряды аз болған жағдайда пайдалануға болады. Басқа микрофонға тек қоңырау кезінде ауысады."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Дауыспен басқару арқылы қоңырау шалу үшін есту аппаратының микрофонын пайдалана аласыз. Басқа микрофонға тек қоңырау кезінде ауысады."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Ауысу"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Параметрлер"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> профиліне ауысу…"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Босату алдында бекітпесін ашу өрнегін сұрау"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Босату алдында құпия сөзді сұрау"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Әкімшіңіз орнатты.\nБерілген рұқсаттарды көру үшін параметрлерге өтіңіз."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Әкімші жаңартқан"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Әкімшіңіз жаңартты.\nБерілген рұқсаттарды көру үшін параметрлерге өтіңіз."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Әкімші жойған"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Жарайды"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Батареяны үнемдеу режимі қараңғы режимді іске қосады және фондық әрекеттерге, кейбір визуалдық әсерлерге, белгілі бір функциялар мен кейбір желі байланыстарына шектеу қояды немесе оларды өшіреді."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Айналдыру"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Кідірту"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Орналастыру"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ШЕКТЕЛГЕН себетке салынды."</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"сурет жіберілді"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Жұмыс 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Сынақ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Жұмыс профилі"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Құпия кеңістік"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index d7e0299..e9ee62a 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"មុខងារប្រើដៃម្ខាង"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ងងឹតខ្លាំង"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"ចុចស្វ័យប្រវត្តិ"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"បាន​ផ្ដាច់"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"បានភ្ជាប់"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"សកម្ម"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"សួរ​រក​លំនាំ​ដោះ​សោ​មុន​ពេលដោះខ្ទាស់"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"សួរ​រក​ពាក្យ​សម្ងាត់​មុន​ពេល​ផ្ដាច់"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"បានដំឡើងដោយអ្នកគ្រប់គ្រងរបស់អ្នក។\nចូលទៅកាន់ការកំណត់ ដើម្បីមើលការ​អនុញ្ញាតដែលផ្ដល់ឱ្យ"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"ធ្វើ​បច្ចុប្បន្នភាព​ដោយ​អ្នកគ្រប់គ្រង​របស់​អ្នក"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"បានធ្វើបច្ចុប្បន្នភាពដោយអ្នកគ្រប់គ្រងរបស់អ្នក។\nចូលទៅកាន់ការកំណត់ ដើម្បីមើលការ​អនុញ្ញាតដែលផ្ដល់ឱ្យ"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"លុប​ដោយ​អ្នកគ្រប់គ្រង​របស់​អ្នក"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"យល់ព្រម"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"មុខងារ​សន្សំថ្មបើកទម្រង់រចនាងងឹត និងដាក់កំហិត ឬបិទសកម្មភាពផ្ទៃខាងក្រោយ បែបផែនរូបភាពមួយចំនួន មុខងារជាក់លាក់ និងការតភ្ជាប់បណ្ដាញមួយចំនួន។"</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"រំកិល"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ផ្អាក"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"ទីតាំង"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"រំកិលឡើងលើ"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"រំកិលចុះក្រោម"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"រំកិល​ទៅ​ឆ្វេង"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"រំកិល​ទៅ​ស្ដាំ"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ចាកចេញពី​មុខងារ​រំកិល"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"ផ្ទាំងរំកិល"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ត្រូវបានដាក់​ទៅក្នុងធុង​ដែលបានដាក់កំហិត"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>៖"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"បាន​ផ្ញើរូបភាព"</string>
@@ -2481,6 +2486,7 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ការងារទី 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ការធ្វើ​តេស្ត"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ទូទៅ"</string>
+    <string name="profile_label_supervising" msgid="5649312778545745371">"កំពុងគ្រប់គ្រង"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"កម្រងព័ត៌មានការងារ"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"លំហ​ឯកជន"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ក្លូន"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 9771def..c9a0f21 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ಒಂದು ಕೈ ಮೋಡ್‌"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ಇನ್ನಷ್ಟು ಮಬ್ಬು"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"ಆಟೋಕ್ಲಿಕ್"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"ಡಿಸ್‌ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"ಸಕ್ರಿಯವಾಗಿದೆ"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ಅನ್‌ಪಿನ್ ಮಾಡಲು ಅನ್‌ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಕೇಳಿ"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ಅನ್‌ಪಿನ್ ಮಾಡಲು ಪಾಸ್‌ವರ್ಡ್ ಕೇಳು"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿದ್ದಾರೆ.\nನೀಡಲಾದ ಅನುಮತಿಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಹೋಗಿ"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರಿಂದ ಅಪ್‌ಡೇಟ್ ಮಾಡಲ್ಪಟ್ಟಿದೆ"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ಅಪ್‌ಡೇಟ್ ಮಾಡಿದ್ದಾರೆ.\nನೀಡಲಾದ ಅನುಮತಿಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಹೋಗಿ"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ಅಳಿಸಿದ್ದಾರೆ"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ಸರಿ"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"ಬ್ಯಾಟರಿ ಸೇವರ್, ಡಾರ್ಕ್ ಥೀಮ್ ಅನ್ನು ಆನ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಚಟುವಟಿಕೆ, ಕೆಲವು ವಿಷುವಲ್ ಎಫೆಕ್ಟ್‌ಗಳು, ಕೆಲವು ಫೀಚರ್‌ಗಳು ಮತ್ತು ಇತರ ನೆಟ್‌ವರ್ಕ್ ಸಂಪರ್ಕಗಳನ್ನು ಮಿತಿಗೊಳಿಸುತ್ತದೆ ಅಥವಾ ಆಫ್ ಮಾಡುತ್ತದೆ."</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ವಿರಾಮಗೊಳಿಸಿ"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"ಸ್ಥಾನ"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ಮೇಲಕ್ಕೆ ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"ಕೆಳಕ್ಕೆ ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ಎಡಕ್ಕೆ ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ಬಲಕ್ಕೆ ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ಸ್ಕ್ರಾಲ್ ಮೋಡ್‌ನಿಂದ ನಿರ್ಗಮಿಸಿ"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"ಪ್ಯಾನೆಲ್ ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ಅನ್ನು ನಿರ್ಬಂಧಿತ ಬಕೆಟ್‌ಗೆ ಹಾಕಲಾಗಿದೆ"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ಚಿತ್ರವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ"</string>
@@ -2481,6 +2486,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ಕೆಲಸ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ಪರೀಕ್ಷೆ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ಸಮುದಾಯ"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ಕ್ಲೋನ್"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index a39db4b..852051f 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"한 손 모드"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"더 어둡게"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"청각 보조 기기"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"자동 클릭"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"연결 끊김"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"연결됨"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"활성"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"고정 해제 시 잠금 해제 패턴 요청"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"고정 해제 이전에 비밀번호 요청"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"관리자에 의해 설치되었습니다.\n부여된 권한을 확인하려면 설정으로 이동하세요."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"관리자에 의해 업데이트되었습니다."</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"관리자에 의해 업데이트되었습니다.\n부여된 권한을 확인하려면 설정으로 이동하세요."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"관리자에 의해 삭제되었습니다."</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"확인"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"절전 모드는 어두운 테마를 사용하고 백그라운드 활동, 일부 시각 효과, 특정 기능 및 일부 네트워크 연결을 제한하거나 사용 중지합니다."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"스크롤"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"일시중지"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"위치"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 항목이 RESTRICTED 버킷으로 이동함"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"이미지 보냄"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"직장 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"테스트"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"공동"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"직장 프로필"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"비공개 스페이스"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"클론"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index fbc3afd..8115a00 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Бир кол режими"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Кошумча караңгылатуу"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Угуу түзмөктөрү"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Авточыкылдатуу"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Ажыратылды"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Туташты"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Жигердүү"</string>
@@ -1821,8 +1820,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Чоңойтуу"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Телефондун микрофонуна которуласызбы?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Угуу аппаратынын микрофонуна которуласызбы?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Үндүн сапатын жакшыртуу үчүн же угуу аппаратыңыздын батареясы аз болсо, телефондун микрофонун колдоно аласыз. Микрофонуңуз чалуу учурунда гана которулат."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Угуу аппаратыңыздын микрофонун үн режиминде чалуу үчүн колдоно аласыз. Микрофонуңуз чалуу учурунда гана которулат."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Угуу аппаратыңыз жакшы угулбай жатса же батареясы отурайын деп калса, микрофонго которула аласыз. Микрофонго чалуу учурунда гана которуласыз."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Телефон менен сүйлөшүп жатканда угуу аппаратыңыздын микрофонун колдоно аласыз. Микрофонго чалуу учурунда гана которуласыз."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Которулуу"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Параметрлер"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> дегенге которулууда…"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Бошотуудан мурун графикалык ачкыч суралсын"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Бошотуудан мурун сырсөз суралсын"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Администраторуңуз орнотту.\nБерилген уруксаттарды көрүү үчүн параметрлерге өтүңүз"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Администраторуңуз жаңыртып койгон"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Администраторуңуз орнотту.\nБерилген уруксаттарды көрүү үчүн параметрлерге өтүңүз"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Администраторуңуз жок кылып салган"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ЖАРАЙТ"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Батареяны үнөмдөөчү режимде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер, белгилүү бир функциялар жана айрым тармакка туташуулар чектелип же өчүрүлөт."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Сыдыруу"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Тындыруу"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Орду"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ЧЕКТЕЛГЕН чакага коюлган"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"сүрөт жөнөттү"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Жумуш 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Сыноо"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Жумуш профили"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Жеке мейкиндик"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index a0a6b38..b9868d1 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ໂໝດມືດຽວ"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ຫຼຸດແສງເປັນພິເສດ"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ອຸປະກອນຊ່ວຍຟັງ"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"ການຄລິກອັດຕະໂນມັດ"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"ນຳໃຊ້ຢູ່"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"​ຖາມ​ຫາ​ຮູບ​ແບບ​ປົດ​ລັອກ​ກ່ອນ​ຍົກ​ເລີກ​ການ​ປັກ​ໝຸດ"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"​ຖາມ​ຫາ​ລະ​ຫັດ​ຜ່ານ​ກ່ອນ​ຍົກ​ເລີກ​ການ​ປັກ​ໝຸດ"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"ຕິດຕັ້ງໂດຍຜູ້ເບິ່ງແຍງຂອງທ່ານ.\nເຂົ້າໄປການຕັ້ງຄ່າເພື່ອເບິ່ງສິດທີ່ໄດ້ຮັບອະນຸຍາດ"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"ຖືກອັບໂຫລດໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"ອັບເດດໂດຍຜູ້ເບິ່ງແຍງຂອງທ່ານ.\nເຂົ້າໄປການຕັ້ງຄ່າເພື່ອເບິ່ງສິດທີ່ໄດ້ຮັບອະນຸຍາດ"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"ຖືກລຶບອອກໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ຕົກລົງ"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"ຕົວປະຢັດແບັດເຕີຣີຈະເປີດໃຊ້ຮູບແບບສີສັນມືດ ແລະ ຈຳກັດ ຫຼື ປິດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກທາງພາບຈຳນວນໜຶ່ງ, ຄຸນສົມບັດບາງຢ່າງ ແລະ ການເຊື່ອມຕໍ່ເຄືອຂ່າຍບາງອັນ."</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ເລື່ອນ"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ຢຸດຊົ່ວຄາວ"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"ຕຳແໜ່ງ"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ເລື່ອນຂຶ້ນ"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"ເລື່ອນລົງ"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ເລື່ອນໄປທາງຊ້າຍ"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ເລື່ອນໄປທາງຂວາ"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ອອກຈາກໂໝດເລື່ອນ"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"ເລື່ອນແຜງ"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ຖືກວາງໄວ້ໃນກະຕ່າ \"ຈຳກັດ\" ແລ້ວ"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ສົ່ງຮູບແລ້ວ"</string>
@@ -2481,6 +2486,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ວຽກ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ທົດສອບ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ສ່ວນກາງ"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ໂຄລນ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 0b4967a..c71c2b8e 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1805,8 +1805,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Vienos rankos režimas"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Itin blanku"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Klausos įrenginiai"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatinis paspaudimas"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Atjungta"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Prisijungta"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktyvus"</string>
@@ -1968,7 +1967,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Prašyti atrakinimo piešinio prieš atsegant"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Prašyti slaptažodžio prieš atsegant"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Įdiegė administratorius.\nEikite į nustatymus ir peržiūrėkite suteiktus leidimus"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Atnaujino administratorius"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Atnaujino administratorius.\nEikite į nustatymus ir peržiūrėkite suteiktus leidimus"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Ištrynė administratorius"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Gerai"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Akumuliatoriaus tausojimo priemonė įjungia tamsiąją temą ir apriboja arba išjungia veiklą fone, kai kuriuos vaizdinius efektus, tam tikras funkcijas bei kai kuriuos tinklo ryšius."</string>
@@ -2276,6 +2275,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Slinkti"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pristabdyti"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicija"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Slinkti aukštyn"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Slinkti žemyn"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Slinkti į kairę"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Slinkti į dešinę"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Išeiti iš slinkimo režimo"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Slinkimo skydelis"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"„<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>“ įkeltas į grupę APRIBOTA"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"išsiuntė vaizdą"</string>
@@ -2483,6 +2488,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Darbas (3)"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Bandymas"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Bendruomenės"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Darbo profilis"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privati erdvė"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonuoti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 0d19dc2..2523437 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Vienas rokas režīms"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Papildu aptumšošana"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Dzirdes aparāti"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automātiskā klikšķināšana"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Atvienota"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Pievienota"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktīva"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pirms atspraušanas pieprasīt atbloķēšanas kombināciju"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Pirms atspraušanas pieprasīt paroli"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instalēja jūsu administrators.\nPārejiet uz iestatījumiem, lai skatītu piešķirtās atļaujas."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Atjaunināja administrators"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Instalēja jūsu administrators.\nPārejiet uz iestatījumiem, lai skatītu piešķirtās atļaujas."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Dzēsa administrators"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Labi"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Akumulatora enerģijas taupīšanas režīmā tiek ieslēgts tumšais motīvs un tiek ierobežotas vai izslēgtas darbības fonā, daži vizuālie efekti, noteiktas funkcijas un noteikti tīkla savienojumi."</string>
@@ -2275,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Ritināt"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pārtraukt"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozīcija"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Pakotne “<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>” ir ievietota ierobežotā kopā."</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"nosūtīts attēls"</string>
@@ -2482,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Darbam (3.)"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testēšanai"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Kopīgs"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Darba profils"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privātā telpa"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klons"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 9889ea9..c6cb1fa 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1531,7 +1531,7 @@
     <string name="ext_media_status_unmounted" msgid="8145812017295835941">"Исфрлено"</string>
     <string name="ext_media_status_checking" msgid="159013362442090347">"Се проверува..."</string>
     <string name="ext_media_status_mounted" msgid="3459448555811203459">"Подготвено"</string>
-    <string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"Само за читање"</string>
+    <string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"Само за преглед"</string>
     <string name="ext_media_status_bad_removal" msgid="508448566481406245">"Отстранет небезбедно"</string>
     <string name="ext_media_status_unmountable" msgid="7043574843541087748">"Оштетено"</string>
     <string name="ext_media_status_unsupported" msgid="5460509911660539317">"Неподдржано"</string>
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим со една рака"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнително затемнување"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слушни помагала"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Автоматско кликнување"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Не е поврзано"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Поврзано"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Активно"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Побарај шема за откл. пред откачување"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Побарај лозинка пред откачување"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Инсталирано од администраторот.\nОдете во „Поставки“ за да ги прегледате доделените дозволи"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирано од администраторот"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Ажурирано од администраторот.\nОдете во „Поставки“ за да ги прегледате доделените дозволи"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Избришано од администраторот"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Во ред"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"„Штедачот на батерија“ вклучува „Темна тема“ и ограничува или исклучува активност во заднина, некои визуелни ефекти, одредени функции и некои мрежни врски."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Лизгање"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Паузирај"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Позиционирај"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> е ставен во корпата ОГРАНИЧЕНИ"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"испрати слика"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Работен профил 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Профил за тестирање"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Профил на заедницата"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Работен профил"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватен простор"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониран профил"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index ff07aa0..fca58a1 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1965,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"അൺപിന്നിനുമുമ്പ് അൺലോക്ക് പാറ്റേൺ ആവശ്യപ്പെടൂ"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"അൺപിന്നിനുമുമ്പ് പാസ്‌വേഡ് ആവശ്യപ്പെടൂ"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"നിങ്ങളുടെ അഡ്‌മിൻ ഇൻസ്‌റ്റാൾ ചെയ്തത്.\nനൽകിയ അനുമതികൾ കാണാൻ ക്രമീകരണത്തിലേക്ക് പോകുക"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"നിങ്ങളുടെ അഡ്‌മിൻ അപ്‌ഡേറ്റ് ചെയ്യുന്നത്"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"നിങ്ങളുടെ അഡ്‌മിൻ അപ്‌ഡേറ്റ് ചെയ്തത്.\nനൽകിയ അനുമതികൾ കാണാൻ ക്രമീകരണത്തിലേക്ക് പോകുക"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"നിങ്ങളുടെ അഡ്‌മിൻ ഇല്ലാതാക്കുന്നത്"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ശരി"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"\'ബാറ്ററി സേവർ\' ഡാർക്ക് തീം ഓണാക്കുന്നു, ഒപ്പം പശ്ചാത്തല ആക്‌റ്റിവിറ്റിയും ചില വിഷ്വൽ ഇഫക്റ്റുകളും ചില ഫീച്ചറുകളും ചില നെറ്റ്‌വർക്ക് കണക്ഷനുകളും പരിമിതപ്പെടുത്തുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു."</string>
@@ -2273,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"സ്‌ക്രോൾ ചെയ്യുക"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"താൽക്കാലികമായി നിർത്തുക"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"സ്ഥാനം"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> നിയന്ത്രിത ബക്കറ്റിലേക്ക് നീക്കി"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ചിത്രം അയച്ചു"</string>
@@ -2480,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ഔദ്യോഗികം 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ടെസ്‌റ്റ്"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"കമ്മ്യൂണൽ"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"സ്വകാര്യ സ്പേസ്"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ക്ലോൺ ചെയ്യുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 3a1e0a6..740c783 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Нэг гарын горим"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Хэт бүүдгэр"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Сонсголын төхөөрөмжүүд"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Автомат товшилт"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Салсан"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Холбогдсон"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Идэвхтэй"</string>
@@ -1821,7 +1820,7 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Томруулах"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Утасны микрофон руу сэлгэх үү?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Сонсголын төхөөрөмжийн микрофон руу сэлгэх үү?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Дуу чимээг сайжруулахын тулд эсвэл таны сонсголын төхөөрөмжийн батарей бага бол. Энэ нь зөвхөн дуудлагын үеэр таны микрофоныг сэлгэнэ."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Дуу чимээг сайжруулахын тулд эсвэл таны сонсголын төхөөрөмжийн цэнэг бага үед, зөвхөн дуудлагын үеэр таны микрофоныг сэлгэнэ."</string>
     <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Та гараас хамаарахгүй дуудлагад сонсголын төхөөрөмжийнхөө микрофоныг ашиглаж болно. Энэ нь зөвхөн дуудлагын үеэр таны микрофоныг сэлгэнэ."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Сэлгэх"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Тохиргоо"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Бэхэлснийг болиулахаас өмнө түгжээ тайлах хээ асуух"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Тогтоосныг суллахаас өмнө нууц үг асуух"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Танай админ суулгасан.\nОлгосон зөвшөөрлүүдийг харахын тулд тохиргоо руу очно уу"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Таны админ шинэчилсэн"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Танай админ шинэчилсэн.\nОлгосон зөвшөөрлийг харахын тулд тохиргоо руу очно уу"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Таны админ устгасан"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Батарей хэмнэгч нь Бараан загварыг асааж, дэвсгэрийн үйл ажиллагаа, зарим визуал эффект, тодорхой онцлогууд болон зарим сүлжээний холболтыг хязгаарлах эсвэл унтраана."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Гүйлгэх"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Түр зогсоох"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Байрлал"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>-г ХЯЗГААРЛАСАН сагс руу орууллаа"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"зураг илгээсэн"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Ажил 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Туршилт"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Нийтийн"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Ажлын профайл"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Хаалттай орон зай"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index d79dc6d..6f741d7 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एकहाती मोड"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"आणखी डिम"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"श्रवणयंत्रे"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"ऑटोक्लिक"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"डिस्कनेक्ट केले आहे"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"कनेक्ट केले आहे"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"अ‍ॅक्टिव्ह"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"अनपिन करण्‍यापूर्वी अनलॉक नमुन्यासाठी विचारा"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"अनपिन करण्‍यापूर्वी संकेतशब्दासाठी विचारा"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"तुमच्या ॲडमिनने इंस्टॉल केले आहे.\nदिलेल्या परवानग्या पाहण्यासाठी सेटिंग्जवर जा"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"आपल्या प्रशासकाने अपडेट केले"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"तुमच्या ॲडमिनने अपडेट केले आहे.\nदिलेल्या परवानग्या पाहण्यासाठी सेटिंग्जवर जा"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"आपल्या प्रशासकाने हटवले"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ओके"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"बॅटरी सेव्हर गडद थीम सुरू करते आणि बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट, ठरावीक वैशिष्ट्ये व काही नेटवर्क कनेक्शन मर्यादित किंवा बंद करते."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल करा"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"थांबवा"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"स्थिती"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> हे प्रतिबंधित बादलीमध्ये ठेवण्यात आले आहे"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"इमेज पाठवली आहे"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ऑफिस ३"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"चाचणी"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"कार्य प्रोफाइल"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"खाजगी स्पेस"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 76d4027..4b2bb4d 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1965,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Minta corak buka kunci sebelum menyahsemat"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Minta kata laluan sebelum menyahsemat"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Dipasang oleh pentadbir anda.\nAkses tetapan untuk melihat kebenaran yang diberikan"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Dikemas kini oleh pentadbir anda"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Dikemaskinikan oleh pentadbir anda.\nAkses tetapan untuk melihat kebenaran yang diberikan"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Dipadamkan oleh pentadbir anda"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Penjimat Bateri menghidupkan tema Gelap dan mengehadkan atau mematikan aktiviti latar, sesetengah kesan visual, ciri tertentu dan sesetengah sambungan rangkaian."</string>
@@ -2273,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Tatal"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Jeda"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Kedudukan"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Tatal Ke Atas"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Tatal Ke Bawah"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Tatal Ke Kiri"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Tatal ke Kanan"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Keluar Daripada Mod Tatal"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Tatal Panel"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> telah diletakkan dalam baldi TERHAD"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"menghantar imej"</string>
@@ -2480,6 +2486,7 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Kerja 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Ujian"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string>
+    <string name="profile_label_supervising" msgid="5649312778545745371">"Mengawas"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil kerja"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang persendirian"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index fc6df7a..5242155 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"လက်တစ်ဖက်သုံးမုဒ်"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ပိုမှိန်ခြင်း"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"နားကြားကိရိယာ"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"အော်တိုနှိပ်ခြင်း"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"ချိတ်ဆက်မထားပါ"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"ချိတ်ဆက်ထားသည်"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"သုံးနေသည်"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ပင်မဖြုတ်မီ လော့ခ်ဖွင့်ပုံစံကို မေးရန်"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ပင်မဖြုတ်မီမှာ စကားဝှက်ကို မေးကြည့်ရန်"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"သင့်စီမံခန့်ခွဲသူက ထည့်သွင်းထားသည်။\nပေးထားသည့် ခွင့်ပြုချက်များကို ကြည့်ရန် ဆက်တင်များသို့ သွားပါ"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"သင်၏ စီမံခန့်ခွဲသူက အပ်ဒိတ်လုပ်ထားသည်"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"သင့်စီမံခန့်ခွဲသူက အပ်ဒိတ်လုပ်ထားသည်။\nပေးထားသည့် ခွင့်ပြုချက်များကို ကြည့်ရန် ဆက်တင်များသို့ သွားပါ"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"သင်၏ စီမံခန့်ခွဲသူက ဖျက်လိုက်ပါပြီ"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"‘ဘက်ထရီ အားထိန်း’ က ‘အမှောင်နောက်ခံ’ ကို ဖွင့်ပြီး နောက်ခံလုပ်ဆောင်ချက်၊ ဖန်တီးပြသချက်အချို့၊ ဝန်ဆောင်မှုအချို့နှင့် ကွန်ရက်ချိတ်ဆက်မှုအချို့တို့ကို ကန့်သတ်သည် သို့မဟုတ် ပိတ်သည်။"</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"လှိမ့်ရန်"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ခဏရပ်ရန်"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"နေရာ"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"အပေါ်သို့ လှိမ့်ရန်"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"အောက်သို့ လှိမ့်ရန်"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ဘယ်ဘက်သို့ လှိမ့်ရန်"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ညာဘက်သို့ လှိမ့်ရန်"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"လှိမ့်ရန်မုဒ်မှ ထွက်ရန်"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"အကန့်ကို လှိမ့်ခြင်း"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ကို တားမြစ်ထားသော သိမ်းဆည်းမှုအတွင်းသို့ ထည့်ပြီးပါပြီ"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>-"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ပုံပို့ထားသည်"</string>
@@ -2481,6 +2486,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"အလုပ် ၃"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"စမ်းသပ်မှု"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"အများသုံး"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"အလုပ်ပရိုဖိုင်"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"သီးသန့်နေရာ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ပုံတူပွားရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index b8cc711..3dca9b9 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhåndsmodus"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra dimmet"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Høreapparater"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Autoklikk"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Frakoblet"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Tilkoblet"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktiv"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Krev opplåsingsmønster for å løsne apper"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Krev passord for å løsne apper"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Installert av administratoren din.\nGå til innstillingene for å se hvilke tillatelser som er gitt"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Oppdatert av administratoren din"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Oppdatert av administratoren din.\nGå til innstillingene for å se hvilke tillatelser som er gitt"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Slettet av administratoren din"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Batterisparing slår på mørkt tema og begrenser eller slår av bakgrunnsaktivitet, enkelte visuelle effekter, noen funksjoner og noen nettverkstilkoblinger."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rull"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sett på pause"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Plassér"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> er blitt plassert i TILGANGSBEGRENSET-toppmappen"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"har sendt et bilde"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Jobb 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Felles"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Jobbprofil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 5907182..13c9855 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एक हाते मोड"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"अझै मधुरो"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"हियरिङ डिभाइसहरू"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"अटोक्लिक"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"डिस्कनेक्ट गरिएको"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"कनेक्ट गरिएको"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"सक्रिय"</string>
@@ -1820,9 +1819,9 @@
     <string name="accessibility_gesture_3finger_instructional_text" msgid="1124458279366968154">"तपाईंले अर्को पटक यो सर्टकट प्रयोग गर्दा यो सुविधा खुल्ने छ। ३ वटा औँलाले स्क्रिनको पुछारबाट माथितिर स्वाइप गर्नुहोस् र तुरुन्तै औँला उठाउनुहोस्।"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"म्याग्निफिकेसन"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"फोनको माइक प्रयोग गर्ने हो?"</string>
-    <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"श्रवण यन्त्रको माइक प्रयोग गर्ने हो?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"अझ राम्रो आवाज सुनाउन वा तपाईंको श्रवण यन्त्रको ब्याट्री कम भएका खण्डमा। यसले कल भइरहेका बेला मात्र तपाईंको माइक बदल्छ।"</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"तपाईं ह्यान्ड्सफ्री तरिकाले कल गर्न आफ्नो श्रवण यन्त्रको माइक्रोफोन प्रयोग गर्न सक्नुहुन्छ। यसले कल भइरहेका बेला मात्र तपाईंको माइक बदल्छ।"</string>
+    <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"हियरिङ एडको माइक प्रयोग गर्ने हो?"</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"अझ राम्रो आवाज सुनाउन वा तपाईंको हियरिङ एडको ब्याट्री कम भएका खण्डमा। यसले कल भइरहेका बेला मात्र तपाईंको माइक बदल्छ।"</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"तपाईं ह्यान्ड्सफ्री तरिकाले कल गर्न आफ्नो हियरिङ एडको माइक्रोफोन प्रयोग गर्न सक्नुहुन्छ। यसले कल भइरहेका बेला मात्र तपाईंको माइक बदल्छ।"</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"बदल्नुहोस्"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"सेटिङ"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> मा स्विच गरिँदै छ…"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"अनपिन गर्नअघि अनलक प्याटर्न माग्नुहोस्"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"पिन निकाल्नुअघि पासवर्ड सोध्नुहोस्"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"यो प्याकेज तपाईंका एड्मिनले इन्स्टल गर्नुभएको हो।\nप्रदान गरिएका अनुमतिसम्बन्धी जानकारी हेर्न सेटिङमा जानुहोस्"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"तपाईंका प्रशासकले अद्यावधिक गर्नुभएको"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"यो प्याकेज तपाईंका एड्मिनले अपडेट गर्नुभएको हो।\nकुन कुन अनुमति दिइएका छन् भन्ने कुरा हेर्न सेटिङमा जानुहोस्"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"तपाईंका प्रशासकले मेट्नुभएको"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ठिक छ"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"ब्याट्री सेभरले अँध्यारो थिम अन गर्छ र ब्याकग्राउन्डमा हुने क्रियाकलाप, केही भिजुअल इफेक्ट, निश्चित सुविधा र केही नेटवर्क कनेक्सनहरू अफ गर्छ वा सीमित रूपमा मात्र चल्न दिन्छ।"</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल गर्नुहोस्"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"पज गर्नुहोस्"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"स्थिति"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> लाई प्रतिबन्धित बाल्टीमा राखियो"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"फोटो पठाइयो"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"कार्य प्रोफाइल ३"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"परीक्षण"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"कार्य प्रोफाइल"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"निजी स्पेस"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 98ce463..f5e3831 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Bediening met 1 hand"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dimmen"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hoortoestellen"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatisch klikken"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Verbinding verbroken"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Verbonden"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Actief"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Ontgrendelingspatroon vragen om app los te maken"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Vraag wachtwoord voor losmaken"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Geïnstalleerd door je beheerder.\nGa naar instellingen om verleende rechten te bekijken."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Geüpdatet door je beheerder"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Geüpdatet door je beheerder.\nGa naar instellingen om verleende rechten te bekijken."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Verwijderd door je beheerder"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Met Batterijbesparing wordt het donkere thema aangezet en worden achtergrondactiviteit, bepaalde visuele effecten, bepaalde functies en sommige netwerkverbindingen beperkt of uitgezet."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrollen"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauzeren"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Positie"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> is in de bucket RESTRICTED geplaatst"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"heeft een afbeelding gestuurd"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Gemeenschappelijk"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Werkprofiel"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privégedeelte"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 4931bd2e..eee243f 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1820,7 +1820,7 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ମେଗ୍ନିଫିକେସନ"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"ଫୋନ ମାଇକକୁ ସୁଇଚ କରିବେ?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"ଶ୍ରବଣ ଯନ୍ତ୍ର ମାଇକକୁ ସୁଇଚ କରିବେ?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"ଭଲ ସାଉଣ୍ଡ ପାଇଁ କିମ୍ବା ଯଦି ଆପଣଙ୍କର ଶ୍ରବଣ ଯନ୍ତ୍ର ବେଟେରୀ କମ ଥିଲେ। କଲ ସମୟରେ ଏହା କେବଳ ଆପଣଙ୍କର ମାଇକକୁ ସୁଇଚ କରିଥାଏ।"</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"ଭଲ ସାଉଣ୍ଡ ପାଇଁ କିମ୍ବା ଯଦି ଆପଣଙ୍କର ଶ୍ରବଣ ଯନ୍ତ୍ରର ବେଟେରୀ କମ ଥାଏ। କଲ ସମୟରେ ଏହା କେବଳ ଆପଣଙ୍କର ମାଇକକୁ ସୁଇଚ କରିଥାଏ।"</string>
     <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"ହେଣ୍ଡସ-ଫ୍ରି କଲିଂ ପାଇଁ ଆପଣ ଆପଣଙ୍କର ଶ୍ରବଣ ଯନ୍ତ୍ର ମାଇକ୍ରୋଫୋନ ବ୍ୟବହାର କରିପାରିବେ। କଲ ସମୟରେ ଏହା କେବଳ ଆପଣଙ୍କର ମାଇକକୁ ସୁଇଚ କରିଥାଏ।"</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"ସୁଇଚ କରନ୍ତୁ"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"ସେଟିଂସ"</string>
@@ -1965,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ଅନପିନ୍‌ କରିବା ପୂର୍ବରୁ ଲକ୍‌ ଖୋଲିବା ପାଟର୍ନ ପଚାରନ୍ତୁ"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ଅନପିନ୍‌ କରିବା ପୂର୍ବରୁ ପାସ୍‌ୱର୍ଡ ପଚାରନ୍ତୁ"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ଇନଷ୍ଟଲ କରାଯାଇଛି।\nଅନୁମୋଦିତ ଅମୁମତିଗୁଡ଼ିକ ଭ୍ୟୁ କରିବା ପାଇଁ ସେଟିଂସକୁ ଯାଆନ୍ତୁ"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"ଆପଣଙ୍କ ଆଡମିନ୍‌‌ ଅପଡେଟ୍‍ କରିଛନ୍ତି"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ଅପଡେଟ କରାଯାଇଛି।\nଅନୁମୋଦିତ ଅମୁମତିଗୁଡ଼ିକ ଭ୍ୟୁ କରିବା ପାଇଁ ସେଟିଂସକୁ ଯାଆନ୍ତୁ"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"ଆପଣଙ୍କ ଆଡମିନ୍‌‌ ଡିଲିଟ୍‍ କରିଛନ୍ତି"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ଠିକ ଅଛି"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"ବେଟେରୀ ସେଭର ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ ଇଫେକ୍ଟ, କିଛି ଫିଚର ଏବଂ କିଛି ନେଟୱାର୍କ ସଂଯୋଗକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ।"</string>
@@ -2273,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ବିରତ କରନ୍ତୁ"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"ସ୍ଥିତି"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>କୁ ପ୍ରତିବନ୍ଧିତ ବକେଟରେ ରଖାଯାଇଛି"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ଏକ ଛବି ପଠାଯାଇଛି"</string>
@@ -2480,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ୱାର୍କ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ଟେଷ୍ଟ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"କମ୍ୟୁନାଲ"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ୱାର୍କ ପ୍ରୋଫାଇଲ"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"କ୍ଲୋନ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 80ed484..6632fb0 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1965,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ਅਨਪਿੰਨ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਅਣਲਾਕ ਪੈਟਰਨ ਵਾਸਤੇ ਪੁੱਛੋ"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ਅਣਪਿੰਨ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਪਾਸਵਰਡ ਮੰਗੋ"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਸਥਾਪਤ ਕੀਤਾ ਗਿਆ।\nਦਿੱਤੀਆਂ ਗਈਆਂ ਇਜਾਜ਼ਤਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ।\nਦਿੱਤੀਆਂ ਗਈਆਂ ਇਜਾਜ਼ਤਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਮਿਟਾਇਆ ਗਿਆ"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ਠੀਕ ਹੈ"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"ਬੈਟਰੀ ਸੇਵਰ ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ, ਕੁਝ ਖਾਸ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਅਤੇ ਕੁਝ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨਾਂ ਨੂੰ ਸੀਮਤ ਜਾਂ ਬੰਦ ਕਰਦਾ ਹੈ।"</string>
@@ -2273,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ਸਕ੍ਰੋਲ ਕਰੋ"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ਰੋਕੋ"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"ਸਥਿਤੀ"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ਨੂੰ ਪ੍ਰਤਿਬੰਧਿਤ ਖਾਨੇ ਵਿੱਚ ਪਾਇਆ ਗਿਆ ਹੈ"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ਚਿੱਤਰ ਭੇਜਿਆ ਗਿਆ"</string>
@@ -2480,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ਕੰਮ ਸੰਬੰਧੀ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ਜਾਂਚ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ਭਾਈਚਾਰਕ"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ਕਲੋਨ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index efae947..2cdac419 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1805,8 +1805,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Tryb jednej ręki"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatkowe przyciemnienie"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Urządzenia słuchowe"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatyczne kliknięcie"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Rozłączone"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Połączone"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktywne"</string>
@@ -1968,7 +1967,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Aby odpiąć, poproś o wzór odblokowania"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Aby odpiąć, poproś o hasło"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Zainstalowany przez administratora.\nOtwórz ustawienia, aby wyświetlić przyznane uprawnienia"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Zaktualizowany przez administratora"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Zaktualizowany przez administratora.\nAby zobaczyć przyznane uprawnienia, przejdź do ustawień"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Usunięty przez administratora"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne, pewne funkcje oraz wybrane połączenia sieciowe."</string>
@@ -2276,6 +2275,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Przewijanie"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Wstrzymaj"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozycja"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Umieszczono pakiet <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> w zasobniku danych RESTRICTED"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"wysłano obraz"</string>
@@ -2483,6 +2494,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Służbowy 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testowy"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Wspólny"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil służbowy"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Przestrzeń prywatna"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 4036d636..52dc72c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo para uma mão"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Tela ainda mais escura"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparelhos auditivos"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Clique automático"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Desconectado"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Conectado"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Ativo"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pedir padrão de desbloqueio antes de liberar"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Pedir senha antes de liberar"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instalado pelo administrador.\nAcesse as configurações para conferir as permissões concedidas"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu administrador"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Atualizado pelo administrador.\nAcesse as configurações para conferir as permissões concedidas"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Excluído pelo seu administrador"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"A Economia de bateria ativa o tema escuro e limita ou desativa atividades em segundo plano, alguns efeitos visuais, recursos específicos e algumas conexões de rede."</string>
@@ -2275,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rolar"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posição"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> foi colocado no intervalo \"RESTRITO\""</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string>
@@ -2482,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Público"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabalho"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index df3f9c6..a8400e2 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo para uma mão"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mais escuro"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Dispositivos auditivos"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Clique automático"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Desligado"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Ligado"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Ativo"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pedir padrão de desbloqueio antes de soltar"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Pedir palavra-passe antes de soltar"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instalado pelo seu administrador.\nAceda às definições para ver as autorizações concedidas"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu gestor"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Atualizado pelo seu administrador.\nAceda às definições para ver as autorizações concedidas"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado pelo seu gestor"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"A Poupança de bateria ativa o tema escuro e limita ou desativa a atividade em segundo plano, alguns efeitos visuais, determinadas funcionalidades e algumas ligações de rede."</string>
@@ -2275,6 +2274,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Deslocar"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posição"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Deslocar página para cima"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Deslocar página para baixo"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Deslocar página para a esquerda"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Deslocar página para a direita"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Sair do modo de deslocamento"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Deslocar painel"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> foi colocado no contentor RESTRITO."</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string>
@@ -2482,6 +2487,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Comum"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabalho"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 4036d636..52dc72c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo para uma mão"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Tela ainda mais escura"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparelhos auditivos"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Clique automático"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Desconectado"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Conectado"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Ativo"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pedir padrão de desbloqueio antes de liberar"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Pedir senha antes de liberar"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instalado pelo administrador.\nAcesse as configurações para conferir as permissões concedidas"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu administrador"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Atualizado pelo administrador.\nAcesse as configurações para conferir as permissões concedidas"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Excluído pelo seu administrador"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"A Economia de bateria ativa o tema escuro e limita ou desativa atividades em segundo plano, alguns efeitos visuais, recursos específicos e algumas conexões de rede."</string>
@@ -2275,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rolar"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posição"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> foi colocado no intervalo \"RESTRITO\""</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string>
@@ -2482,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Público"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabalho"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index b12af39..8ad6d02 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1804,8 +1804,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modul cu o mână"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminozitate redusă suplimentar"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparate auditive"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Clic automat"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Deconectat"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Conectat"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Activ"</string>
@@ -1967,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Solicită mai întâi modelul pentru deblocare"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Solicită parola înainte de a anula fixarea"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instalat de administrator.\nAccesează setările ca să vezi permisiunile acordate."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizat de administrator"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Instalat de administrator.\nAccesează setările ca să vezi permisiunile acordate"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Șters de administrator"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Economisirea bateriei activează tema întunecată și restricționează sau dezactivează activitatea în fundal, unele efecte vizuale, alte funcții și câteva conexiuni la rețea."</string>
@@ -2275,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Derulează"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Întrerupe"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Poziție"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> a fost adăugat la grupul RESTRICȚIONATE"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"a trimis o imagine"</string>
@@ -2482,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Serviciu 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Comun"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil de serviciu"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spațiu privat"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonă"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 4911fc6..0ee2542 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1805,8 +1805,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим управления одной рукой"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнительное уменьшение яркости"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слуховые аппараты"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Автонажатие"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Отключено"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Подключено"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Активно"</string>
@@ -1968,7 +1967,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Запрашивать графический ключ"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Запрашивать пароль"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Установлено администратором.\nЧтобы посмотреть предоставленные разрешения, перейдите в настройки."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Обновлено администратором"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Обновлено администратором.\nЧтобы посмотреть предоставленные разрешения, перейдите в настройки."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Удалено администратором"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"В режиме энергосбережения включается тёмная тема, ограничиваются или отключаются фоновые процессы, а также некоторые визуальные эффекты, часть функций и сетевых подключений."</string>
@@ -2276,6 +2275,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Прокрутить"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Приостановить"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Положение"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Приложение \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" помещено в категорию с ограниченным доступом."</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"Отправлено изображение"</string>
@@ -2483,6 +2494,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Рабочий 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Тестовый"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Совместный"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Рабочий профиль"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частное пространство"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонированный"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index b11e442..0d3cd7e 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"තනි අත් ප්‍රකාරය"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"තවත් අඳුරු"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ශ්‍රවණ උපාංග"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"ස්වයං ක්ලික් කිරීම"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"විසන්ධි විය"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"සම්බන්ධිතයි"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"සක්‍රිය"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ගැලවීමට පෙර අගුළු අරින රටාව සඳහා අසන්න"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ගැලවීමට පෙර මුරපදය විමසන්න"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"ඔබේ පරිපාලකයා විසින් ස්ථාපන කරනු ලබයි.\nදෙන ලද අවසර බැලීමට සැකසීම් වෙත යන්න"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"ඔබගේ පරිපාලක මඟින් යාවත්කාලීන කර ඇත"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"ඔබේ පරිපාලක විසින් යාවත්කාලීන කරන ලදි.\n ලබා දී ඇති අවසර බැලීමට සැකසීම් වෙත යන්න"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"ඔබගේ පරිපාලක මඟින් මකා දමා ඇත"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"හරි"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"බැටරි සුරැකුම අඳුරු තේමාව ක්‍රියාත්මක කර පසුබිම් ක්‍රියාකාරකම්, සමහර දෘශ්‍ය ප්‍රයෝග, යම් විශේෂාංග සහ සමහර ජාල සම්බන්ධතා සීමා හෝ ක්‍රියාවිරහිත කරයි."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"අනුචලනය කරන්න"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"විරාම කරන්න"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"ස්ථානය"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> අවහිර කළ බාල්දියට දමා ඇත"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"රූපයක් එව්වා"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"කාර්යාලය 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"පරීක්ෂණය"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"වාර්ගික"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"කාර්යාල පැතිකඩ"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"රහසිගත අවකාශය"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ක්ලෝන කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 47bee0a..e3855d1 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1805,8 +1805,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Režim jednej ruky"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mimoriadne stmavenie"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Načúvacie zariadenia"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatické kliknutie"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Odpojené"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Pripojené"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktívne"</string>
@@ -1823,8 +1822,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Zväčšenie"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Chcete prepnúť na mikrofón telefónu?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Chcete prepnúť na mikrofón načúvadla?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Keď chcete zlepšiť zvuk alebo ak je batéria načúvadla slabá. Týmto iba prepnete mikrofón počas hovoru."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Volať handsfree môžete pomocou mikrofónu načúvadla. Týmto iba prepnete mikrofón počas hovoru."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Pre lepší zvuk alebo ak je batéria načúvadla takmer vybitá. Mikrofón sa prepne iba na čas hovoru."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Pomocou mikrofónu načúvadla môžete volať handsfree. Mikrofón sa prepne iba na čas hovoru."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Prepnúť"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Nastavenia"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Prepína sa na účet <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1968,7 +1967,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pred uvoľnením požiadať o bezpečnostný vzor"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Pred odopnutím požiadať o heslo"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Nainštaloval správca.\nAk si chcete zobraziť udelené povolenia, prejdite do nastavení."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Aktualizoval správca"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Aktualizoval správca.\nAk si chcete zobraziť udelené povolenia, prejdite do nastavení."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Odstránil správca"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Šetrič batérie zapne tmavý motív a obmedzí alebo vypne aktivitu na pozadí, niektoré vizuálne efekty, určité funkcie a niektoré pripojenia k sieti."</string>
@@ -2276,6 +2275,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Posúvať"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pozastaviť"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozícia"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Balík <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> bol vložený do kontajnera OBMEDZENÉ"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"odoslal(a) obrázok"</string>
@@ -2483,6 +2494,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"3. pracovný"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Spoločné"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Pracovný profil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Súkromný priestor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 208c174..b0ebb5f 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1805,8 +1805,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enoročni način"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Zelo zatemnjen zaslon"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Slušni pripomočki"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Samodejni klik"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Brez povezave"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Povezano"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktivno"</string>
@@ -1822,9 +1821,9 @@
     <string name="accessibility_gesture_3finger_instructional_text" msgid="1124458279366968154">"Funkcija se bo odprla, ko boste naslednjič uporabili to bližnjico. S tremi prsti povlecite navzgor z dna zaslona in hitro spustite."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Povečava"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Želite preklopiti na mikrofon telefona?"</string>
-    <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Želite preklopiti na mikrofon za slušni aparat?"</string>
+    <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Želite preklopiti na mikrofon slušnega aparata?"</string>
     <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Za boljši zvok ali pri skoraj prazni bateriji slušnega aparata. S tem preklopite mikrofon samo med klicem."</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Mikrofon za slušni aparat lahko uporabljate za prostoročno klicanje. S tem preklopite mikrofon samo med klicem."</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Mikrofon slušnega aparata lahko uporabljate za prostoročno klicanje. S tem preklopite mikrofon samo med klicem."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Preklopi"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Nastavitve"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Preklapljanje na uporabnika <xliff:g id="NAME">%1$s</xliff:g> …"</string>
@@ -1968,7 +1967,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Pred odpenjanjem vprašaj za vzorec za odklepanje"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Pred odpenjanjem vprašaj za geslo"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Namestil skrbnik.\nV nastavitvah si oglejte odobrena dovoljenja."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Posodobil skrbnik"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Posodobil skrbnik.\nV nastavitvah si oglejte odobrena dovoljenja"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisal skrbnik"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"V redu"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Funkcija varčevanja z energijo baterije vklopi temno temo ter omeji ali izklopi dejavnost v ozadju, nekatere vizualne učinke, določene funkcije in nekatere omrežne povezave."</string>
@@ -2276,6 +2275,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Drsenje"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Začasna zaustavitev"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Položaj"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Pomik gor"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Pomik dol"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Pomik levo"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Pomik desno"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Izhod iz načina pomikanja"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Podokno za pomikanje"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je bil dodan v segment OMEJENO"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslal(-a) sliko"</string>
@@ -2483,6 +2488,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Delo 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Preizkus"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Skupno"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Delovni profil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Zasebni prostor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 5f84908..0f99307 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modaliteti i përdorimit me një dorë"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Shumë më i zbehtë"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Pajisjet e dëgjimit"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Klikimi automatik"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Shkëputur"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Lidhur"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktive"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Kërko motivin e shkyçjes para heqjes së gozhdimit"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Kërko fjalëkalim para heqjes nga gozhdimi."</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Instaluar nga administratori.\nShko te cilësimet për të shikuar lejet e dhëna"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Përditësuar nga administratori"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Përditësuar nga administratori.\nShko te cilësimet për të shikuar lejet e dhëna"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Fshirë nga administratori"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Në rregull"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"\"Kursyesi i baterisë\" aktivizon \"Temën e errët\" dhe kufizon ose çaktivizon aktivitetin në sfond, disa efekte vizuale, veçori të caktuara dhe disa lidhje të rrjetit."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Lëviz"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Vendos në pauzë"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicioni"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> është vendosur në grupin E KUFIZUAR"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"dërgoi një imazh"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Puna 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"I përbashkët"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profili i punës"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Hapësira private"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 61102d3..4827c0c 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1966,7 +1966,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Тражи шаблон за откључавање пре откачињања"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Тражи лозинку пре откачињања"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Инсталирао је администратор.\nИдите у подешавања да бисте видели одобрене дозволе"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирао је администратор"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Ажурирао је администратор.\nИдите у подешавања да бисте видели одобрене дозволе"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Избрисао је администратор"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Потврди"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Уштеда батерије укључује тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте, одређене функције и неке мрежне везе."</string>
@@ -2274,6 +2274,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Скролујте"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Паузирај"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Позиција"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> је додат у сегмент ОГРАНИЧЕНО"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"је послао/ла слику"</string>
@@ -2481,6 +2493,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Посао 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Тест"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Заједничко"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Пословни профил"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватан простор"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонирано"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index d5fc694..c471f06 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhandsläge"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extradimmat"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hörhjälpmedel"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Automatiskt klick"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Frånkopplad"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Ansluten"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktiv"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Be om upplåsningsmönster innan skärmen slutar fästas"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Be om lösenord innan skärmen slutar fästas"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Har installerats av administratören.\nÖppna inställningarna för att se behörigheter som beviljats"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Administratören uppdaterade paketet"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Har uppdaterats av administratören.\nÖppna inställningarna för att se behörigheter som beviljats"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administratören raderade paketet"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"I batterisparläget aktiveras mörkt tema medan bakgrundsaktivitet, vissa visuella effekter och funktioner samt vissa nätverksanslutningar begränsas eller inaktiveras."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrolla"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausa"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> har placerats i hinken RESTRICTED"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"har skickat en bild"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Arbete 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Allmän"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Jobbprofil"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat utrymme"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 49e9f1c..31f5592 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Hali ya kutumia kwa mkono mmoja"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Kipunguza mwangaza zaidi"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Vifaa vya kusaidia kusikia"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Kubofya kiotomatiki"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Haijaunganishwa"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Imeunganishwa"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Inatumika"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Omba mchoro wa kufungua kabla hujabandua"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Omba nenosiri kabla hujabandua"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Kimewekwa na msimamizi wako.\nNenda kwenye mipangilio ili uone ruhusa zilizotolewa"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Imesasishwa na msimamizi wako"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Imesasishwa na msimamizi wako.\nNenda kwenye mipangilio ili uone ruhusa zilizotolewa"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Imefutwa na msimamizi wako"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Sawa"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Kiokoa Betri huwasha Mandhari meusi na kudhibiti au kuzima shughuli za chinichini, baadhi ya madoido yanayoonekana, vipengele fulani na baadhi ya miunganisho ya mtandao."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Sogeza"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sitisha"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Nafasi"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> kimewekwa katika kikundi KILICHODHIBITIWA"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"alituma picha"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Wa 3 wa Kazini"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Jaribio"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Unaoshirikiwa"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Wasifu wa kazini"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Sehemu ya faragha"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nakala"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index dad40dc..36b82d9 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ஒற்றைக் கைப் பயன்முறை"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"மிகக் குறைவான வெளிச்சம்"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"செவித்துணைக் கருவிகள்"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"ஆட்டோ கிளிக்"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"இணைப்புநீக்கப்பட்டது"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"இணைக்கப்பட்டுள்ளது"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"செயலில் உள்ளது"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"அகற்றும் முன் அன்லாக் பேட்டர்னைக் கேள்"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"அகற்றும் முன் கடவுச்சொல்லைக் கேள்"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"உங்கள் நிர்வாகி நிறுவியுள்ளார்.\nவழங்கப்பட்டுள்ள அனுமதிகளை பார்க்க அமைப்புகளுக்குச் செல்லவும்"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"உங்கள் நிர்வாகி புதுப்பித்துள்ளார்"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"உங்கள் நிர்வாகி புதுப்பித்துள்ளார்.\nவழங்கப்பட்டுள்ள அனுமதிகளைப் பார்க்க அமைப்புகளுக்குச் செல்லவும்"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"உங்கள் நிர்வாகி நீக்கியுள்ளார்"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"சரி"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"பேட்டரி சேமிப்பான் அம்சம் டார்க் தீமை இயக்குவதோடு பின்னணிச் செயல்பாடு, சில விஷுவல் எஃபக்ட்கள், குறிப்பிட்ட அம்சங்கள், சில நெட்வொர்க் இணைப்புகள் ஆகியவற்றைக் கட்டுப்படுத்தும் அல்லது முடக்கும்."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"நகர்த்தும்"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"இடைநிறுத்து"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"நிலை"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> என்பதை வரம்பிடப்பட்ட பக்கெட்திற்குள் சேர்க்கப்பட்டது"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"படம் அனுப்பப்பட்டது"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"பணி 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"பரிசோதனை"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"பொது"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"பணிக் கணக்கு"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ரகசிய இடம்"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"குளோன்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 322a703..74ab1e8 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"వన్-హ్యాండెడ్ మోడ్"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ఎక్స్‌ట్రా డిమ్"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"వినికిడి పరికరాలు"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"ఆటో-క్లిక్"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"డిస్‌కనెక్ట్ అయింది"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"కనెక్ట్ చేయబడింది"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"యాక్టివ్"</string>
@@ -1821,7 +1820,7 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"మ్యాగ్నిఫికేషన్"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"ఫోన్ మైక్‌కు మారాలా?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"వినికిడి పరికరం మైక్‌కు మారాలా?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"మెరుగైన సౌండ్ కోసం లేదా మీ వినికిడి పరికరం బ్యాటరీ తక్కువగా ఉన్నప్పుడు. ఇది కాల్ సమయంలో మాత్రమే మీ మైక్‌ను మారుస్తుంది."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"మెరుగైన సౌండ్ కావాల్సినప్పుడు లేదా మీ వినికిడి పరికరం బ్యాటరీ తక్కువగా ఉన్నప్పుడు ఇది ఉపయోగపడుతుంది. ఇది కాల్ సమయంలో మాత్రమే మీ మైక్‌ను మారుస్తుంది."</string>
     <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"హ్యాండ్స్-ఫ్రీ కాలింగ్ కోసం మీరు మీ వినికిడి పరికరాన్ని ఉపయోగించవచ్చు. ఇది కాల్ సమయంలో మాత్రమే మీ మైక్‌ను మారుస్తుంది."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"మారండి"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"సెట్టింగ్‌లు"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"అన్‌పిన్ చేయడానికి ముందు అన్‌లాక్ ఆకృతి కోసం అడుగు"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"అన్‌పిన్ చేయడానికి ముందు పాస్‌వర్డ్ కోసం అడుగు"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"మీ అడ్మిన్ ఇన్‌స్టాల్ చేశారు.\nసెట్టింగ్‌లకు వెళ్లి, మంజూరు చేసిన అనుమతులు చూడండి"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"మీ నిర్వాహకులు అప్‌డేట్ చేశారు"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"మీ అడ్మిన్ అప్‌డేట్ చేశారు.\nసెట్టింగ్‌లకు వెళ్లి, మంజూరు చేసిన అనుమతులను చూడండి"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"మీ నిర్వాహకులు తొలగించారు"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"సరే"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్‌గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్‌లు, నిర్దిష్ట ఫీచర్‌లు, ఇంకా కొన్ని నెట్‌వర్క్ కనెక్షన్‌లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"స్క్రోల్ చేయండి"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"పాజ్ చేయండి"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"స్థానం"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"పైకి స్క్రోల్ చేయండి"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"కిందికి స్క్రోల్ చేయండి"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ఎడమ వైపునకు స్క్రోల్ చేయండి"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"కుడి వైపునకు స్క్రోల్ చేయండి"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"స్క్రోల్ మోడ్ నుండి ఎగ్జిట్ అవ్వండి"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"ప్యానెల్‌కు స్క్రోల్ చేయండి"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> పరిమితం చేయబడిన బకెట్‌లో ఉంచబడింది"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ఇమేజ్‌ను పంపారు"</string>
@@ -2481,6 +2486,7 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"ఆఫీస్ 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"పరీక్ష"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"కమ్యూనల్"</string>
+    <string name="profile_label_supervising" msgid="5649312778545745371">"పర్యవేక్షిస్తున్నారు"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"వర్క్ ప్రొఫైల్"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ప్రైవేట్ స్పేస్"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"క్లోన్"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 107a59f..b67f382 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1965,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ขอรูปแบบการปลดล็อกก่อนเลิกปักหมุด"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ขอรหัสผ่านก่อนเลิกปักหมุด"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"ติดตั้งโดยผู้ดูแลระบบของคุณ\nไปที่การตั้งค่าเพื่อดูสิทธิ์ที่ได้รับอนุญาต"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"อัปเดตโดยผู้ดูแลระบบ"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"อัปเดตโดยผู้ดูแลระบบของคุณ\nไปที่การตั้งค่าเพื่อดูสิทธิ์ที่ได้รับอนุญาต"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"ลบโดยผู้ดูแลระบบ"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ตกลง"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"โหมดประหยัดแบตเตอรี่จะเปิดธีมมืดและจำกัดหรือปิดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง ฟีเจอร์บางส่วน และการเชื่อมต่อบางเครือข่าย"</string>
@@ -2273,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"เลื่อน"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"หยุดชั่วคราว"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"วางตำแหน่ง"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"ใส่ <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ในที่เก็บข้อมูลที่ถูกจำกัดแล้ว"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ส่งรูปภาพ"</string>
@@ -2480,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"งาน 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ทดสอบ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"ส่วนกลาง"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"โปรไฟล์งาน"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"พื้นที่ส่วนตัว"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"โคลน"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 9788327..3c38088 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-Hand mode"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Mga hearing device"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Autoclick"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Nadiskonekta"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Nakakonekta"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Aktibo"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Humingi ng pattern sa pag-unlock bago mag-unpin"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Humingi ng password bago mag-unpin"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Na-install ng iyong admin.\nPumunta sa mga setting para makita ang mga ibinigay na pahintulot"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Na-update ng iyong admin"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Na-update ng iyong admin.\nPumunta sa mga setting para makita ang mga ibinigay na pahintulot"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Na-delete ng iyong admin"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Ino-on ng Pantipid ng Baterya ang Madilim na tema at nililimitahan o ino-off nito ang aktibidad sa background, ilang visual effect, ilang partikular na feature, at ilang koneksyon sa network."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Mag-scroll"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"I-pause"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisyon"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Inilagay ang <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> sa PINAGHIHIGPITANG bucket"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"nagpadala ng larawan"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Trabaho 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profile sa trabaho"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Pribadong space"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 095a3da..8119d7d 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -962,7 +962,7 @@
     <string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
     <string name="eventTypeCustom" msgid="3257367158986466481">"Özel"</string>
     <string name="eventTypeBirthday" msgid="7770026752793912283">"Doğum günü"</string>
-    <string name="eventTypeAnniversary" msgid="4684702412407916888">"Yıldönümü"</string>
+    <string name="eventTypeAnniversary" msgid="4684702412407916888">"Yıl dönümü"</string>
     <string name="eventTypeOther" msgid="530671238533887997">"Diğer"</string>
     <string name="emailTypeCustom" msgid="1809435350482181786">"Özel"</string>
     <string name="emailTypeHome" msgid="1597116303154775999">"Ev"</string>
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Tek El modu"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra loş"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"İşitme cihazları"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Otomatik tıklama"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Bağlı değil"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Bağlı"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Etkin"</string>
@@ -1821,7 +1820,7 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Büyütme"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Telefon mikrofonuna geçilsin mi?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"İşitme cihazı mikrofonuna geçilsin mi?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Daha iyi ses kalitesi için veya işitme cihazınızın pili azaldığında Bu işlem yalnızca görüşme sırasında mikrofonunuzu değiştirir."</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Daha iyi ses kalitesi için veya işitme cihazınızın pili azaldığında. Bu işlem yalnızca görüşme sırasında mikrofonunuzu değiştirir."</string>
     <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Eller serbest modunda arama yapmak için işitme cihazı mikrofonunu kullanabilirsiniz. Bu işlem yalnızca görüşme sırasında mikrofonunuzu değiştirir."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Geçiş yap"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Ayarlar"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Sabitlemeyi kaldırmadan önce kilit açma desenini sor"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Sabitlemeyi kaldırmadan önce şifre sor"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Yöneticiniz tarafından yüklendi.\nVerilen izinleri görüntülemek için ayarlara gidin"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Yöneticiniz tarafından güncellendi"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Yöneticiniz tarafından güncellendi.\nVerilen izinleri görüntülemek için ayarlara gidin"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Yöneticiniz tarafından silindi"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"Tamam"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Pil Tasarrufu, Koyu temayı açıp arka plan etkinliğini, bazı görsel efektleri, belirli özellikleri ve bazı ağ bağlantılarını sınırlandırır veya kapatır."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Kaydırma"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Duraklatma"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Konum"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> KISITLANMIŞ gruba yerleştirildi"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"bir resim gönderildi"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"İş 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Paylaşılan"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"İş profili"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Özel alan"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index bc05f49..cda2af4 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1805,8 +1805,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим керування однією рукою"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Додаткове зменшення яскравості"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слухові апарати"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Автоматичне натискання"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Від’єднано"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Під’єднано"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Активний"</string>
@@ -1968,7 +1967,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Запитувати ключ розблокування перед відкріпленням"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Запитувати пароль перед відкріпленням"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Установлено адміністратором.\nПерейдіть у налаштування, щоб переглянути надані дозволи."</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Оновлено адміністратором"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Оновлено адміністратором.\nПерейдіть у налаштування, щоб переглянути надані дозволи."</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Видалено адміністратором"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"У режимі енергозбереження вмикається темна тема й обмежуються чи вимикаються дії у фоновому режимі, а також деякі візуальні ефекти, функції та з’єднання з мережами."</string>
@@ -2276,6 +2275,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Прокрутити"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Призупинити"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Змінити позицію"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" додано в сегмент з обмеженнями"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"надіслано зображення"</string>
@@ -2483,6 +2494,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Робочий профіль 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Тестовий профіль"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Спільний профіль"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Робочий профіль"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватний простір"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Копія профілю"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 7794c9a..df87d192 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -21,7 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="byteShort" msgid="202579285008794431">"بائٹس"</string>
-    <string name="fileSizeSuffix" msgid="4233671691980131257">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
+    <string name="fileSizeSuffix" msgid="4233671691980131257">"<xliff:g id="NUMBER">%1$s</xliff:g>‎ <xliff:g id="UNIT">%2$s</xliff:g>"</string>
     <string name="untitled" msgid="3381766946944136678">"‏‎&gt;‎بلا عنوان‎&lt;‎"</string>
     <string name="emptyPhoneNumber" msgid="5812172618020360048">"(کوئی فون نمبر نہیں ہے)"</string>
     <string name="unknownName" msgid="7078697621109055330">"نامعلوم"</string>
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ایک ہاتھ کی وضع"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"اضافی مدھم"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"سماعتی آلات"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"خودکار کلک"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"غیر منسلک ہے"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"منسلک ہے"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"فعال"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"پن ہٹانے سے پہلے غیر مقفل کرنے کا پیٹرن طلب کریں"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"پن ہٹانے سے پہلے پاس ورڈ طلب کریں"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"آپ کے منتظم نے انسٹال کیا ہے۔\nدی گئی اجازتیں دیکھنے کیلئے ترتیبات پر جائیں"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"آپ کے منتظم کے ذریعے اپ ڈیٹ کیا گیا"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"آپ کے منتظم نے اپ ڈیٹ کیا ہے۔\nدی گئی اجازتیں دیکھنے کیلئے ترتیبات پر جائیں"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"آپ کے منتظم کے ذریعے حذف کیا گیا"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"ٹھیک ہے"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"بیٹری سیور گہری تھیم کو آن کرتی ہے اور پس منظر کی سرگرمی، کچھ بصری اثرات، مخصوص خصوصیات اور کچھ نیٹ ورک کنکشنز کو محدود یا آف کرتی ہے۔"</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"اسکرول کریں"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"موقوف کریں"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"پوزیشن"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"اوپر اسکرول کریں"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"نیچے اسکرول کریں"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"بائیں اسکرول کریں"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"دائیں اسکرول کریں"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"اسکرول موڈ سے باہر نکلیں"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"اسکرول پینل"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> کو پابند کردہ بکٹ میں رکھ دیا گیا ہے"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ایک تصویر بھیجی"</string>
@@ -2481,6 +2486,7 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"تیسری دفتری پروفائل"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"ٹیسٹ"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"کمیونل"</string>
+    <string name="profile_label_supervising" msgid="5649312778545745371">"نگرانی کرنے والی"</string>
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"دفتری پروفائل"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"پرائیویٹ اسپیس"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"کلون"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 95a44bb..fc2bd4b 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Ixcham rejim"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Juda xira"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Eshitish qurilmalari"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Avtoklik"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Uzildi"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Ulandi"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Faol"</string>
@@ -1819,9 +1818,9 @@
     <string name="accessibility_gesture_instructional_text" msgid="4133877896011098550">"Keyingi safar shu buyruqdan foydalanganingizda funksiya ochiladi. Ekranning pastidan 2 barmoq bilan tepaga suring va darhol qoʻyib yuboring."</string>
     <string name="accessibility_gesture_3finger_instructional_text" msgid="1124458279366968154">"Keyingi safar shu buyruqdan foydalanganingizda funksiya ochiladi. Ekranning pastidan 3 barmoq bilan tepaga suring va darhol qoʻyib yuboring."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Kattalashtirish"</string>
-    <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Telefon mikrofoniga almashtirilsinmi?"</string>
-    <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Eshitish moslamasi mikrofoniga almashtirilsinmi?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Tovush yaxshilanishi uchun yoki eshitish moslamasi batareyasi quvvati kam boʻlsa. Bu faqat chaqiruv paytida mikrofonni almashtiradi."</string>
+    <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"Telefon mikrofoniga almashilsinmi?"</string>
+    <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"Eshitish moslamasi mikrofoniga almashilsinmi?"</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"Tovush sifati sizni qoniqtirmasa yoki eshitish moslamasi batareyasi quvvati kamligida telefon mikrofonidan foydalanishingiz mumkin. Mikrofon faqat chaqiruv vaqtida almashadi."</string>
     <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"Garniturali chaqiruv uchun eshitish moslamasi mikrofonidan foydalanish mumkin. Bu faqat chaqiruv paytida mikrofonni almashtiradi."</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"Almashtirish"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"Sozlamalar"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Yechishdan oldin grafik kalit so‘ralsin"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Bo‘shatishdan oldin parol so‘ralsin"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Administrator oʻrnatgan.\nBerilgan ruxsatlarni koʻrish uchun sozlamalarni oching"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Administrator tomonidan yangilangan"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Administrator yangilagan.\nBerilgan ruxsatlarni koʻrish uchun sozlamalarni oching"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administrator tomonidan o‘chirilgan"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Quvvat tejash funksiyasi Tungi mavzuni va cheklovlarni yoqadi hamda fondagi harakatlar, vizual effektlar, ayrim funksiyalar va tarmoq aloqalari kabi boshqa funksiyalarni faolsizlantiradi yoki cheklaydi."</string>
@@ -2274,6 +2273,12 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Aylantirish"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauza"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Joylashuvi"</string>
+    <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Tepaga varaqlash"</string>
+    <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Pastga varaqlash"</string>
+    <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Chapga varaqlash"</string>
+    <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Oʻngga varaqlash"</string>
+    <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Varaqlash rejimidan chiqish"</string>
+    <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Varaqlash paneli"</string>
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> cheklangan turkumga joylandi"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"rasm yuborildi"</string>
@@ -2481,6 +2486,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Ish 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Umumiy"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Ish profili"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Maxfiy makon"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nusxalash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index bb9b797..4141af0 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Chế độ một tay"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Siêu tối"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Thiết bị trợ thính"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Tự động nhấp"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Đã ngắt kết nối"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Đã kết nối"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Đang hoạt động"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Hỏi hình mở khóa trước khi bỏ ghim"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Hỏi mật khẩu trước khi bỏ ghim"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Do quản trị viên của bạn cài đặt.\nChuyển đến phần cài đặt để xem các quyền được cấp"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Do quản trị viên của bạn cập nhật"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Do quản trị viên của bạn cập nhật.\nChuyển đến phần cài đặt để xem các quyền được cấp"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Do quản trị viên của bạn xóa"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Trình tiết kiệm pin sẽ bật Giao diện tối, đồng thời hạn chế hoặc tắt hoạt động chạy trong nền, một số hiệu ứng hình ảnh, các tính năng nhất định và một số đường kết nối mạng."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Cuộn"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Tạm dừng"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Vị trí"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Đã đưa <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> vào bộ chứa BỊ HẠN CHẾ"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"đã gửi hình ảnh"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Công việc 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Kiểm thử"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Dùng chung"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Hồ sơ công việc"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Không gian riêng tư"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nhân bản"</string>
diff --git a/core/res/res/values-w192dp/dimens_watch.xml b/core/res/res/values-w192dp/dimens_watch.xml
new file mode 100644
index 0000000..a3730aa
--- /dev/null
+++ b/core/res/res/values-w192dp/dimens_watch.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+    <!--  watch's indeterminate progress bar dimens based on the current screen size -->
+    <dimen name="loader_horizontal_min_width_watch">67dp</dimen>
+    <dimen name="loader_horizontal_min_height_watch">15dp</dimen>
+</resources>
diff --git a/core/res/res/values-w204dp/dimens_watch.xml b/core/res/res/values-w204dp/dimens_watch.xml
new file mode 100644
index 0000000..3509474
--- /dev/null
+++ b/core/res/res/values-w204dp/dimens_watch.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+    <!--  watch's indeterminate progress bar dimens based on the current screen size -->
+    <dimen name="loader_horizontal_min_width_watch">70dp</dimen>
+    <dimen name="loader_horizontal_min_height_watch">15dp</dimen>
+</resources>
diff --git a/core/res/res/values-w216dp/dimens_watch.xml b/core/res/res/values-w216dp/dimens_watch.xml
new file mode 100644
index 0000000..96d80ab
--- /dev/null
+++ b/core/res/res/values-w216dp/dimens_watch.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+    <!--  watch's indeterminate progress bar dimens based on the current screen size -->
+    <dimen name="loader_horizontal_min_width_watch">74dp</dimen>
+    <dimen name="loader_horizontal_min_height_watch">16dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-w228dp/dimens_watch.xml b/core/res/res/values-w228dp/dimens_watch.xml
new file mode 100644
index 0000000..960e322
--- /dev/null
+++ b/core/res/res/values-w228dp/dimens_watch.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+    <!--  watch's indeterminate progress bar dimens based on the current screen size -->
+    <dimen name="loader_horizontal_min_width_watch">78dp</dimen>
+    <dimen name="loader_horizontal_min_height_watch">16dp</dimen>
+</resources>
diff --git a/core/res/res/values-w240dp/dimens_watch.xml b/core/res/res/values-w240dp/dimens_watch.xml
new file mode 100644
index 0000000..d8478a8
--- /dev/null
+++ b/core/res/res/values-w240dp/dimens_watch.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+    <!--  watch's indeterminate progress bar dimens based on the current screen size -->
+    <dimen name="loader_horizontal_min_width_watch">82dp</dimen>
+    <dimen name="loader_horizontal_min_height_watch">17dp</dimen>
+</resources>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index ef5875e..57a09ea 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -114,4 +114,7 @@
     <!-- By default ActivityOptions#makeScaleUpAnimation is only used between activities. This
      config enables OEMs to support its usage across tasks.-->
     <bool name="config_enableCrossTaskScaleUpAnimation">true</bool>
+
+    <!-- The amount of friction applied to scrolls and flings. -->
+    <item name="config_scrollFriction" format="float" type="dimen">0.023</item>
 </resources>
diff --git a/core/res/res/values-watch/styles_device_defaults.xml b/core/res/res/values-watch/styles_device_defaults.xml
index fb7dbb0..eeb66e7 100644
--- a/core/res/res/values-watch/styles_device_defaults.xml
+++ b/core/res/res/values-watch/styles_device_defaults.xml
@@ -42,5 +42,8 @@
         <item name="indeterminateOnly">false</item>
         <!-- Use Wear Material3 ring shape as default determinate drawable -->
         <item name="progressDrawable">@drawable/progress_ring_watch</item>
+        <item name="indeterminateDrawable">@drawable/loader_horizontal_watch</item>
+        <item name="android:minWidth">@dimen/loader_horizontal_min_width_watch</item>
+        <item name="android:minHeight">@dimen/loader_horizontal_min_height_watch</item>
     </style>
 </resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 60aec53..4738d20 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -48,6 +48,9 @@
         <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
         <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
         <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
+
+        <!-- Material3 default delay before scroll bar fading animation. -->
+        <item name="scrollbarDefaultDelayBeforeFade">1000</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 108d62e..95dd35f 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"单手模式"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"极暗"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"助听装置"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"自动点击"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"已断开连接"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"已连接"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"活跃"</string>
@@ -1821,8 +1820,8 @@
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"放大功能"</string>
     <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"要切换为手机麦克风吗?"</string>
     <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"要切换为助听器麦克风吗?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"可改善音质,或在助听器电池电量不足时使用。此操作只会切换通话期间的麦克风。"</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"您可以使用助听器麦克风进行免提通话。此操作只会切换通话期间的麦克风。"</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"可改善音质,或在助听器电池电量不足时使用。系统仅会在通话期间切换麦克风。"</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"您可以使用助听器麦克风进行免提通话。系统只会在通话期间切换麦克风。"</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"切换"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"设置"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"正在切换为<xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"取消固定前要求绘制解锁图案"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"取消时要求输入密码"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"由您的管理员安装。\n前往设置可查看已授予的权限"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理员更新"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"由您的管理员更新。\n前往设置可查看已授予的权限"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由您的管理员删除"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"确定"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"在省电模式下,系统会启用深色主题,并限制或关闭后台活动、某些视觉效果、特定功能和部分网络连接。"</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"滚动"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暂停"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 已被放入受限存储分区"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"发送了一张图片"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"测试"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"工作资料"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私密空间"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"克隆"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index fc9e6e8..b956da1 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"單手模式"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"超暗"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"助聽器"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"自動點擊"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"已中斷連線"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"已連線"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"運作中"</string>
@@ -1819,10 +1818,10 @@
     <string name="accessibility_gesture_instructional_text" msgid="4133877896011098550">"下次使用此快速鍵時,就會開啟此功能。請用 2 隻手指從螢幕底部向上滑動並快速放開。"</string>
     <string name="accessibility_gesture_3finger_instructional_text" msgid="1124458279366968154">"下次使用此快速鍵時,就會開啟此功能。請用 3 隻手指從螢幕底部向上滑動並快速放開。"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"放大"</string>
-    <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"要切換至手機麥克風嗎?"</string>
-    <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"要切換至助聽器麥克風嗎?"</string>
-    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"可改善音質,也在助聽器電量不足時適用。此操作只會切換通話期間的麥克風。"</string>
-    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"你可使用助聽器麥克風進行免提通話。此操作只會切換通話期間的麥克風。"</string>
+    <string name="hearing_device_switch_phone_mic_notification_title" msgid="6645178038359708836">"要轉用手機麥克風嗎?"</string>
+    <string name="hearing_device_switch_hearing_mic_notification_title" msgid="4612074852145289569">"要轉用助聽器麥克風嗎?"</string>
+    <string name="hearing_device_switch_phone_mic_notification_text" msgid="1332426273666077412">"這樣可改善音質,助聽器電量不足時亦適用。系統只會在通話期間切換麥克風。"</string>
+    <string name="hearing_device_switch_hearing_mic_notification_text" msgid="8288368365767284208">"你可使用助聽器麥克風進行免提通話。系統只會在通話期間切換麥克風。"</string>
     <string name="hearing_device_notification_switch_button" msgid="3619524619430941300">"切換"</string>
     <string name="hearing_device_notification_settings_button" msgid="6673651052880279178">"設定"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"正在切換至<xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"取消固定時必須提供解鎖圖案"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"取消固定時必須輸入密碼"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"已由你的管理員安裝。\n請前往設定查看已授予的權限"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"已由你的管理員更新"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"已由你的管理員更新。\n請前往設定查看已授予的權限"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由你的管理員刪除"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"好"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"「慳電模式」會開啟深色主題背景,並限制或關閉背景活動、部分視覺效果、特定功能和部分網絡連線。"</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"捲動"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暫停"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 已納入受限制的儲存區"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"已傳送圖片"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"測試"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"工作設定檔"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index d2ca941..f9abe0e 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"單手模式"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"超暗"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"助聽器"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"自動點選"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"連線中斷"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"已連線"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"運作中"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"取消固定時必須畫出解鎖圖案"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"取消固定時必須輸入密碼"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"這是管理員安裝的套件。\n你可以前往設定查看授予的權限"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"已由你的管理員更新"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"管理員已更新套件。\n你可以前往設定查看授予的權限"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由你的管理員刪除"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"省電模式會開啟深色主題,並限制或關閉背景活動、某些視覺效果、特定功能和部分網路連線。"</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"捲動"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暫停"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"已將「<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>」移入受限制的值區"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"傳送了一張圖片"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"測試"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"通用"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"工作資料夾"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index e116832..4292eab 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1803,8 +1803,7 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Imodi yesandla esisodwa"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ukufiphaza okwengeziwe"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Amadivayizi okuzwa"</string>
-    <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
-    <skip />
+    <string name="autoclick_feature_name" msgid="8149248738736949630">"Chofoza ngokuzenzekelayo"</string>
     <string name="hearing_device_status_disconnected" msgid="497547752953543832">"Inqamukile"</string>
     <string name="hearing_device_status_connected" msgid="2149385149669918764">"Ixhunyiwe"</string>
     <string name="hearing_device_status_active" msgid="4770378695482566032">"Kuyasebenza"</string>
@@ -1966,7 +1965,7 @@
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Cela iphethini yokuvula ngaphambi kokususa ukuphina"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Cela iphasiwedi ngaphambi kokususa ukuphina"</string>
     <string name="package_installed_device_owner" msgid="8684974629306529138">"Kufakwe ngumphathi wakho.\nIya kumasethingi ukuze ubuke izimvume ezinikeziwe"</string>
-    <string name="package_updated_device_owner" msgid="7560272363805506941">"Kubuyekezwe umlawuli wakho"</string>
+    <string name="package_updated_device_owner" msgid="7770195449213776218">"Kubuyekezwe ngumphathi wakho.\nIya kumasethingi ukuze ubuke izimvume ezinikeziwe"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Kususwe umlawuli wakho"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"KULUNGILE"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Isilondolozi Sebhethri sivula ingqikithi emnyama futhi sibeke umkhawulo noma sivale umsebenzi ongemuva, imiphumela ethile yokubuka, izici ezithile, nokuxhumeka okuthile kwenethiwekhi."</string>
@@ -2274,6 +2273,18 @@
     <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Skrola"</string>
     <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Misa"</string>
     <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Indawo"</string>
+    <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
+    <skip />
+    <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
+    <skip />
     <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"I-<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ifakwe kubhakede LOKUKHAWULELWE"</string>
     <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
     <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"uthumele isithombe"</string>
@@ -2481,6 +2492,8 @@
     <string name="profile_label_work_3" msgid="4834572253956798917">"Umsebenzi 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Hlola"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Okomphakathi"</string>
+    <!-- no translation found for profile_label_supervising (5649312778545745371) -->
+    <skip />
     <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Iphrofayela yomsebenzi"</string>
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Indawo engasese"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Yenza i-Clone"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9773f55..e47adc9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2812,11 +2812,29 @@
     <integer name="config_dreamsBatteryLevelDrainCutoff">5</integer>
     <!-- Limit of how long the device can remain unlocked due to attention checking.  -->
     <integer name="config_attentionMaximumExtension">900000</integer> <!-- 15 minutes.  -->
+
+    <!-- Enables or disables the 'prevent screen timeout' feature, where when a user manually
+         undims the screen, the feature acquires a wakelock to prevent screen timeout.
+         false = disabled, true = enabled. Disabled by default. -->
+    <bool name="config_defaultPreventScreenTimeoutEnabled">false</bool>
+    <!-- Default value (in milliseconds) to prevent the screen timeout after user undims it
+         manually between screen dims, a sign the user is interacting with the device. -->
+    <integer name="config_defaultPreventScreenTimeoutForMillis">300000</integer> <!-- 5 minutes. -->
+    <!-- Default max duration (in milliseconds) of the time between undims to still consider them
+         consecutive. -->
+    <integer name="config_defaultMaxDurationBetweenUndimsMillis">600000</integer> <!-- 10 min.  -->
+    <!-- Default number of user undims required to trigger preventing screen sleep. -->
+    <integer name="config_defaultUndimsRequired">2</integer>
+
     <!-- Whether there is to be a chosen Dock User who is the only user allowed to dream. -->
     <bool name="config_dreamsOnlyEnabledForDockUser">false</bool>
     <!-- Whether dreams are disabled when ambient mode is suppressed. -->
     <bool name="config_dreamsDisabledByAmbientModeSuppressionConfig">false</bool>
 
+    <!-- The default for the setting that controls when to auto-start hub mode.
+          0 means "never" -->
+    <integer name="config_whenToStartHubModeDefault">0</integer>
+
     <!-- The duration in milliseconds of the dream opening animation.  -->
     <integer name="config_dreamOpenAnimationDuration">250</integer>
     <!-- The duration in milliseconds of the dream closing animation.  -->
@@ -4170,6 +4188,11 @@
     <!-- Whether device supports double tap to wake -->
     <bool name="config_supportDoubleTapWake">false</bool>
 
+    <!-- Whether device supports double tap to sleep. This will allow the user to enable/disable
+         double tap gestures in non-action areas in the lock screen and launcher workspace to go to
+         sleep. -->
+    <bool name="config_supportDoubleTapSleep">false</bool>
+
     <!-- The RadioAccessFamilies supported by the device.
          Empty is viewed as "all".  Only used on devices which
          don't support RIL_REQUEST_GET_RADIO_CAPABILITY
@@ -6089,6 +6112,9 @@
     <!-- Whether to default to an expanded list of users on the lock screen user switcher. -->
     <bool name="config_expandLockScreenUserSwitcher">false</bool>
 
+    <!-- Help URI, action disabled by advanced protection [DO NOT TRANSLATE] -->
+    <string name="config_help_url_action_disabled_by_advanced_protection" translatable="false"></string>
+
     <!-- Toasts posted from these packages will be shown to the current user, regardless of the user
          the process belongs to. This is useful for packages that run under a single user but serve
          multiple users, e.g. the system.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a1961ae..c0d2779 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -277,12 +277,29 @@
     <!-- The margin on the end of the top-line content views (accommodates the expander) -->
     <dimen name="notification_heading_margin_end">56dp</dimen>
 
+    <!-- The vertical spacing for the smart reply/smart action container.
+     Note that the button background itself also has an inset of 6dp (making the height of the
+     tappable area 48dp total, 32dp for the visible button plus 6dp top and bottom), so the visible
+     space between the button and the other content is going to be 16dp. -->
+    <dimen name="notification_2025_smart_reply_container_margin">10dp</dimen>
+
     <!-- The total height of the notification action list -->
     <dimen name="notification_action_list_height">60dp</dimen>
 
+    <!-- The total height of the notification action list (2025 redesign version) -->
+    <dimen name="notification_2025_action_list_height">48dp</dimen>
+
     <!-- The margin of the notification action list at the top -->
     <dimen name="notification_action_list_margin_top">0dp</dimen>
 
+    <!-- The margin of the notification action list at the bottom in the 2025 redesign -->
+    <dimen name="notification_2025_action_list_margin_bottom">6dp</dimen>
+
+    <!-- The minimum height of the notification action container, to act as a bottom padding for the
+         notification when there are no actions. This should always be equal to
+         notification_2025_margin - notification_2025_action_list_margin_bottom. -->
+    <dimen name="notification_2025_action_list_min_height">10dp</dimen>
+
     <!-- The overall height of the emphasized notification action -->
     <dimen name="notification_action_emphasized_height">48dp</dimen>
 
@@ -304,6 +321,9 @@
     <!-- The size of icons for visual actions in the notification_material_action_list -->
     <dimen name="notification_actions_icon_size">56dp</dimen>
 
+    <!-- The size of icon actions in notification_material_action_list (2025 redesign version) -->
+    <dimen name="notification_2025_actions_icon_size">48dp</dimen>
+
     <!-- The size of icons for visual actions in the notification_material_action_list -->
     <dimen name="notification_actions_icon_drawable_size">20dp</dimen>
 
@@ -318,9 +338,9 @@
 
     <!-- The spacing between the content and the header text above it, scaling with text size.
          This value is chosen so that, taking into account the text spacing for both the text in the
-         top line and the text in the content, the distance between them is 4dp with the default
+         top line and the text in the content, the distance between them is ~2dp with the default
          screen configuration (and will grow accordingly for larger font sizes) -->
-    <dimen name="notification_2025_content_margin_top">10sp</dimen>
+    <dimen name="notification_2025_content_margin_top">8sp</dimen>
 
     <!-- height of the content margin that is applied at the end of the notification content -->
     <dimen name="notification_content_margin">20dp</dimen>
@@ -390,6 +410,16 @@
     <!-- The absolute size of the notification expand icon. -->
     <dimen name="notification_header_expand_icon_size">56dp</dimen>
 
+    <!-- Margin to allow space for the expand button when showing the right icon in expanded -->
+    <!-- notifications. This is equal to notification_2025_expand_button_pill_width -->
+    <!-- + notification_2025_margin (end padding for expand button) -->
+    <!-- + notification_2025_expand_button_right_icon_spacing (space between pill and icon) -->
+    <dimen name="notification_2025_right_icon_expanded_margin_end">52dp</dimen>
+
+    <!-- The large icon has a smaller vertical margin than most other notification content, to -->
+    <!-- allow it to grow up to 48dp. -->
+    <dimen name="notification_2025_right_icon_vertical_margin">12dp</dimen>
+
     <!-- the height of the expand button pill -->
     <dimen name="notification_expand_button_pill_height">24dp</dimen>
 
@@ -411,15 +441,26 @@
     <!-- the padding of the expand icon in the notification header -->
     <dimen name="notification_2025_expand_button_horizontal_icon_padding">6dp</dimen>
 
-    <!-- a smaller padding for the end of the expand button, for use when showing the number -->
+    <!-- smaller padding for the end of the expand icon, for use when showing the number -->
     <dimen name="notification_2025_expand_button_reduced_end_padding">4dp</dimen>
 
+    <!-- the space needed between the expander pill and the large icon when visible -->
+    <dimen name="notification_2025_expand_button_right_icon_spacing">8dp</dimen>
+
     <!-- the size of the notification close button -->
     <dimen name="notification_close_button_size">16dp</dimen>
 
+    <dimen name="notification_close_button_padding">2dp</dimen>
+
     <!-- Margin for all notification content -->
     <dimen name="notification_2025_margin">16dp</dimen>
 
+    <!-- A smaller version of the margin to be used when we need more space for the content -->
+    <dimen name="notification_2025_reduced_margin">12dp</dimen>
+
+    <!-- The difference between the usual margin and the reduced margin -->
+    <dimen name="notification_2025_additional_margin">4dp</dimen>
+
     <!-- Vertical margin for the headerless notification content, when content has 1 line -->
     <!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) -->
     <dimen name="notification_headerless_margin_oneline">16dp</dimen>
@@ -438,7 +479,7 @@
     <dimen name="notification_collapsed_height_with_summarization">156dp</dimen>
 
     <!-- Max height of a collapsed (headerless) notification with one or two lines -->
-    <!-- 16 * 2 (margins) + 48 (min content height) = 72 (notification) -->
+    <!-- 14 * 2 (reduced margins) + 48 (max collapsed content height) = 72 (notification) -->
     <dimen name="notification_2025_min_height">72dp</dimen>
 
     <!-- Height of a headerless notification with one line -->
@@ -482,6 +523,9 @@
     <!-- The spacing between messages in Notification.MessagingStyle -->
     <dimen name="notification_messaging_spacing">6dp</dimen>
 
+    <!-- The spacing between messages in Notification.MessagingStyle (2025 redesign version) -->
+    <dimen name="notification_2025_messaging_spacing">14dp</dimen>
+
     <!-- The spacing between messages in Notification.MessagingStyle -->
     <dimen name="notification_messaging_spacing_conversation_group">24dp</dimen>
 
@@ -729,6 +773,9 @@
     <!-- The accessibility autoclick panel divider height -->
     <dimen name="accessibility_autoclick_type_panel_divider_height">24dp</dimen>
 
+    <!-- The accessibility autoclick scroll panel button width and height -->
+    <dimen name="accessibility_autoclick_scroll_panel_button_size">36dp</dimen>
+
     <!-- Margin around the various security views -->
     <dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
 
@@ -893,6 +940,8 @@
     <dimen name="notification_right_icon_size">48dp</dimen>
     <!-- The margin between the right icon and the content. -->
     <dimen name="notification_right_icon_content_margin">12dp</dimen>
+    <!-- The margin between the right icon and the content. (2025 redesign version) -->
+    <dimen name="notification_2025_right_icon_content_margin">16dp</dimen>
     <!-- The top and bottom margin of the right icon in the normal notification states -->
     <dimen name="notification_right_icon_headerless_margin">20dp</dimen>
     <!-- The top margin of the right icon in the "big" notification states -->
@@ -1211,4 +1260,7 @@
 
     <!-- The maximum width for a context menu icon -->
     <dimen name="list_menu_item_icon_max_width">24dp</dimen>
+
+    <!-- Default height of desktop view header for freeform tasks on launch. -->
+    <dimen name="desktop_view_default_header_height">40dp</dimen>
 </resources>
diff --git a/core/res/res/values/dimens_watch.xml b/core/res/res/values/dimens_watch.xml
index 7462b73..a8ed666 100644
--- a/core/res/res/values/dimens_watch.xml
+++ b/core/res/res/values/dimens_watch.xml
@@ -61,4 +61,8 @@
     <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>
+
+    <!--  watch's indeterminate progress bar dimens -->
+    <dimen name="loader_horizontal_min_width_watch">76dp</dimen>
+    <dimen name="loader_horizontal_min_height_watch">14dp</dimen>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b3cc883..d94d659 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6226,6 +6226,18 @@
     <string name="accessibility_autoclick_pause">Pause</string>
     <!-- Label for autoclick position button [CHAR LIMIT=NONE] -->
     <string name="accessibility_autoclick_position">Position</string>
+    <!-- Label for autoclick scroll up button [CHAR LIMIT=NONE] -->
+    <string name="accessibility_autoclick_scroll_up">Scroll Up</string>
+    <!-- Label for autoclick scroll down button [CHAR LIMIT=NONE] -->
+    <string name="accessibility_autoclick_scroll_down">Scroll Down</string>
+    <!-- Label for autoclick scroll left button [CHAR LIMIT=NONE] -->
+    <string name="accessibility_autoclick_scroll_left">Scroll Left</string>
+    <!-- Label for autoclick scroll right button [CHAR LIMIT=NONE] -->
+    <string name="accessibility_autoclick_scroll_right">Scroll Right</string>
+    <!-- Label for autoclick scroll exit button [CHAR LIMIT=NONE] -->
+    <string name="accessibility_autoclick_scroll_exit">Exit Scroll Mode</string>
+    <!-- Label for autoclick scroll panel title [CHAR LIMIT=NONE] -->
+    <string name="accessibility_autoclick_scroll_panel_title">Scroll Panel</string>
 
     <!-- Text to tell the user that a package has been forced by themselves in the RESTRICTED bucket. [CHAR LIMIT=NONE] -->
     <string name="as_app_forced_to_restricted_bucket">
@@ -6698,6 +6710,8 @@
     <string name="profile_label_test">Test</string>
     <!-- Communal profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
     <string name="profile_label_communal">Communal</string>
+    <!-- Supervising profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+    <string name="profile_label_supervising">Supervising</string>
 
     <!-- Accessibility label for managed profile user type [CHAR LIMIT=30] -->
     <string name="accessibility_label_managed_profile">Work profile</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index ee1edda..1c3529f 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1761,6 +1761,19 @@
         <item name="android:tint">@color/materialColorPrimary</item>
     </style>
 
+    <style name="AccessibilityAutoclickScrollPanelButtonLayoutStyle">
+        <item name="android:gravity">center</item>
+        <item name="android:layout_width">@dimen/accessibility_autoclick_scroll_panel_button_size   </item>
+        <item name="android:layout_height">@dimen/accessibility_autoclick_scroll_panel_button_size</item>
+    </style>
+
+    <style name="AccessibilityAutoclickScrollPanelImageButtonStyle">
+        <item name="android:gravity">center</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:scaleType">center</item>
+    </style>
+
     <!--
         TODO(b/309578419): Make activities go edge-to-edge properly and then remove this.
     -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2b7261b..a37ca28 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1129,6 +1129,7 @@
   <java-symbol type="string" name="profile_label_work_3" />
   <java-symbol type="string" name="profile_label_test" />
   <java-symbol type="string" name="profile_label_communal" />
+  <java-symbol type="string" name="profile_label_supervising" />
   <java-symbol type="string" name="accessibility_label_managed_profile" />
   <java-symbol type="string" name="accessibility_label_private_profile" />
   <java-symbol type="string" name="accessibility_label_clone_profile" />
@@ -2071,6 +2072,7 @@
   <java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" />
   <java-symbol type="bool" name="config_keepDreamingWhenUnplugging" />
   <java-symbol type="bool" name="config_glanceableHubEnabled" />
+  <java-symbol type="integer" name="config_whenToStartHubModeDefault" />
   <java-symbol type="integer" name="config_keyguardDrawnTimeout" />
   <java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" />
   <java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
@@ -3141,6 +3143,7 @@
   <java-symbol type="color" name="chooser_row_divider" />
   <java-symbol type="layout" name="chooser_row_direct_share" />
   <java-symbol type="bool" name="config_supportDoubleTapWake" />
+  <java-symbol type="bool" name="config_supportDoubleTapSleep" />
   <java-symbol type="drawable" name="ic_perm_device_info" />
   <java-symbol type="string" name="config_radio_access_family" />
   <java-symbol type="string" name="notification_inbox_ellipsis" />
@@ -3241,6 +3244,7 @@
   <java-symbol type="id" name="expand_button_pill_colorized_layer" />
   <java-symbol type="id" name="expand_button_number" />
   <java-symbol type="id" name="expand_button_icon" />
+  <java-symbol type="id" name="close_button_pill_colorized_layer" />
   <java-symbol type="id" name="alternate_expand_target" />
   <java-symbol type="id" name="notification_header" />
   <java-symbol type="id" name="notification_top_line" />
@@ -3267,6 +3271,10 @@
   <java-symbol type="dimen" name="notification_2025_content_margin_start" />
   <java-symbol type="dimen" name="notification_2025_expand_button_horizontal_icon_padding" />
   <java-symbol type="dimen" name="notification_2025_expand_button_reduced_end_padding" />
+  <java-symbol type="dimen" name="notification_2025_expand_button_right_icon_spacing" />
+  <java-symbol type="dimen" name="notification_2025_right_icon_expanded_margin_end" />
+  <java-symbol type="dimen" name="notification_2025_action_list_margin_bottom" />
+  <java-symbol type="dimen" name="notification_2025_smart_reply_container_margin" />
   <java-symbol type="dimen" name="notification_progress_margin_horizontal" />
   <java-symbol type="dimen" name="notification_header_background_height" />
   <java-symbol type="dimen" name="notification_header_touchable_height" />
@@ -3493,6 +3501,7 @@
   <java-symbol type="dimen" name="input_extract_action_button_height" />
 
   <java-symbol type="dimen" name="notification_action_list_height" />
+  <java-symbol type="dimen" name="notification_2025_action_list_height" />
   <java-symbol type="dimen" name="notification_action_emphasized_height" />
 
   <!-- TV Remote Service package -->
@@ -3957,6 +3966,7 @@
   <java-symbol type="dimen" name="notification_big_picture_max_width"/>
   <java-symbol type="dimen" name="notification_right_icon_size"/>
   <java-symbol type="dimen" name="notification_right_icon_content_margin"/>
+  <java-symbol type="dimen" name="notification_2025_right_icon_content_margin"/>
   <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/>
   <java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
   <java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
@@ -4442,6 +4452,11 @@
   <!-- For Attention Service -->
   <java-symbol type="integer" name="config_attentionMaximumExtension" />
 
+  <java-symbol type="bool" name="config_defaultPreventScreenTimeoutEnabled" />
+  <java-symbol type="integer" name="config_defaultPreventScreenTimeoutForMillis" />
+  <java-symbol type="integer" name="config_defaultMaxDurationBetweenUndimsMillis" />
+  <java-symbol type="integer" name="config_defaultUndimsRequired" />
+
   <java-symbol type="string" name="config_incidentReportApproverPackage" />
   <java-symbol type="array" name="config_restrictedImagesServices" />
 
@@ -5666,6 +5681,32 @@
   <java-symbol type="drawable" name="accessibility_autoclick_resume" />
   <java-symbol type="drawable" name="ic_accessibility_autoclick" />
 
+  <!-- Accessibility autoclick scroll panel related -->
+  <java-symbol type="layout" name="accessibility_autoclick_scroll_panel" />
+  <java-symbol type="dimen" name="accessibility_autoclick_scroll_panel_button_size" />
+  <java-symbol type="drawable" name="accessibility_autoclick_scroll_up" />
+  <java-symbol type="drawable" name="accessibility_autoclick_scroll_down" />
+  <java-symbol type="drawable" name="accessibility_autoclick_scroll_left" />
+  <java-symbol type="drawable" name="accessibility_autoclick_scroll_right" />
+  <java-symbol type="drawable" name="accessibility_autoclick_scroll_exit" />
+  <java-symbol type="string" name="accessibility_autoclick_scroll_up" />
+  <java-symbol type="string" name="accessibility_autoclick_scroll_down" />
+  <java-symbol type="string" name="accessibility_autoclick_scroll_left" />
+  <java-symbol type="string" name="accessibility_autoclick_scroll_right" />
+  <java-symbol type="string" name="accessibility_autoclick_scroll_exit" />
+  <java-symbol type="string" name="accessibility_autoclick_scroll_panel_title" />
+  <java-symbol type="id" name="accessibility_autoclick_scroll_panel" />
+  <java-symbol type="id" name="scroll_up_layout" />
+  <java-symbol type="id" name="scroll_down_layout" />
+  <java-symbol type="id" name="scroll_left_layout" />
+  <java-symbol type="id" name="scroll_right_layout" />
+  <java-symbol type="id" name="scroll_exit_layout" />
+  <java-symbol type="id" name="scroll_up" />
+  <java-symbol type="id" name="scroll_down" />
+  <java-symbol type="id" name="scroll_left" />
+  <java-symbol type="id" name="scroll_right" />
+  <java-symbol type="id" name="scroll_exit" />
+
   <!-- For HapticFeedbackConstants configurability defined at HapticFeedbackCustomization -->
   <java-symbol type="string" name="config_hapticFeedbackCustomizationFile" />
   <java-symbol type="xml" name="haptic_feedback_customization" />
@@ -5924,6 +5965,8 @@
   <!-- Device CMF Theming Settings -->
   <java-symbol type="array" name="theming_defaults" />
 
+  <java-symbol type="string" name="config_help_url_action_disabled_by_advanced_protection" />
+
   <!-- Advanced Protection Service USB feature -->
   <java-symbol type="string" name="usb_apm_usb_plugged_in_when_locked_notification_title" />
   <java-symbol type="string" name="usb_apm_usb_plugged_in_when_locked_notification_text" />
@@ -5939,4 +5982,6 @@
   <!-- Enable OEMs to support scale up anim across tasks.-->
   <java-symbol type="bool" name="config_enableCrossTaskScaleUpAnimation" />
 
+  <!-- Default height of desktop view header for freeform tasks on launch. -->
+  <java-symbol type="dimen" name="desktop_view_default_header_height" />
 </resources>
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index fa07447..5af837f 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -18,7 +18,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 
 import static org.junit.Assert.*;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
diff --git a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
index e8b295b..0287e6c 100644
--- a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
@@ -22,7 +22,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.hardware.usb.UsbDevice;
 import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -118,6 +118,6 @@
 
         verify(mBrailleDisplayCallback).onConnectionFailed(
                 BrailleDisplayController.BrailleDisplayCallback.FLAG_ERROR_CANNOT_ACCESS);
-        verifyZeroInteractions(mAccessibilityServiceConnection);
+        verifyNoMoreInteractions(mAccessibilityServiceConnection);
     }
 }
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
index 33a46d0..746ba96 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
@@ -20,6 +20,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.os.Handler;
+import android.os.Looper;
 import android.util.PollingCheck;
 import android.view.View;
 
@@ -36,6 +38,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 
 
 @MediumTest
@@ -486,6 +489,57 @@
         });
     }
 
+    @Test
+    public void testCancelOnPendingEndListener() throws Throwable {
+        testPendingEndListener(AnimatorSet::cancel);
+    }
+
+    @Test
+    public void testEndOnPendingEndListener() throws Throwable {
+        testPendingEndListener(animatorSet -> {
+            // This verifies that isRunning() and isStarted() are true at last frame.
+            // Then the end() should invoke the end callback immediately.
+            if (animatorSet.isRunning() && animatorSet.isStarted()) {
+                animatorSet.end();
+            }
+        });
+    }
+
+    private void testPendingEndListener(Consumer<AnimatorSet> finishOnLastFrame) throws Throwable {
+        final CountDownLatch endLatch = new CountDownLatch(1);
+        final Handler handler = new Handler(Looper.getMainLooper());
+        final boolean[] endCalledImmediately = new boolean[2];
+        final AnimatorSet set = new AnimatorSet();
+        final ValueAnimatorTests.MyListener asListener = new ValueAnimatorTests.MyListener();
+        final ValueAnimatorTests.MyListener vaListener = new ValueAnimatorTests.MyListener();
+        final ValueAnimator va = new ValueAnimator();
+        va.setFloatValues(0f, 1f);
+        va.setDuration(30);
+        va.addUpdateListener(animation -> {
+            if (animation.getAnimatedFraction() == 1f) {
+                handler.post(() -> {
+                    finishOnLastFrame.accept(set);
+                    endCalledImmediately[0] = vaListener.endCalled;
+                    endCalledImmediately[1] = asListener.endCalled;
+                    endLatch.countDown();
+                });
+            }
+        });
+        set.addListener(asListener);
+        va.addListener(vaListener);
+        set.play(va);
+
+        ValueAnimator.setPostNotifyEndListenerEnabled(true);
+        try {
+            handler.post(set::start);
+            assertTrue(endLatch.await(1, TimeUnit.SECONDS));
+            assertTrue(endCalledImmediately[0]);
+            assertTrue(endCalledImmediately[1]);
+        } finally {
+            ValueAnimator.setPostNotifyEndListenerEnabled(false);
+        }
+    }
+
     private void waitForOnUiThread(PollingCheck.PollingCheckCondition condition) {
         final boolean[] value = new boolean[1];
         PollingCheck.waitFor(() -> {
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index 0469846..89664ff 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -44,6 +44,7 @@
 import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 @RunWith(AndroidJUnit4.class)
 @MediumTest
@@ -922,6 +923,52 @@
     }
 
     @Test
+    public void testCancelOnPendingEndListener() throws Throwable {
+        testPendingEndListener(ValueAnimator::cancel);
+    }
+
+    @Test
+    public void testEndOnPendingEndListener() throws Throwable {
+        testPendingEndListener(animator -> {
+            // This verifies that isRunning() and isStarted() are true at last frame.
+            // Then the end() should invoke the end callback immediately.
+            if (animator.isRunning() && animator.isStarted()) {
+                animator.end();
+            }
+        });
+    }
+
+    private void testPendingEndListener(Consumer<ValueAnimator> finishOnLastFrame)
+            throws Throwable {
+        final boolean[] endCalledImmediately = new boolean[1];
+        final CountDownLatch endLatch = new CountDownLatch(1);
+        final Handler handler = new Handler(Looper.getMainLooper());
+        final MyListener listener = new MyListener();
+        final ValueAnimator va = new ValueAnimator();
+        va.setFloatValues(0f, 1f);
+        va.setDuration(30);
+        va.addUpdateListener(animation -> {
+            if (animation.getAnimatedFraction() == 1f) {
+                handler.post(() -> {
+                    finishOnLastFrame.accept(va);
+                    endCalledImmediately[0] = listener.endCalled;
+                    endLatch.countDown();
+                });
+            }
+        });
+        va.addListener(listener);
+
+        ValueAnimator.setPostNotifyEndListenerEnabled(true);
+        try {
+            handler.post(va::start);
+            assertThat(endLatch.await(1, TimeUnit.SECONDS)).isTrue();
+            assertThat(endCalledImmediately[0]).isTrue();
+        } finally {
+            ValueAnimator.setPostNotifyEndListenerEnabled(false);
+        }
+    }
+
+    @Test
     public void testZeroDuration() throws Throwable {
         // Run two animators with zero duration, with one running forward and the other one
         // backward. Check that the animations start and finish with the correct end fractions.
@@ -1182,6 +1229,7 @@
             assertEquals(A1_START_VALUE, a1.getAnimatedValue());
         });
     }
+
     class MyUpdateListener implements ValueAnimator.AnimatorUpdateListener {
         boolean wasRunning = false;
         long firstRunningFrameTime = -1;
@@ -1207,7 +1255,7 @@
         }
     }
 
-    class MyListener implements Animator.AnimatorListener {
+    static class MyListener implements Animator.AnimatorListener {
         boolean startCalled = false;
         boolean cancelCalled = false;
         boolean endCalled = false;
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 62d89f6..146b386 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -16,19 +16,37 @@
 
 package android.app;
 
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_INFO;
+import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.os.storage.VolumeInfo.STATE_MOUNTED;
 import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;
 
 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.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+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.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -44,6 +62,7 @@
 
 import junit.framework.TestCase;
 
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mockito;
 import org.xmlpull.v1.XmlPullParser;
 
@@ -102,14 +121,14 @@
         sVolumes.add(sPrivateUnmountedVol);
     }
 
-    private static final class MockedApplicationPackageManager extends ApplicationPackageManager {
+    public static class MockedApplicationPackageManager extends ApplicationPackageManager {
         private boolean mForceAllowOnExternal = false;
         private boolean mAllow3rdPartyOnInternal = true;
         private HashMap<ApplicationInfo, Resources> mResourcesMap;
 
         public MockedApplicationPackageManager() {
             super(null, null);
-            mResourcesMap = new HashMap<ApplicationInfo, Resources>();
+            mResourcesMap = new HashMap<>();
         }
 
         public void setForceAllowOnExternal(boolean forceAllowOnExternal) {
@@ -153,7 +172,7 @@
     }
 
     private StorageManager getMockedStorageManager() {
-        StorageManager storageManager = Mockito.mock(StorageManager.class);
+        StorageManager storageManager = mock(StorageManager.class);
         Mockito.when(storageManager.getVolumes()).thenReturn(sVolumes);
         Mockito.when(storageManager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL))
                 .thenReturn(sInternalVol);
@@ -190,7 +209,7 @@
         sysAppInfo.flags = ApplicationInfo.FLAG_SYSTEM;
 
         StorageManager storageManager = getMockedStorageManager();
-        IPackageManager pm = Mockito.mock(IPackageManager.class);
+        IPackageManager pm = mock(IPackageManager.class);
 
         MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
 
@@ -220,7 +239,7 @@
         ApplicationInfo appInfo = new ApplicationInfo();
         StorageManager storageManager = getMockedStorageManager();
 
-        IPackageManager pm = Mockito.mock(IPackageManager.class);
+        IPackageManager pm = mock(IPackageManager.class);
         Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false);
 
         MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
@@ -249,7 +268,7 @@
         ApplicationInfo appInfo = new ApplicationInfo();
         StorageManager storageManager = getMockedStorageManager();
 
-        IPackageManager pm = Mockito.mock(IPackageManager.class);
+        IPackageManager pm = mock(IPackageManager.class);
 
         MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
         appPkgMgr.setForceAllowOnExternal(true);
@@ -291,15 +310,15 @@
 
     public void testExtractPackageItemInfoAttributes_noMetaData() {
         final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
-        final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
+        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
         assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, null,
                 new int[]{})).isNull();
     }
 
     public void testExtractPackageItemInfoAttributes_noParser() {
         final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
-        final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
-        final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
+        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+        final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
         when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
         assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, null,
                 new int[]{})).isNull();
@@ -307,8 +326,8 @@
 
     public void testExtractPackageItemInfoAttributes_noMetaDataXml() {
         final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
-        final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
-        final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
+        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+        final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
         when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
         when(packageItemInfo.loadXmlMetaData(any(), any())).thenReturn(null);
         assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, null,
@@ -318,9 +337,9 @@
     public void testExtractPackageItemInfoAttributes_nonMatchingRootTag() throws Exception {
         final String rootTag = "rootTag";
         final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
-        final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
-        final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
-        final XmlResourceParser parser = Mockito.mock(XmlResourceParser.class);
+        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+        final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+        final XmlResourceParser parser = mock(XmlResourceParser.class);
 
         when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
         packageItemInfo.metaData = new Bundle();
@@ -334,11 +353,11 @@
     public void testExtractPackageItemInfoAttributes_successfulExtraction() throws Exception {
         final String rootTag = "rootTag";
         final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
-        final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
-        final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
-        final XmlResourceParser parser = Mockito.mock(XmlResourceParser.class);
-        final Resources resources = Mockito.mock(Resources.class);
-        final TypedArray attributes = Mockito.mock(TypedArray.class);
+        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+        final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+        final XmlResourceParser parser = mock(XmlResourceParser.class);
+        final Resources resources = mock(Resources.class);
+        final TypedArray attributes = mock(TypedArray.class);
 
         when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
         packageItemInfo.metaData = new Bundle();
@@ -351,4 +370,123 @@
         assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, rootTag,
                 new int[]{})).isEqualTo(attributes);
     }
+
+    public void testGetLaunchIntentForPackage_categoryInfoActivity_returnsIt() throws Exception {
+        String pkg = "com.some.package";
+        int userId = 42;
+        ResolveInfo categoryInfoResolveInfo = new ResolveInfo();
+        categoryInfoResolveInfo.activityInfo = new ActivityInfo();
+        categoryInfoResolveInfo.activityInfo.packageName = pkg;
+        categoryInfoResolveInfo.activityInfo.name = "activity";
+        Intent baseIntent = new Intent(ACTION_MAIN).setPackage(pkg);
+
+        final MockedApplicationPackageManager pm = spy(new MockedApplicationPackageManager());
+        doReturn(userId).when(pm).getUserId();
+        doReturn(List.of(categoryInfoResolveInfo))
+                .when(pm).queryIntentActivitiesAsUser(
+                        eqIntent(new Intent(baseIntent).addCategory(CATEGORY_INFO)),
+                        any(ResolveInfoFlags.class),
+                        anyInt());
+        doReturn(
+                List.of())
+                .when(pm).queryIntentActivitiesAsUser(
+                        eqIntent(new Intent(baseIntent).addCategory(CATEGORY_LAUNCHER)),
+                        any(ResolveInfoFlags.class),
+                        anyInt());
+
+        Intent intent = pm.getLaunchIntentForPackage(pkg, true);
+
+        assertThat(intent).isNotNull();
+        assertThat(intent.getComponent()).isEqualTo(new ComponentName(pkg, "activity"));
+        assertThat(intent.getCategories()).containsExactly(CATEGORY_INFO);
+        assertThat(intent.getFlags()).isEqualTo(FLAG_ACTIVITY_NEW_TASK);
+        verify(pm).queryIntentActivitiesAsUser(
+                eqIntent(new Intent(ACTION_MAIN).addCategory(CATEGORY_INFO).setPackage(pkg)),
+                eqResolveInfoFlags(MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE),
+                eq(userId));
+    }
+
+    public void testGetLaunchIntentForPackage_categoryLauncherActivity_returnsIt() {
+        String pkg = "com.some.package";
+        int userId = 42;
+        ResolveInfo categoryLauncherResolveInfo1 = new ResolveInfo();
+        categoryLauncherResolveInfo1.activityInfo = new ActivityInfo();
+        categoryLauncherResolveInfo1.activityInfo.packageName = pkg;
+        categoryLauncherResolveInfo1.activityInfo.name = "activity1";
+        ResolveInfo categoryLauncherResolveInfo2 = new ResolveInfo();
+        categoryLauncherResolveInfo2.activityInfo = new ActivityInfo();
+        categoryLauncherResolveInfo2.activityInfo.packageName = pkg;
+        categoryLauncherResolveInfo2.activityInfo.name = "activity2";
+        Intent baseIntent = new Intent(ACTION_MAIN).setPackage(pkg);
+
+        final MockedApplicationPackageManager pm = spy(new MockedApplicationPackageManager());
+        doReturn(userId).when(pm).getUserId();
+        doReturn(List.of())
+                .when(pm).queryIntentActivitiesAsUser(
+                        eqIntent(new Intent(baseIntent).addCategory(CATEGORY_INFO)),
+                        any(ResolveInfoFlags.class),
+                        anyInt());
+        doReturn(
+                List.of(categoryLauncherResolveInfo1, categoryLauncherResolveInfo2))
+                .when(pm).queryIntentActivitiesAsUser(
+                        eqIntent(new Intent(baseIntent).addCategory(CATEGORY_LAUNCHER)),
+                        any(ResolveInfoFlags.class),
+                        anyInt());
+
+        Intent intent = pm.getLaunchIntentForPackage(pkg, true);
+
+        assertThat(intent).isNotNull();
+        assertThat(intent.getComponent()).isEqualTo(new ComponentName(pkg, "activity1"));
+        assertThat(intent.getCategories()).containsExactly(CATEGORY_LAUNCHER);
+        assertThat(intent.getFlags()).isEqualTo(FLAG_ACTIVITY_NEW_TASK);
+    }
+
+    public void testGetLaunchIntentForPackage_noSuitableActivity_returnsNull() throws Exception {
+        String pkg = "com.some.package";
+        int userId = 42;
+
+        final MockedApplicationPackageManager pm = spy(new MockedApplicationPackageManager());
+        doReturn(userId).when(pm).getUserId();
+        doReturn(List.of())
+                .when(pm).queryIntentActivitiesAsUser(
+                        any(),
+                        any(ResolveInfoFlags.class),
+                        anyInt());
+
+        Intent intent = pm.getLaunchIntentForPackage(pkg, true);
+
+        assertThat(intent).isNull();
+    }
+
+    /** Equality check for intents -- ignoring extras */
+    private static Intent eqIntent(Intent wanted) {
+        return argThat(
+                new ArgumentMatcher<>() {
+                    @Override
+                    public boolean matches(Intent argument) {
+                        return wanted.filterEquals(argument)
+                                && wanted.getFlags() == argument.getFlags();
+                    }
+
+                    @Override
+                    public String toString() {
+                        return wanted.toString();
+                    }
+                });
+    }
+
+    private static ResolveInfoFlags eqResolveInfoFlags(long flagsWanted) {
+        return argThat(
+                new ArgumentMatcher<>() {
+                    @Override
+                    public boolean matches(ResolveInfoFlags argument) {
+                        return argument.getValue() == flagsWanted;
+                    }
+
+                    @Override
+                    public String toString() {
+                        return String.valueOf(flagsWanted);
+                    }
+                });
+    }
 }
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index f89e441..157c74a 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -371,16 +371,6 @@
 
     @Test
     @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
-    public void testHasPromotableStyle_bigPicture() {
-        Notification n = new Notification.Builder(mContext, "test")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .setStyle(new Notification.BigPictureStyle())
-                .build();
-        assertThat(n.hasPromotableStyle()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
     public void testHasPromotableStyle_bigText() {
         Notification n = new Notification.Builder(mContext, "test")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -391,6 +381,16 @@
 
     @Test
     @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableStyle_no_bigPictureStyle() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigPictureStyle())
+                .build();
+        assertThat(n.hasPromotableStyle()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
     public void testHasPromotableStyle_no_messagingStyle() {
         Notification.MessagingStyle style = new Notification.MessagingStyle("self name")
                 .setGroupConversation(true)
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index f75a72d..21ab8fc 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -41,7 +41,6 @@
 import com.android.internal.os.ApplicationSharedMemory;
 
 import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index ee4761b..70b4150 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -233,7 +233,7 @@
     /**
      * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
      */
-    private class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
+    public class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
         static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
         static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
         static final String ATTRIBUTES_NAME = "test";
@@ -332,7 +332,7 @@
         }
     }
 
-    static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
+    public static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
 
         public void writeAsXml(TestServiceType item, TypedXmlSerializer out) throws IOException {
             out.attribute(null, "type", item.type);
@@ -347,7 +347,7 @@
         }
     }
 
-    static class TestServiceType implements Parcelable {
+    public static class TestServiceType implements Parcelable {
         final String type;
         final String value;
 
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
new file mode 100644
index 0000000..8349659
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.content.pm.Flags.FLAG_OPTIMIZE_PARSING_IN_REGISTERED_SERVICES_CACHE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.RegisteredServicesCacheTest.TestServiceType;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+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.util.AttributeSet;
+import android.util.SparseArray;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.os.BackgroundThread;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link android.content.pm.RegisteredServicesCache}
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(FLAG_OPTIMIZE_PARSING_IN_REGISTERED_SERVICES_CACHE)
+public class RegisteredServicesCacheUnitTest {
+    private static final String TAG = "RegisteredServicesCacheUnitTest";
+    private static final int U0 = 0;
+    private static final int U1 = 1;
+    private static final int UID1 = 1;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private final ResolveInfo mResolveInfo1 = new ResolveInfo();
+    private final ResolveInfo mResolveInfo2 = new ResolveInfo();
+    private final TestServiceType mTestServiceType1 = new TestServiceType("t1", "value1");
+    private final TestServiceType mTestServiceType2 = new TestServiceType("t2", "value2");
+    @Mock
+    RegisteredServicesCache.Injector<TestServiceType> mMockInjector;
+    @Mock
+    Context mMockContext;
+    Handler mMockBackgroundHandler;
+    @Mock
+    PackageManager mMockPackageManager;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mMockInjector.getContext()).thenReturn(mMockContext);
+        mMockBackgroundHandler = spy(BackgroundThread.getHandler());
+        when(mMockInjector.getBackgroundHandler()).thenReturn(mMockBackgroundHandler);
+        doReturn(mock(Intent.class)).when(mMockContext).registerReceiverAsUser(any(), any(), any(),
+                any(), any());
+        doReturn(mock(Intent.class)).when(mMockContext).registerReceiver(any(), any(), any(),
+                any());
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+
+        addServiceInfoIntoResolveInfo(mResolveInfo1, "r1.package.name" /* packageName */,
+                "r1.service.name" /* serviceName */);
+        addServiceInfoIntoResolveInfo(mResolveInfo2, "r2.package.name" /* packageName */,
+                "r2.service.name" /* serviceName */);
+    }
+
+    @Test
+    public void testSaveServiceInfoIntoCaches() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+        PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo2.serviceInfo.packageName),
+                anyInt(), eq(U1))).thenReturn(packageInfo2);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        int u1uid = UserHandle.getUid(U1, UID1);
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+                mTestServiceType2, u1uid, mResolveInfo2.serviceInfo.getComponentName(),
+                2000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U1, mResolveInfo2, serviceInfo2);
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+        testServicesCache.getAllServices(U1);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+
+        reset(testServicesCache);
+
+        testServicesCache.invalidateCache(U0);
+        testServicesCache.invalidateCache(U1);
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+        testServicesCache.getAllServices(U1);
+        verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+    }
+
+    @Test
+    public void testClearServiceInfoCachesAfterRemoveUserId() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+        reset(testServicesCache);
+
+        testServicesCache.onUserRemoved(U0);
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+    }
+
+    @Test
+    public void testGetServiceInfoCachesForMultiUser() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U1))).thenReturn(packageInfo1);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+        reset(testServicesCache);
+
+        testServicesCache.clearServicesForQuerying();
+        int u1uid = UserHandle.getUid(U1, UID1);
+        assertThat(u1uid).isNotEqualTo(UID1);
+
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+                mTestServiceType1, u1uid, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U1, mResolveInfo1, serviceInfo2);
+
+        testServicesCache.getAllServices(U1);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+        reset(testServicesCache);
+
+        testServicesCache.invalidateCache(U0);
+        testServicesCache.invalidateCache(U1);
+
+        // There is a bug to return the same info from the cache for different users. Make sure it
+        // will return the different info from the cache for different users.
+        Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> serviceInfos;
+        serviceInfos = testServicesCache.getAllServices(U0);
+        // Make sure the service info is retrieved from the cache for U0.
+        verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+        for (RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo : serviceInfos) {
+            assertThat(serviceInfo.componentInfo.applicationInfo.uid).isEqualTo(UID1);
+        }
+
+        serviceInfos = testServicesCache.getAllServices(U1);
+        // Make sure the service info is retrieved from the cache for U1.
+        verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+        for (RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo : serviceInfos) {
+            assertThat(serviceInfo.componentInfo.applicationInfo.uid).isEqualTo(u1uid);
+        }
+    }
+
+    @Test
+    public void testUpdateServiceInfoIntoCachesWhenPackageInfoNotFound() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+        reset(testServicesCache);
+        reset(mMockPackageManager);
+
+        doThrow(new SecurityException("")).when(mMockPackageManager).getPackageInfoAsUser(
+                eq(mResolveInfo1.serviceInfo.packageName), anyInt(), eq(U0));
+
+        testServicesCache.invalidateCache(U0);
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), anyLong());
+    }
+
+    @Test
+    public void testUpdateServiceInfoIntoCachesWhenTheApplicationHasBeenUpdated() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+        reset(testServicesCache);
+        reset(mMockPackageManager);
+
+        PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo2);
+
+        testServicesCache.invalidateCache(U0);
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(2000L));
+    }
+
+    @Test
+    public void testClearServiceInfoCachesAfterTimeout() throws Exception {
+        PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+        when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+                anyInt(), eq(U0))).thenReturn(packageInfo1);
+
+        TestRegisteredServicesCache testServicesCache = spy(
+                new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+        final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+                mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+                1000L /* lastUpdateTime */);
+        testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+        // Immediately invoke run on the Runnable posted to the handler
+        doAnswer(invocation -> {
+            Message message = invocation.getArgument(0);
+            message.getCallback().run();
+            return true;
+        }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong());
+
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+        verify(mMockBackgroundHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
+
+        reset(testServicesCache);
+
+        testServicesCache.invalidateCache(U0);
+        testServicesCache.getAllServices(U0);
+        verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+    }
+
+    private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
+            TestServiceType type, int uid, ComponentName componentName, long lastUpdateTime) {
+        final ComponentInfo info = new ComponentInfo();
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.uid = uid;
+        return new RegisteredServicesCache.ServiceInfo<>(type, info, componentName, lastUpdateTime);
+    }
+
+    private void addServiceInfoIntoResolveInfo(ResolveInfo resolveInfo, String packageName,
+            String serviceName) {
+        final ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.packageName = packageName;
+        serviceInfo.name = serviceName;
+        resolveInfo.serviceInfo = serviceInfo;
+    }
+
+    private PackageInfo createPackageInfo(long lastUpdateTime) {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.lastUpdateTime = lastUpdateTime;
+        return packageInfo;
+    }
+
+    /**
+     * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
+     */
+    public class TestRegisteredServicesCache extends RegisteredServicesCache<TestServiceType> {
+        static final String SERVICE_INTERFACE = "RegisteredServicesCacheUnitTest";
+        static final String SERVICE_META_DATA = "RegisteredServicesCacheUnitTest";
+        static final String ATTRIBUTES_NAME = "test";
+        private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices =
+                new SparseArray<>();
+
+        public TestRegisteredServicesCache(Injector<TestServiceType> injector,
+                XmlSerializerAndParser<TestServiceType> serializerAndParser) {
+            super(injector, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
+                    serializerAndParser);
+        }
+
+        @Override
+        public TestServiceType parseServiceAttributes(Resources res, String packageName,
+                AttributeSet attrs) {
+            return null;
+        }
+
+        @Override
+        protected List<ResolveInfo> queryIntentServices(int userId) {
+            Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId,
+                    new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>());
+            return new ArrayList<>(map.keySet());
+        }
+
+        void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
+                ServiceInfo<TestServiceType> serviceInfo) {
+            Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
+            if (map == null) {
+                map = new HashMap<>();
+                mServices.put(userId, map);
+            }
+            map.put(resolveInfo, serviceInfo);
+        }
+
+        void clearServicesForQuerying() {
+            mServices.clear();
+        }
+
+        @Override
+        protected ServiceInfo<TestServiceType> parseServiceInfo(ResolveInfo resolveInfo,
+                long lastUpdateTime) throws XmlPullParserException, IOException {
+            int size = mServices.size();
+            for (int i = 0; i < size; i++) {
+                Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
+                ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo);
+                if (serviceInfo != null) {
+                    return serviceInfo;
+                }
+            }
+            throw new IllegalArgumentException("Unexpected service " + resolveInfo);
+        }
+
+        @Override
+        public void onUserRemoved(int userId) {
+            super.onUserRemoved(userId);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java b/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
index 8b513cb..524e355 100644
--- a/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
@@ -136,9 +136,11 @@
         SystemFeaturesCache cache = new SystemFeaturesCache(features);
 
         SystemFeaturesCache.clearInstance();
+        assertThat(SystemFeaturesCache.hasInstance()).isFalse();
         assertThrows(IllegalStateException.class, () -> SystemFeaturesCache.getInstance());
 
         SystemFeaturesCache.setInstance(cache);
+        assertThat(SystemFeaturesCache.hasInstance()).isTrue();
         assertThat(SystemFeaturesCache.getInstance()).isEqualTo(cache);
 
         assertThrows(
@@ -149,6 +151,7 @@
     @Test
     public void testSingletonAutomaticallySetWithFeatureEnabled() {
         assumeTrue(android.content.pm.Flags.cacheSdkSystemFeatures());
+        assertThat(SystemFeaturesCache.hasInstance()).isTrue();
         assertThat(SystemFeaturesCache.getInstance()).isNotNull();
     }
 
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index de5f0ff..34650be 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -264,7 +264,7 @@
                         /* isEventFilterExplicit */ true);
         callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
         waitForHandler();
-        Mockito.verifyZeroInteractions(mDisplayListener);
+        Mockito.verifyNoMoreInteractions(mDisplayListener);
 
         mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
                 ALL_DISPLAY_EVENTS
@@ -272,7 +272,7 @@
                         /* isEventFilterExplicit */ true);
         callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
         waitForHandler();
-        Mockito.verifyZeroInteractions(mDisplayListener);
+        Mockito.verifyNoMoreInteractions(mDisplayListener);
 
         mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
                 ALL_DISPLAY_EVENTS
@@ -280,7 +280,7 @@
                         /* isEventFilterExplicit */ true);
         callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
         waitForHandler();
-        Mockito.verifyZeroInteractions(mDisplayListener);
+        Mockito.verifyNoMoreInteractions(mDisplayListener);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
index 46f22ce..5047861 100644
--- a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
+++ b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
@@ -20,8 +20,8 @@
 import static junit.framework.Assert.assertSame;
 import static junit.framework.Assert.fail;
 
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 
 import android.Manifest.permission;
 import android.content.Context;
diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java
index 006828f..570f236 100644
--- a/core/tests/coretests/src/android/os/AidlTest.java
+++ b/core/tests/coretests/src/android/os/AidlTest.java
@@ -22,7 +22,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
@@ -35,7 +35,7 @@
 
 import java.util.List;
 
-@IgnoreUnderRavenwood(blockedBy = Parcel.class)
+@DisabledOnRavenwood(blockedBy = Parcel.class)
 public class AidlTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
index 5ef1460..3125fdd 100644
--- a/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
+++ b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
@@ -25,7 +25,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.ArraySet;
 import android.util.Log;
@@ -55,7 +55,7 @@
  * Tests functionality of {@link android.os.IBinder.DeathRecipient} callbacks.
  */
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+@DisabledOnRavenwood(blockedBy = ActivityManager.class)
 public class BinderDeathRecipientTest {
     private static final String TAG = BinderDeathRecipientTest.class.getSimpleName();
     private static final String TEST_PACKAGE_NAME_1 =
diff --git a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
index 523fe1a..0ebd293 100644
--- a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
+++ b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
@@ -27,7 +27,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
@@ -55,7 +55,7 @@
  * Tests functionality of {@link android.os.IBinder.FrozenStateChangeCallback}.
  */
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+@DisabledOnRavenwood(blockedBy = ActivityManager.class)
 public class BinderFrozenStateChangeNotificationTest {
     private static final String TAG = BinderFrozenStateChangeNotificationTest.class.getSimpleName();
 
diff --git a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
index 4dfe2e2..31353e7 100644
--- a/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyCountingTest.java
@@ -24,7 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
@@ -74,7 +74,7 @@
  */
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+@DisabledOnRavenwood(blockedBy = ActivityManager.class)
 public class BinderProxyCountingTest {
     private static final String TAG = BinderProxyCountingTest.class.getSimpleName();
 
diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java
index 5fff0b8..656d3bf 100644
--- a/core/tests/coretests/src/android/os/BinderProxyTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyTest.java
@@ -27,7 +27,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
@@ -43,7 +43,7 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+@DisabledOnRavenwood(blockedBy = ActivityManager.class)
 public class BinderProxyTest {
     private static class CountingListener implements Binder.ProxyTransactListener {
         int mStartedCount;
diff --git a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
index 9a679d8..3438028 100644
--- a/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
+++ b/core/tests/coretests/src/android/os/BinderThreadPriorityTest.java
@@ -24,7 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
@@ -46,7 +46,7 @@
  * Test whether Binder calls inherit thread priorities correctly.
  */
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+@DisabledOnRavenwood(blockedBy = ActivityManager.class)
 public class BinderThreadPriorityTest {
     private static final String TAG = "BinderThreadPriorityTest";
 
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
index 98e96c2..9b61cd2 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -24,7 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.ravenwood.RavenwoodRule;
 
@@ -45,7 +45,7 @@
 @LargeTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+@DisabledOnRavenwood(blockedBy = ActivityManager.class)
 public class BinderWorkSourceTest {
     private static Context sContext;
     private static final int UID = 100;
diff --git a/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java b/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
index 2117e74..1e6fc15 100644
--- a/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
+++ b/core/tests/coretests/src/android/os/CancellationSignalBeamerTest.java
@@ -22,7 +22,7 @@
 
 import android.content.Context;
 import android.os.CancellationSignalBeamer.Receiver;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.PollingCheck;
 import android.util.PollingCheck.PollingCheckCondition;
@@ -44,7 +44,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-@IgnoreUnderRavenwood(blockedBy = CancellationSignalBeamer.class)
+@DisabledOnRavenwood(blockedBy = CancellationSignalBeamer.class)
 public class CancellationSignalBeamerTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/CancellationSignalTest.java b/core/tests/coretests/src/android/os/CancellationSignalTest.java
index 8e11df5..3b05cf7 100644
--- a/core/tests/coretests/src/android/os/CancellationSignalTest.java
+++ b/core/tests/coretests/src/android/os/CancellationSignalTest.java
@@ -19,11 +19,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.ravenwood.RavenwoodRule;
-
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -32,11 +29,6 @@
 
 @RunWith(AndroidJUnit4.class)
 public class CancellationSignalTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     @Test
     public void testSimple() throws Exception {
         final CancellationSignal signal = new CancellationSignal();
diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java
index 1b49624..99ce175 100644
--- a/core/tests/coretests/src/android/os/EnvironmentTest.java
+++ b/core/tests/coretests/src/android/os/EnvironmentTest.java
@@ -28,7 +28,7 @@
 
 import android.content.Context;
 import android.os.storage.StorageManager;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
@@ -46,7 +46,7 @@
 import java.util.function.BiFunction;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = Environment.class)
+@DisabledOnRavenwood(blockedBy = Environment.class)
 public class EnvironmentTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/FileBridgeTest.java b/core/tests/coretests/src/android/os/FileBridgeTest.java
index 726670b..fa2a5f4 100644
--- a/core/tests/coretests/src/android/os/FileBridgeTest.java
+++ b/core/tests/coretests/src/android/os/FileBridgeTest.java
@@ -24,7 +24,7 @@
 import static org.junit.Assert.fail;
 
 import android.os.FileBridge.FileBridgeOutputStream;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.test.MoreAsserts;
 
@@ -46,7 +46,7 @@
 import java.util.Random;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class)
+@DisabledOnRavenwood(blockedBy = ParcelFileDescriptor.class)
 public class FileBridgeTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/FileObserverTest.java b/core/tests/coretests/src/android/os/FileObserverTest.java
index 3cd8045..6412023 100644
--- a/core/tests/coretests/src/android/os/FileObserverTest.java
+++ b/core/tests/coretests/src/android/os/FileObserverTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
@@ -42,7 +42,7 @@
 import java.util.Map;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = FileObserver.class)
+@DisabledOnRavenwood(blockedBy = FileObserver.class)
 public class FileObserverTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index 791ec5d..2b49b38 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -26,7 +26,7 @@
 import android.app.PropertyInvalidatedCache;
 import android.app.PropertyInvalidatedCache.Args;
 import android.multiuser.Flags;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
index a695424..38ba639 100644
--- a/core/tests/coretests/src/android/os/MemoryFileTest.java
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -40,7 +40,7 @@
 import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = MemoryFile.class)
+@DisabledOnRavenwood(blockedBy = MemoryFile.class)
 public class MemoryFileTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/MessengerTest.java b/core/tests/coretests/src/android/os/MessengerTest.java
index eb6263f..2014aa7 100644
--- a/core/tests/coretests/src/android/os/MessengerTest.java
+++ b/core/tests/coretests/src/android/os/MessengerTest.java
@@ -21,7 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
@@ -35,7 +35,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+@DisabledOnRavenwood(blockedBy = ActivityManager.class)
 public class MessengerTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
index 790ac4a..b345315 100644
--- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -23,7 +23,7 @@
 import static perfetto.protos.ChromeLatencyInfoOuterClass.ChromeLatencyInfo.LatencyComponentType.COMPONENT_INPUT_EVENT_LATENCY_BEGIN_RWH;
 import static perfetto.protos.ChromeLatencyInfoOuterClass.ChromeLatencyInfo.LatencyComponentType.COMPONENT_INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -68,7 +68,7 @@
  * while tracing on the emulator and then run traceview to view the trace.
  */
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = PerfettoTrace.class)
+@DisabledOnRavenwood(blockedBy = PerfettoTrace.class)
 public class PerfettoTraceTest {
     @Rule
     public final CheckFlagsRule mCheckFlagsRule =
diff --git a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
index 436720e..c256680 100644
--- a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
@@ -22,7 +22,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.PerformanceCollector.PerformanceResultsWriter;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -40,7 +40,7 @@
 import java.util.Random;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = PerformanceCollector.class)
+@DisabledOnRavenwood(blockedBy = PerformanceCollector.class)
 public class PerformanceCollectorTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
index 4b49fde..b7c25f2 100644
--- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
@@ -24,7 +24,7 @@
 import static org.junit.Assume.assumeNotNull;
 
 import android.os.PerformanceHintManager.Session;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
@@ -38,7 +38,7 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = PerformanceHintManager.class)
+@DisabledOnRavenwood(blockedBy = PerformanceHintManager.class)
 public class PerformanceHintManagerTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java
index ea39db7..3d50cfe 100644
--- a/core/tests/coretests/src/android/os/ProcessTest.java
+++ b/core/tests/coretests/src/android/os/ProcessTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import org.junit.Rule;
@@ -29,7 +29,7 @@
 
 import java.util.Arrays;
 
-@IgnoreUnderRavenwood(blockedBy = Process.class)
+@DisabledOnRavenwood(blockedBy = Process.class)
 public class ProcessTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
index e22c862..a6160bb 100644
--- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -24,7 +24,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.Context;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.system.Os;
 
@@ -44,7 +44,7 @@
 import java.util.Arrays;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = RedactingFileDescriptor.class)
+@DisabledOnRavenwood(blockedBy = RedactingFileDescriptor.class)
 public class RedactingFileDescriptorTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/RemoteCallbackTest.java b/core/tests/coretests/src/android/os/RemoteCallbackTest.java
index ddcc380..cbbaa75 100644
--- a/core/tests/coretests/src/android/os/RemoteCallbackTest.java
+++ b/core/tests/coretests/src/android/os/RemoteCallbackTest.java
@@ -19,11 +19,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.ravenwood.RavenwoodRule;
-
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -32,11 +29,6 @@
 
 @RunWith(AndroidJUnit4.class)
 public class RemoteCallbackTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     @Test
     public void testSimple() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
diff --git a/core/tests/coretests/src/android/os/ResultReceiverTest.java b/core/tests/coretests/src/android/os/ResultReceiverTest.java
index be67825..21d4078 100644
--- a/core/tests/coretests/src/android/os/ResultReceiverTest.java
+++ b/core/tests/coretests/src/android/os/ResultReceiverTest.java
@@ -19,11 +19,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.ravenwood.RavenwoodRule;
-
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -32,11 +29,6 @@
 
 @RunWith(AndroidJUnit4.class)
 public class ResultReceiverTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     @Test
     public void testSimple() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
diff --git a/core/tests/coretests/src/android/os/TraceTest.java b/core/tests/coretests/src/android/os/TraceTest.java
index 5462f32..4117dd6 100644
--- a/core/tests/coretests/src/android/os/TraceTest.java
+++ b/core/tests/coretests/src/android/os/TraceTest.java
@@ -16,7 +16,7 @@
 
 package android.os;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
 
@@ -105,7 +105,7 @@
 
     @Test
     @SmallTest
-    @IgnoreUnderRavenwood(blockedBy = Debug.class)
+    @DisabledOnRavenwood(blockedBy = Debug.class)
     public void testNativeTracingFromJava()
     {
         long start = System.currentTimeMillis();
@@ -126,7 +126,7 @@
     
     // This should not run in the automated suite.
     @Suppress
-    @IgnoreUnderRavenwood(blockedBy = Debug.class)
+    @DisabledOnRavenwood(blockedBy = Debug.class)
     public void disableTestNativeTracingFromC()
     {
         long start = System.currentTimeMillis();
@@ -142,7 +142,7 @@
     @Test
     @LargeTest
     @Suppress  // Failing.
-    @IgnoreUnderRavenwood(blockedBy = Debug.class)
+    @DisabledOnRavenwood(blockedBy = Debug.class)
     public void testMethodTracing()
     {
         long start = System.currentTimeMillis();
diff --git a/core/tests/coretests/src/android/os/VintfObjectTest.java b/core/tests/coretests/src/android/os/VintfObjectTest.java
index f81b31d..1b7bb41 100644
--- a/core/tests/coretests/src/android/os/VintfObjectTest.java
+++ b/core/tests/coretests/src/android/os/VintfObjectTest.java
@@ -20,7 +20,7 @@
 
 import static java.util.stream.Collectors.toList;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Pair;
 
@@ -37,7 +37,7 @@
 import javax.xml.parsers.DocumentBuilderFactory;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = VintfObject.class)
+@DisabledOnRavenwood(blockedBy = VintfObject.class)
 public class VintfObjectTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/os/WorkSourceParcelTest.java b/core/tests/coretests/src/android/os/WorkSourceParcelTest.java
index 5f49186..0ebf128 100644
--- a/core/tests/coretests/src/android/os/WorkSourceParcelTest.java
+++ b/core/tests/coretests/src/android/os/WorkSourceParcelTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -30,7 +30,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-@IgnoreUnderRavenwood(reason = "JNI")
+@DisabledOnRavenwood(reason = "JNI")
 public class WorkSourceParcelTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
@@ -49,7 +49,7 @@
             String[] names, int parcelEndMarker);
 
     static {
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             System.loadLibrary("worksourceparceltest_jni");
         }
     }
diff --git a/core/tests/coretests/src/android/provider/FontsContractTest.java b/core/tests/coretests/src/android/provider/FontsContractTest.java
index 21a2205..f520995 100644
--- a/core/tests/coretests/src/android/provider/FontsContractTest.java
+++ b/core/tests/coretests/src/android/provider/FontsContractTest.java
@@ -21,8 +21,8 @@
 import static android.provider.FontsContract.Columns.RESULT_CODE_MALFORMED_QUERY;
 import static android.provider.FontsContract.Columns.RESULT_CODE_OK;
 
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 9e78af5..11ec9f8 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -16,6 +16,9 @@
 
 package android.text;
 
+import static android.text.Layout.HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_FACTOR;
+import static android.text.Layout.HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_MIN_DP;
+
 import static com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -1073,6 +1076,68 @@
         }
     }
 
+    @Test
+    @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+    public void highContrastTextEnabled_testRoundedRectSize_belowMinimum_usesMinimumValue() {
+        mTextPaint.setColor(Color.BLACK);
+        mTextPaint.setTextSize(8); // Value chosen so that N * RADIUS_FACTOR < RADIUS_MIN_DP
+        Layout layout = new StaticLayout("Test text", mTextPaint, mWidth,
+                mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
+
+        MockCanvas c = new MockCanvas(/* width= */ 256, /* height= */ 256);
+        c.setHighContrastTextEnabled(true);
+        layout.draw(
+                c,
+                /* highlightPaths= */ null,
+                /* highlightPaints= */ null,
+                /* selectionPath= */ null,
+                /* selectionPaint= */ null,
+                /* cursorOffsetVertical= */ 0
+        );
+
+        final float expectedRoundedRectSize =
+                mTextPaint.density * HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_MIN_DP;
+        List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+        for (int i = 0; i < drawCommands.size(); i++) {
+            MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+            if (drawCommand.rect != null) {
+                expect.that(drawCommand.rX).isEqualTo(expectedRoundedRectSize);
+                expect.that(drawCommand.rY).isEqualTo(expectedRoundedRectSize);
+            }
+        }
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+    public void highContrastTextEnabled_testRoundedRectSize_aboveMinimum_usesScaledValue() {
+        mTextPaint.setColor(Color.BLACK);
+        mTextPaint.setTextSize(50); // Value chosen so that N * RADIUS_FACTOR > RADIUS_MIN_DP
+        Layout layout = new StaticLayout("Test text", mTextPaint, mWidth,
+                mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
+
+        MockCanvas c = new MockCanvas(/* width= */ 256, /* height= */ 256);
+        c.setHighContrastTextEnabled(true);
+        layout.draw(
+                c,
+                /* highlightPaths= */ null,
+                /* highlightPaints= */ null,
+                /* selectionPath= */ null,
+                /* selectionPaint= */ null,
+                /* cursorOffsetVertical= */ 0
+        );
+
+        final float expectedRoundedRectSize =
+                mTextPaint.getTextSize() * HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_FACTOR;
+        List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+        for (int i = 0; i < drawCommands.size(); i++) {
+            MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+            if (drawCommand.rect != null) {
+                expect.that(drawCommand.rX).isEqualTo(expectedRoundedRectSize);
+                expect.that(drawCommand.rY).isEqualTo(expectedRoundedRectSize);
+            }
+        }
+    }
+
     private int removeAlpha(int color) {
         return Color.rgb(
                 Color.red(color),
@@ -1087,6 +1152,8 @@
             public final String text;
             public final float x;
             public final float y;
+            public final float rX;
+            public final float rY;
             public final Path path;
             public final RectF rect;
             public final Paint paint;
@@ -1098,6 +1165,8 @@
                 this.paint = new Paint(paint);
                 path = null;
                 rect = null;
+                this.rX = 0;
+                this.rY = 0;
             }
 
             DrawCommand(Path path, Paint paint) {
@@ -1107,15 +1176,19 @@
                 x = 0;
                 text = null;
                 rect = null;
+                this.rX = 0;
+                this.rY = 0;
             }
 
-            DrawCommand(RectF rect, Paint paint) {
+            DrawCommand(RectF rect, Paint paint, float rX, float rY) {
                 this.rect = new RectF(rect);
                 this.paint = new Paint(paint);
                 path = null;
                 y = 0;
                 x = 0;
                 text = null;
+                this.rX = rX;
+                this.rY = rY;
             }
 
             @Override
@@ -1189,12 +1262,12 @@
 
         @Override
         public void drawRect(RectF rect, Paint p) {
-            mDrawCommands.add(new DrawCommand(rect, p));
+            mDrawCommands.add(new DrawCommand(rect, p, 0, 0));
         }
 
         @Override
         public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
-            mDrawCommands.add(new DrawCommand(rect, paint));
+            mDrawCommands.add(new DrawCommand(rect, paint, rx, ry));
         }
 
         List<DrawCommand> getDrawCommands() {
diff --git a/core/tests/coretests/src/android/util/ArrayMapTest.java b/core/tests/coretests/src/android/util/ArrayMapTest.java
index 711ff94..d71a603 100644
--- a/core/tests/coretests/src/android/util/ArrayMapTest.java
+++ b/core/tests/coretests/src/android/util/ArrayMapTest.java
@@ -14,14 +14,18 @@
 
 package android.util;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.fail;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.annotations.Presubmit;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,6 +36,7 @@
  * Unit tests for ArrayMap that don't belong in CTS.
  */
 @LargeTest
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ArrayMapTest {
     private static final String TAG = "ArrayMapTest";
@@ -51,7 +56,8 @@
      * @throws Exception
      */
     @Test
-    @IgnoreUnderRavenwood(reason = "Long test runtime")
+    @Ignore("Failing; b/399137661")
+    @DisabledOnRavenwood(reason = "Long test runtime")
     public void testConcurrentModificationException() throws Exception {
         final int TEST_LEN_MS = 5000;
         System.out.println("Starting ArrayMap concurrency test");
@@ -118,4 +124,49 @@
             }
         }
     }
+
+    @Test
+    public void testToString() {
+        map.put("1", "One");
+        map.put("2", "Two");
+        map.put("3", "Five");
+        map.put("3", "Three");
+
+        assertThat(map.toString()).isEqualTo("{1=One, 2=Two, 3=Three}");
+        assertThat(map.keySet().toString()).isEqualTo("[1, 2, 3]");
+        assertThat(map.values().toString()).isEqualTo("[One, Two, Three]");
+    }
+
+    @Test
+    public void testToStringRecursive() {
+        ArrayMap<Object, Object> weird = new ArrayMap<>();
+        weird.put("1", weird);
+
+        assertThat(weird.toString()).isEqualTo("{1=(this Map)}");
+        assertThat(weird.keySet().toString()).isEqualTo("[1]");
+        assertThat(weird.values().toString()).isEqualTo("[{1=(this Map)}]");
+    }
+
+    @Test
+    public void testToStringRecursiveKeySet() {
+        ArrayMap<Object, Object> weird = new ArrayMap<>();
+        weird.put("1", weird.keySet());
+        weird.put(weird.keySet(), "2");
+
+        assertThat(weird.toString()).isEqualTo("{1=[1, (this KeySet)], [1, (this KeySet)]=2}");
+        assertThat(weird.keySet().toString()).isEqualTo("[1, (this KeySet)]");
+        assertThat(weird.values().toString()).isEqualTo("[[1, (this KeySet)], 2]");
+    }
+
+    @Test
+    public void testToStringRecursiveValues() {
+        ArrayMap<Object, Object> weird = new ArrayMap<>();
+        weird.put("1", weird.values());
+        weird.put(weird.values(), "2");
+
+        assertThat(weird.toString()).isEqualTo(
+                "{1=[(this ValuesCollection), 2], [(this ValuesCollection), 2]=2}");
+        assertThat(weird.keySet().toString()).isEqualTo("[1, [(this ValuesCollection), 2]]");
+        assertThat(weird.values().toString()).isEqualTo("[(this ValuesCollection), 2]");
+    }
 }
diff --git a/core/tests/coretests/src/android/util/CharsetUtilsTest.java b/core/tests/coretests/src/android/util/CharsetUtilsTest.java
index 33936e9..cf6e1eb 100644
--- a/core/tests/coretests/src/android/util/CharsetUtilsTest.java
+++ b/core/tests/coretests/src/android/util/CharsetUtilsTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -33,7 +33,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = CharsetUtils.class)
+@DisabledOnRavenwood(blockedBy = CharsetUtils.class)
 public class CharsetUtilsTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
@@ -43,7 +43,7 @@
 
     @Before
     public void setUp() {
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             dest = (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, 8);
             destPtr = VMRuntime.getRuntime().addressOf(dest);
         }
diff --git a/core/tests/coretests/src/android/util/CloseGuardTest.java b/core/tests/coretests/src/android/util/CloseGuardTest.java
index 15c57b1..c91ef1c 100644
--- a/core/tests/coretests/src/android/util/CloseGuardTest.java
+++ b/core/tests/coretests/src/android/util/CloseGuardTest.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import libcore.dalvik.system.CloseGuardSupport;
@@ -26,7 +26,7 @@
 import org.junit.rules.TestRule;
 
 /** Unit tests for {@link android.util.CloseGuard} */
-@IgnoreUnderRavenwood(blockedBy = CloseGuard.class)
+@DisabledOnRavenwood(blockedBy = CloseGuard.class)
 public class CloseGuardTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
@@ -35,7 +35,7 @@
     public final TestRule rule;
 
     public CloseGuardTest() {
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             rule = CloseGuardSupport.getRule();
         } else {
             rule = null;
diff --git a/core/tests/coretests/src/android/util/HashedStringCacheTest.java b/core/tests/coretests/src/android/util/HashedStringCacheTest.java
index 08c85ac..c26855b 100644
--- a/core/tests/coretests/src/android/util/HashedStringCacheTest.java
+++ b/core/tests/coretests/src/android/util/HashedStringCacheTest.java
@@ -26,7 +26,7 @@
 import android.content.SharedPreferences;
 import android.os.Environment;
 import android.os.storage.StorageManager;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
@@ -40,7 +40,7 @@
 /**
  * Unit tests for {@link HashedStringCache}.
  */
-@IgnoreUnderRavenwood(blockedBy = HashedStringCache.class)
+@DisabledOnRavenwood(blockedBy = HashedStringCache.class)
 public class HashedStringCacheTest {
     private static final String TAG = "HashedStringCacheTest";
     private Context mContext;
@@ -51,7 +51,7 @@
 
     @Before
     public void setup() {
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             mContext = InstrumentationRegistry.getContext();
             clearSharedPreferences();
         } else {
diff --git a/core/tests/coretests/src/android/util/LogNullabilityTest.java b/core/tests/coretests/src/android/util/LogNullabilityTest.java
index 5aa2626..795444d 100644
--- a/core/tests/coretests/src/android/util/LogNullabilityTest.java
+++ b/core/tests/coretests/src/android/util/LogNullabilityTest.java
@@ -37,7 +37,7 @@
         Log.i(null, "");
         Log.w(null, "");
         Log.e(null, "");
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             Log.wtf(null, "");
             Log.wtfStack(null, "");
         }
@@ -53,7 +53,7 @@
         Log.i(null, "", new Throwable());
         Log.w(null, "", new Throwable());
         Log.e(null, "", new Throwable());
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             Log.wtf(null, "", new Throwable());
         }
         Log.printlns(Log.LOG_ID_MAIN, Log.INFO, null, "", new Throwable());
@@ -90,7 +90,7 @@
         } catch (NullPointerException expected) {
         }
 
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             Log.wtf("", (String) null);
             Log.wtfStack("", (String) null);
         }
@@ -111,7 +111,7 @@
         Log.i("", null, new Throwable());
         Log.w("", null, new Throwable());
         Log.e("", null, new Throwable());
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             Log.wtf("", null, new Throwable());
         }
         Log.printlns(Log.LOG_ID_MAIN, Log.INFO, "", null, new Throwable());
@@ -124,7 +124,7 @@
         Log.i("", "", null);
         Log.w("", "", null);
         Log.e("", "", null);
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             Log.wtf("", "", null);
         }
 
@@ -136,7 +136,7 @@
         // Implicit assertions of not crashing.
 
         // WTF has its own (String, Throwable) overload with different behavior.
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             try {
                 Log.wtf("", (Throwable) null);
                 fail();
@@ -152,7 +152,7 @@
         Log.i("", null, null);
         Log.w("", null, null);
         Log.e("", null, null);
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             Log.wtf("", null, null);
         }
         Log.printlns(Log.LOG_ID_MAIN, Log.INFO, "", null, null);
diff --git a/core/tests/coretests/src/android/util/NtpTrustedTimeTest.java b/core/tests/coretests/src/android/util/NtpTrustedTimeTest.java
index ce1eabc..32fb3a2 100644
--- a/core/tests/coretests/src/android/util/NtpTrustedTimeTest.java
+++ b/core/tests/coretests/src/android/util/NtpTrustedTimeTest.java
@@ -31,7 +31,7 @@
 import static java.util.Arrays.asList;
 
 import android.net.Network;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -56,7 +56,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = NtpTrustedTime.class)
+@DisabledOnRavenwood(blockedBy = NtpTrustedTime.class)
 public class NtpTrustedTimeTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
index 48e76f7..56d6594 100644
--- a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
+++ b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
@@ -26,7 +26,7 @@
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 
 import android.content.Context;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -52,7 +52,7 @@
 
 /** Unit test for {@link android.util.apk.SourceStampVerifier} */
 @RunWith(JUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = SourceStampVerifier.class)
+@DisabledOnRavenwood(blockedBy = SourceStampVerifier.class)
 public class SourceStampVerifierTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index af87af0..79d8bb1 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -1208,8 +1208,9 @@
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        verify(mTestHost, times(1)).updateAnimatingTypes(eq(types));
-        verify(mTestHost, times(1)).updateAnimatingTypes(eq(0) /* animatingTypes */);
+        verify(mTestHost, times(1)).updateAnimatingTypes(eq(types), any() /* statsToken */);
+        verify(mTestHost, times(1)).updateAnimatingTypes(eq(0) /* animatingTypes */,
+                any() /* statsToken */);
     }
 
     private void waitUntilNextFrame() throws Exception {
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 8ac9292..50cd4c0 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -29,7 +29,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.os.CancellationSignal;
@@ -230,7 +230,7 @@
         InsetsController secondController = mock(InsetsController.class);
         mPendingInsetsController.replayAndAttach(secondController);
         verify(mReplayedController).show(eq(systemBars()));
-        verifyZeroInteractions(secondController);
+        verifyNoMoreInteractions(secondController);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 39f3d33..5774109 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -1063,7 +1063,7 @@
         ViewRootImpl viewRootImpl = mView.getViewRootImpl();
         sInstrumentation.runOnMainSync(() -> {
             mView.invalidate();
-            viewRootImpl.updateAnimatingTypes(Type.systemBars());
+            viewRootImpl.updateAnimatingTypes(Type.systemBars(), null /* statsToken */);
             mView.invalidate();
         });
         sInstrumentation.waitForIdleSync();
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index e5ad561..341947c 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -29,8 +29,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyObject;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -79,7 +79,7 @@
     @Before
     public void setUp() {
         mAccessibilityNodeRefresher = mock(AccessibilityCache.AccessibilityNodeRefresher.class);
-        when(mAccessibilityNodeRefresher.refreshNode(anyObject(), anyBoolean())).thenReturn(true);
+        when(mAccessibilityNodeRefresher.refreshNode(any(), anyBoolean())).thenReturn(true);
         mAccessibilityCache = new AccessibilityCache(mAccessibilityNodeRefresher);
     }
 
@@ -854,7 +854,7 @@
                 try {
                     assertEventTypeClearsNode(eventType, false);
                     verify(mAccessibilityNodeRefresher, never())
-                            .refreshNode(anyObject(), anyBoolean());
+                            .refreshNode(any(), anyBoolean());
                 } catch (Throwable e) {
                     throw new AssertionError(
                             "Failed for eventType: " + AccessibilityEvent.eventTypeToString(
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index eb482f2e..f811d8e 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.MockitoAnnotations.initMocks;
 
 import android.os.Bundle;
@@ -74,7 +74,7 @@
                 MOCK_CONNECTION_ID, windowId, accessibilityNodeId, true, 0, null);
         assertEquals("Node got lost along the way", nodeFromConnection, node);
 
-        verifyZeroInteractions(mMockCache);
+        verifyNoMoreInteractions(mMockCache);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 82e3427..551357c 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -34,8 +34,8 @@
 import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index 5f89f9c..8bbe81d 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -27,7 +27,7 @@
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
@@ -130,7 +130,7 @@
         mTestableLooper.processAllMessages();
 
         assertThat(session.mContentProtectionEventProcessor).isNull();
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
     }
 
     @Test
@@ -151,7 +151,7 @@
         mTestableLooper.processAllMessages();
 
         assertThat(session.mContentProtectionEventProcessor).isNull();
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
     }
 
     @Test
@@ -172,7 +172,7 @@
         mTestableLooper.processAllMessages();
 
         assertThat(session.mContentProtectionEventProcessor).isNull();
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
     }
 
     @Test
@@ -197,7 +197,7 @@
         session.sendEvent(EVENT);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNull();
     }
 
@@ -227,7 +227,7 @@
         session.sendEvent(EVENT);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNotNull();
         assertThat(session.mEvents).containsExactly(EVENT);
     }
@@ -255,7 +255,7 @@
         session.sendEvent(EVENT);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNull();
     }
 
@@ -272,8 +272,8 @@
         session.flush(REASON);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentCaptureDirectManager);
         assertThat(session.mEvents).containsExactly(EVENT);
     }
 
@@ -289,8 +289,8 @@
         session.flush(REASON);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentCaptureDirectManager);
         assertThat(session.mEvents).containsExactly(EVENT);
     }
 
@@ -307,7 +307,7 @@
         session.flush(REASON);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isEmpty();
         assertEventFlushedContentCapture(options);
     }
@@ -325,7 +325,7 @@
         session.flush(REASON);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isEmpty();
         assertEventFlushedContentCapture(options);
     }
@@ -339,7 +339,7 @@
         mTestableLooper.processAllMessages();
 
         verify(mMockSystemServerInterface).finishSession(anyInt());
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mDirectServiceInterface).isNull();
         assertThat(session.mContentProtectionEventProcessor).isNull();
     }
@@ -352,8 +352,8 @@
         session.resetSession(/* newState= */ 0);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockSystemServerInterface);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockSystemServerInterface);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mDirectServiceInterface).isNull();
         assertThat(session.mContentProtectionEventProcessor).isNull();
     }
@@ -370,8 +370,8 @@
         notifyContentCaptureEvents(session);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNull();
     }
 
@@ -388,8 +388,8 @@
         notifyContentCaptureEvents(session);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNull();
     }
 
@@ -407,8 +407,8 @@
         notifyContentCaptureEvents(session);
         mTestableLooper.processAllMessages();
 
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
+        verifyNoMoreInteractions(mMockContentCaptureDirectManager);
+        verifyNoMoreInteractions(mMockContentProtectionEventProcessor);
         assertThat(session.mEvents).isNull();
     }
 
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
index ba0dbf4..e75452c 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -443,7 +442,7 @@
         mTestLooper.dispatchAll();
         verify(mMockEventBuffer, never()).clear();
         verify(mMockEventBuffer, never()).toArray();
-        verifyZeroInteractions(mMockContentCaptureManager);
+        verifyNoMoreInteractions(mMockContentCaptureManager);
     }
 
     private void assertLoginDetected() throws Exception {
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 9110898..2dcb692 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -1080,6 +1080,9 @@
         rv.setOnClickFillInIntent(R.id.view, fillInIntent);
         assertNotEquals(0, fillInIntent.getExtendedFlags()
                 & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
+
+        RemoteViews.RemoteResponse rr = RemoteViews.RemoteResponse.fromFillInIntent(null);
+        assertNotNull(rr);
     }
 
     private static LayoutInflater.Factory2 createLayoutInflaterFactory(String viewTypeToReplace,
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 3eb7d9a..34ccc8b 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -51,7 +51,7 @@
 
 import static org.hamcrest.Matchers.anyOf;
 import static org.hamcrest.Matchers.is;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
diff --git a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
index b61d868..3570c2e 100644
--- a/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewReceiveContentTest.java
@@ -33,7 +33,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.app.Activity;
 import android.app.Instrumentation;
@@ -159,7 +158,7 @@
         ContentInfo payload =
                 new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
-        verifyZeroInteractions(ic.mMock);
+        verifyNoMoreInteractions(ic.mMock);
     }
 
     @Test
@@ -180,19 +179,19 @@
         ContentInfo payload =
                 new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
-        verifyZeroInteractions(ic.mMock);
+        verifyNoMoreInteractions(ic.mMock);
 
         payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
-        verifyZeroInteractions(ic.mMock);
+        verifyNoMoreInteractions(ic.mMock);
 
         payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
-        verifyZeroInteractions(ic.mMock);
+        verifyNoMoreInteractions(ic.mMock);
 
         payload = new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
-        verifyZeroInteractions(ic.mMock);
+        verifyNoMoreInteractions(ic.mMock);
     }
 
     private static class MyInputConnection extends InputConnectionWrapper {
diff --git a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
index 381b566..ad68e38 100644
--- a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
+++ b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
@@ -37,7 +37,7 @@
     fun generatesProgress_onStart() {
         val linearTracker = linearTouchTracker()
         linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
-        val event = linearTracker.createStartEvent(null)
+        val event = linearTracker.createStartEvent()
         assertEquals(0f, event.progress, 0f)
     }
 
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 4d6c30e..66524d1 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -541,24 +541,30 @@
             throws RemoteException, InterruptedException {
         // Setup a callback that unregisters itself after the gesture is finished but before the
         // progress is animated back to 0f
-        final AtomicBoolean unregisterOnProgressUpdate = new AtomicBoolean(false);
+        final AtomicBoolean unregisterOnNextCallbackInvocation = new AtomicBoolean(false);
         final AtomicInteger onBackInvokedCalled = new AtomicInteger(0);
         final CountDownLatch onBackCancelledCalled = new CountDownLatch(1);
         OnBackAnimationCallback onBackAnimationCallback = new OnBackAnimationCallback() {
             @Override
             public void onBackProgressed(@NonNull BackEvent backEvent) {
-                if (unregisterOnProgressUpdate.get()) {
+                if (unregisterOnNextCallbackInvocation.getAndSet(false)) {
                     mDispatcher.unregisterOnBackInvokedCallback(this);
                 }
             }
 
             @Override
             public void onBackInvoked() {
+                if (unregisterOnNextCallbackInvocation.getAndSet(false)) {
+                    mDispatcher.unregisterOnBackInvokedCallback(this);
+                }
                 onBackInvokedCalled.getAndIncrement();
             }
 
             @Override
             public void onBackCancelled() {
+                if (unregisterOnNextCallbackInvocation.getAndSet(false)) {
+                    mDispatcher.unregisterOnBackInvokedCallback(this);
+                }
                 onBackCancelledCalled.countDown();
             }
         };
@@ -572,7 +578,7 @@
 
         // simulate back gesture finished and onBackCancelled() called, which starts the progress
         // animation back to 0f. On the first progress emission, the callback will unregister itself
-        unregisterOnProgressUpdate.set(true);
+        unregisterOnNextCallbackInvocation.set(true);
         callbackInfo.getCallback().onBackCancelled();
         waitForIdle();
         onBackCancelledCalled.await(1000, TimeUnit.MILLISECONDS);
@@ -689,8 +695,7 @@
                 /* frameTimeMillis = */ 0,
                 /* progress = */ progress,
                 /* triggerBack = */ false,
-                /* swipeEdge = */ BackEvent.EDGE_LEFT,
-                /* departingAnimationTarget = */ null);
+                /* swipeEdge = */ BackEvent.EDGE_LEFT);
     }
 
     private void verifyImeCallackRegistrations() throws RemoteException {
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 74b4de1..1977ff5 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -20,6 +20,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
 import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES;
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 
 import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
@@ -34,17 +35,16 @@
 import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -165,7 +165,7 @@
                 .thenReturn(accessibilityManager);
         when(mFrameworkObjectProvider.getAlertDialogBuilder(mContext))
                 .thenReturn(mAlertDialogBuilder);
-        when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), anyObject(), anyInt()))
+        when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), any(), anyInt()))
                 .thenReturn(mToast);
         when(mFrameworkObjectProvider.getSystemUiContext()).thenReturn(mContext);
         when(mFrameworkObjectProvider.getTextToSpeech(eq(mContext), any()))
@@ -179,20 +179,20 @@
         ResolveInfo resolveInfo = mock(ResolveInfo.class);
         resolveInfo.serviceInfo = mock(ServiceInfo.class);
         resolveInfo.serviceInfo.applicationInfo = mApplicationInfo;
-        when(resolveInfo.loadLabel(anyObject())).thenReturn(PACKAGE_NAME_STRING);
+        when(resolveInfo.loadLabel(any())).thenReturn(PACKAGE_NAME_STRING);
         when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
         when(mServiceInfo.getComponentName())
                 .thenReturn(ComponentName.unflattenFromString(SERVICE_NAME_STRING));
         when(mServiceInfo.loadSummary(any())).thenReturn(SERVICE_NAME_SUMMARY);
 
-        when(mAlertDialogBuilder.setTitle(anyObject())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setTitle(any())).thenReturn(mAlertDialogBuilder);
         when(mAlertDialogBuilder.setCancelable(anyBoolean())).thenReturn(mAlertDialogBuilder);
-        when(mAlertDialogBuilder.setMessage(anyObject())).thenReturn(mAlertDialogBuilder);
-        when(mAlertDialogBuilder.setPositiveButton(anyInt(), anyObject()))
+        when(mAlertDialogBuilder.setMessage(any())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setPositiveButton(anyInt(), any()))
                 .thenReturn(mAlertDialogBuilder);
-        when(mAlertDialogBuilder.setNegativeButton(anyInt(), anyObject()))
+        when(mAlertDialogBuilder.setNegativeButton(anyInt(), any()))
                 .thenReturn(mAlertDialogBuilder);
-        when(mAlertDialogBuilder.setOnCancelListener(anyObject())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setOnCancelListener(any())).thenReturn(mAlertDialogBuilder);
         when(mAlertDialogBuilder.create()).thenReturn(mAlertDialog);
 
         mLayoutParams.privateFlags = 0;
@@ -348,7 +348,7 @@
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         AccessibilityShortcutController accessibilityShortcutController = getController();
         accessibilityShortcutController.performAccessibilityShortcut();
-        verify(mVibrator).vibrate(aryEq(VIBRATOR_PATTERN_LONG), eq(-1), anyObject());
+        verify(mVibrator).vibrate(aryEq(VIBRATOR_PATTERN_LONG), eq(-1), any());
     }
 
     @Test
@@ -522,7 +522,7 @@
                 AccessibilityShortcutController.DialogStatus.SHOWN);
         getController().performAccessibilityShortcut();
 
-        verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
+        verifyNoMoreInteractions(mAlertDialogBuilder, mAlertDialog);
         verify(mToast).show();
         verify(mAccessibilityManagerService).performAccessibilityShortcut(
                 Display.DEFAULT_DISPLAY, HARDWARE, null);
@@ -615,7 +615,7 @@
                 AccessibilityShortcutController.DialogStatus.SHOWN);
         getController().performAccessibilityShortcut();
 
-        verifyZeroInteractions(mToast);
+        verifyNoMoreInteractions(mToast);
         verify(mAccessibilityManagerService).performAccessibilityShortcut(
                 Display.DEFAULT_DISPLAY, HARDWARE, null);
     }
@@ -632,7 +632,7 @@
                 AccessibilityShortcutController.DialogStatus.SHOWN);
         getController().performAccessibilityShortcut();
 
-        verifyZeroInteractions(mToast);
+        verifyNoMoreInteractions(mToast);
         verify(mAccessibilityManagerService).performAccessibilityShortcut(
                 Display.DEFAULT_DISPLAY, HARDWARE, null);
     }
@@ -715,6 +715,25 @@
         verify(mRingtone, times(0)).play();
     }
 
+    @Test
+    public void onUserSetupComplete_noEnabledServices_blankHardwareSetting() throws Exception {
+        AccessibilityShortcutController controller = getController();
+        configureValidShortcutService();
+        // Shortcut setting should be cleared on user setup
+        Settings.Secure.putStringForUser(
+                mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null, 0);
+        when(mAccessibilityManagerService
+                .getEnabledAccessibilityServiceList(anyInt(), eq(0)))
+                .thenReturn(Collections.emptyList());
+        Settings.Secure.putInt(mContentResolver, USER_SETUP_COMPLETE, 1);
+
+        controller.mUserSetupCompleteObserver.onChange(true);
+
+        final String shortcut = Settings.Secure.getStringForUser(
+                mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, 0);
+        assertThat(shortcut).isEqualTo("");
+    }
+
     private void configureNoShortcutService() throws Exception {
         when(mAccessibilityManagerService
                 .getAccessibilityShortcutTargets(HARDWARE))
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
index db69cf2..02a2968 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
@@ -72,7 +72,8 @@
 
     private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
             .getInstrumentation().getTargetContext().getUser();
-    private static final UserHandle WORK_USER_HANDLE = UserHandle.of(10);
+    private static final UserHandle WORK_USER_HANDLE =
+            UserHandle.of(PERSONAL_USER_HANDLE.getIdentifier() + 1);
 
     @Rule
     public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index d21ab44..15e746c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -87,6 +87,10 @@
 
     private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
             .getInstrumentation().getTargetContext().getUser();
+    private static final int WORK_USER_ID = PERSONAL_USER_HANDLE.getIdentifier() + 1;
+    private static final int CLONE_USER_ID = PERSONAL_USER_HANDLE.getIdentifier() + 2;
+    private static final int PRIVATE_USER_ID = PERSONAL_USER_HANDLE.getIdentifier() + 3;
+
     @Rule
     public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
             new ActivityTestRule<>(ResolverWrapperActivity.class, false,
@@ -247,7 +251,7 @@
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10,
+                createResolvedComponentsForTestWithOtherProfile(2, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         markWorkProfileUserAvailable();
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
@@ -270,7 +274,7 @@
         };
         // Make a stable copy of the components as the original list may be modified
         List<ResolvedComponentInfo> stableCopy =
-                createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10,
+                createResolvedComponentsForTestWithOtherProfile(2, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         // We pick the first one as there is another one in the work profile side
         onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
@@ -444,7 +448,7 @@
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(3, /* userId = */ 10,
+                createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         markWorkProfileUserAvailable();
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
@@ -456,7 +460,7 @@
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
         waitForIdle();
 
-        assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
+        assertThat(activity.getCurrentUserHandle(), is(PERSONAL_USER_HANDLE));
         // The work list adapter must be populated in advance before tapping the other tab
         assertThat(activity.getWorkListAdapter().getCount(), is(4));
     }
@@ -466,7 +470,7 @@
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+                createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         markWorkProfileUserAvailable();
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
@@ -478,7 +482,7 @@
         waitForIdle();
         onView(withText(R.string.resolver_work_tab)).perform(click());
 
-        assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
+        assertThat(activity.getCurrentUserHandle().getIdentifier(), is(WORK_USER_ID));
         assertThat(activity.getWorkListAdapter().getCount(), is(4));
     }
 
@@ -498,7 +502,7 @@
         waitForIdle();
         onView(withText(R.string.resolver_work_tab)).perform(click());
 
-        assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
+        assertThat(activity.getCurrentUserHandle().getIdentifier(), is(WORK_USER_ID));
         assertThat(activity.getPersonalListAdapter().getCount(), is(2));
     }
 
@@ -508,7 +512,7 @@
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         markWorkProfileUserAvailable();
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+                createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
                 sOverrides.workProfileUserHandle);
@@ -530,7 +534,7 @@
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         markWorkProfileUserAvailable();
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+                createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
                 sOverrides.workProfileUserHandle);
@@ -633,7 +637,7 @@
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         markWorkProfileUserAvailable();
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(3, /* userId= */ 10,
+                createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
                 sOverrides.workProfileUserHandle);
@@ -669,7 +673,7 @@
         markWorkProfileUserAvailable();
         int workProfileTargets = 4;
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+                createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         List<ResolvedComponentInfo> workResolvedComponentInfos =
                 createResolvedComponentsForTest(workProfileTargets,
@@ -697,7 +701,7 @@
         markWorkProfileUserAvailable();
         int workProfileTargets = 4;
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+                createResolvedComponentsForTestWithOtherProfile(3, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         List<ResolvedComponentInfo> workResolvedComponentInfos =
                 createResolvedComponentsForTest(workProfileTargets,
@@ -844,7 +848,7 @@
     public void testAutolaunch_singleTarget_withWorkProfileAndTabbedViewOff_noAutolaunch() {
         ResolverActivity.ENABLE_TABBED_VIEW = false;
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10,
+                createResolvedComponentsForTestWithOtherProfile(2, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
                 Mockito.anyBoolean(),
@@ -898,7 +902,7 @@
         markWorkProfileUserAvailable();
         int workProfileTargets = 4;
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10,
+                createResolvedComponentsForTestWithOtherProfile(2, WORK_USER_ID,
                         PERSONAL_USER_HANDLE);
         List<ResolvedComponentInfo> workResolvedComponentInfos =
                 createResolvedComponentsForTest(workProfileTargets,
@@ -1376,15 +1380,16 @@
     }
 
     private void markWorkProfileUserAvailable() {
-        ResolverWrapperActivity.sOverrides.workProfileUserHandle = UserHandle.of(10);
+        ResolverWrapperActivity.sOverrides.workProfileUserHandle = UserHandle.of(WORK_USER_ID);
     }
 
     private void markCloneProfileUserAvailable() {
-        ResolverWrapperActivity.sOverrides.cloneProfileUserHandle = UserHandle.of(11);
+        ResolverWrapperActivity.sOverrides.cloneProfileUserHandle = UserHandle.of(CLONE_USER_ID);
     }
 
     private void markPrivateProfileUserAvailable() {
-        ResolverWrapperActivity.sOverrides.privateProfileUserHandle = UserHandle.of(12);
+        ResolverWrapperActivity.sOverrides.privateProfileUserHandle =
+                UserHandle.of(PRIVATE_USER_ID);
     }
 
     private void setTabOwnerUserHandleForLaunch(UserHandle tabOwnerUserHandleForLaunch) {
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 90f5c24..cdf506c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -22,10 +22,10 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasSize;
 import static org.mockito.ArgumentMatchers.intThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 4604b01..050a68a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -112,8 +112,8 @@
 
     @Override
     protected ResolverListController createListController(UserHandle userHandle) {
-        if (userHandle == UserHandle.SYSTEM) {
-            when(sOverrides.resolverListController.getUserHandle()).thenReturn(UserHandle.SYSTEM);
+        if (userHandle == getUser()) {
+            when(sOverrides.resolverListController.getUserHandle()).thenReturn(getUser());
             return sOverrides.resolverListController;
         }
         if (isLaunchedInSingleUserMode()) {
diff --git a/core/tests/coretests/src/com/android/internal/os/BackgroundThreadTest.java b/core/tests/coretests/src/com/android/internal/os/BackgroundThreadTest.java
index 8bdf4c6..402ba0d 100644
--- a/core/tests/coretests/src/com/android/internal/os/BackgroundThreadTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BackgroundThreadTest.java
@@ -21,19 +21,13 @@
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.Looper;
-import android.platform.test.ravenwood.RavenwoodRule;
 
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.concurrent.Executor;
 
 public class BackgroundThreadTest {
 
-    @Rule
-    public final RavenwoodRule mRavenwood =
-            new RavenwoodRule.Builder().setProvideMainThread(true).build();
-
     @Test
     public void test_get() {
         BackgroundThread thread = BackgroundThread.get();
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 5f8ab28..f44aa05 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -29,7 +29,7 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.SystemClock;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.ArrayMap;
@@ -59,7 +59,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 @Presubmit
-@IgnoreUnderRavenwood(blockedBy = BinderCallsStats.class)
+@DisabledOnRavenwood(blockedBy = BinderCallsStats.class)
 public class BinderCallsStatsTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
index 3355cc3..5c2bf38 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
@@ -23,7 +23,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Binder;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.ArrayMap;
@@ -50,7 +50,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 @Presubmit
-@IgnoreUnderRavenwood(blockedBy = BinderLatencyObserver.class)
+@DisabledOnRavenwood(blockedBy = BinderLatencyObserver.class)
 public class BinderLatencyObserverTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/DebugTest.java b/core/tests/coretests/src/com/android/internal/os/DebugTest.java
index 4371f26..e6a0e4a 100644
--- a/core/tests/coretests/src/com/android/internal/os/DebugTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/DebugTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.Debug;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
@@ -28,7 +28,7 @@
 import org.junit.Test;
 
 @SmallTest
-@IgnoreUnderRavenwood(reason = "Requires ART support")
+@DisabledOnRavenwood(reason = "Requires ART support")
 public class DebugTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index 8fd87c0..a625317 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -24,7 +24,7 @@
 import android.content.Context;
 import android.os.FileUtils;
 import android.os.SystemClock;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.InstrumentationRegistry;
@@ -60,7 +60,7 @@
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelCpuProcStringReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
index 78cf65c..06d7521 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderDiffTest.java
@@ -24,7 +24,7 @@
 
 import static java.util.stream.Collectors.toList;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.ravenwood.RavenwoodRule;
 
@@ -47,7 +47,7 @@
 @Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelCpuThreadReaderDiffTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
index 8c5e3d0..bb7a81a 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
@@ -22,7 +22,7 @@
 
 import android.os.Process;
 import android.os.SystemClock;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -50,7 +50,7 @@
  */
 @RunWith(AndroidJUnit4.class)
 @LargeTest
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelCpuThreadReaderEndToEndTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index c3d4b83..74dd999 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -25,7 +25,7 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.ravenwood.RavenwoodRule;
 
@@ -51,7 +51,7 @@
 @Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelCpuThreadReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
index d35e0fc..fc090ca 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -21,7 +21,7 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseArray;
 import android.util.SparseLongArray;
@@ -54,7 +54,7 @@
  */
 @SmallTest
 @RunWith(Parameterized.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelCpuUidActiveTimeReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
index b75ad7f..5a01175 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
@@ -23,7 +23,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseArray;
 
@@ -52,7 +52,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelCpuUidBpfMapReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
index 8807de0..bde66c7 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -23,7 +23,7 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseArray;
 
@@ -55,7 +55,7 @@
  */
 @SmallTest
 @RunWith(Parameterized.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelCpuUidClusterTimeReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
index b730344..28c340d 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -24,7 +24,7 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseArray;
 
@@ -58,7 +58,7 @@
  */
 @SmallTest
 @RunWith(Parameterized.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelCpuUidFreqTimeReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
index 864e198..010d3d6 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -23,7 +23,7 @@
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseArray;
 
@@ -52,7 +52,7 @@
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelCpuUidUserSysTimeReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
index a74f339..2b99a27 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelMemoryBandwidthStatsTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.LongSparseLongArray;
 
@@ -36,7 +36,7 @@
  * Tests for KernelMemoryBandwidthStats parsing and delta calculation, based on memory_state_time.
  */
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelMemoryBandwidthStatsTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
index cdfef25..a4b44c7 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
@@ -19,7 +19,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -35,7 +35,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelSingleProcessCpuThreadReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index 0ba2d85..262d6a2 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -24,7 +24,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.annotation.SuppressLint;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseArray;
 
@@ -47,7 +47,7 @@
 
 @SmallTest
 @RunWith(Parameterized.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class KernelSingleUidTimeReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
index 93dd09d..0de5f15 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.fail;
 
 import android.os.FileUtils;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -40,7 +40,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ProcTimeInStateReader.class)
+@DisabledOnRavenwood(blockedBy = ProcTimeInStateReader.class)
 public class ProcTimeInStateReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcessCpuTrackerTest.java b/core/tests/coretests/src/com/android/internal/os/ProcessCpuTrackerTest.java
index d11c500..46cb382 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcessCpuTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcessCpuTrackerTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
@@ -30,7 +30,7 @@
 
 @SmallTest
 @RunWith(JUnit4.class)
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
+@DisabledOnRavenwood(reason = "Needs kernel support")
 public class ProcessCpuTrackerTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java b/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java
index d1c0668..dad1f13 100644
--- a/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java
+++ b/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java
@@ -32,7 +32,7 @@
 
     @Test
     public void testIsRunningOnRavenwood() {
-        assertEquals(RavenwoodRule.isUnderRavenwood(),
+        assertEquals(RavenwoodRule.isOnRavenwood(),
                 RavenwoodEnvironment.getInstance().isRunningOnRavenwood());
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java
index aa59afe..28533de 100644
--- a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java
@@ -23,7 +23,7 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -43,7 +43,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = Color.class)
+@DisabledOnRavenwood(blockedBy = Color.class)
 public class ContrastColorUtilTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java
index aee352b..83eb1e6 100644
--- a/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/FakeLatencyTrackerTest.java
@@ -24,7 +24,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.provider.DeviceConfig;
 
@@ -45,7 +45,7 @@
  * {@link LatencyTrackerTest}
  */
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = DeviceConfig.class)
+@DisabledOnRavenwood(blockedBy = DeviceConfig.class)
 public class FakeLatencyTrackerTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/util/FastDataTest.java b/core/tests/coretests/src/com/android/internal/util/FastDataTest.java
index 316b95ac..f603fdb 100644
--- a/core/tests/coretests/src/com/android/internal/util/FastDataTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/FastDataTest.java
@@ -64,7 +64,7 @@
 
     @Parameters(name = "use4ByteSequence={0}")
     public static Collection<Object[]> data() {
-        if (RavenwoodRule.isUnderRavenwood()) {
+        if (RavenwoodRule.isOnRavenwood()) {
             // TODO: 4-byte sequences are only supported on ART
             return Arrays.asList(new Object[][]{{false}});
         } else {
diff --git a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
index ce265a3..1415651 100644
--- a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
@@ -25,7 +25,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.provider.DeviceConfig;
 
@@ -51,7 +51,7 @@
 import java.util.stream.Collectors;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = DeviceConfig.class)
+@DisabledOnRavenwood(blockedBy = DeviceConfig.class)
 public class LatencyTrackerTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java b/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java
index e0d5499..bb1c9ed 100644
--- a/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -29,7 +29,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ProgressReporter.class)
+@DisabledOnRavenwood(blockedBy = ProgressReporter.class)
 public class ProgressReporterTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
index d1fbc77c..e0e24e7 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
@@ -53,7 +53,7 @@
 import android.os.UserManager;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.provider.Settings;
@@ -80,7 +80,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-@IgnoreUnderRavenwood(blockedBy = LockPatternUtils.class)
+@DisabledOnRavenwood(blockedBy = LockPatternUtils.class)
 public class LockPatternUtilsTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 17fe15c..21ef391 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -28,7 +28,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.annotation.EnforcePermission;
 import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
@@ -207,7 +207,7 @@
 
         mService.setSupportedStates(List.of(OTHER_DEVICE_STATE));
         mService.setBaseState(OTHER_DEVICE_STATE);
-        verifyZeroInteractions(callback);
+        verifyNoMoreInteractions(callback);
     }
 
     @Test
diff --git a/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
index 8de9196..7c60462 100644
--- a/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
+++ b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java
@@ -22,10 +22,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.contains;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.matches;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.matches;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 
diff --git a/core/tests/utiltests/src/android/util/AtomicFileTest.java b/core/tests/utiltests/src/android/util/AtomicFileTest.java
index 742307b..8897f0f 100644
--- a/core/tests/utiltests/src/android/util/AtomicFileTest.java
+++ b/core/tests/utiltests/src/android/util/AtomicFileTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.Mockito.spy;
 
 import android.os.SystemClock;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.annotation.NonNull;
@@ -281,7 +281,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(blockedBy = SystemConfigFileCommitEventLogger.class)
+    @DisabledOnRavenwood(blockedBy = SystemConfigFileCommitEventLogger.class)
     public void testTimeLogging() throws Exception {
         var logger = spy(new SystemConfigFileCommitEventLogger("name"));
         var file = new AtomicFile(mBaseFile, logger);
diff --git a/core/tests/utiltests/src/android/util/EventLogTest.java b/core/tests/utiltests/src/android/util/EventLogTest.java
index 0ebf2c1..35803e7 100644
--- a/core/tests/utiltests/src/android/util/EventLogTest.java
+++ b/core/tests/utiltests/src/android/util/EventLogTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.EventLog.Event;
 
@@ -51,7 +51,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Reading not yet supported")
+    @DisabledOnRavenwood(reason = "Reading not yet supported")
     public void testWithNewData() throws Throwable {
         Event event = createEvent(() -> {
             EventLog.writeEvent(314,  123);
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
index 8093af9..1459212 100644
--- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -23,7 +23,7 @@
 import static org.junit.Assert.fail;
 
 import android.os.Parcel;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -39,13 +39,13 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = MemoryIntArray.class)
+@DisabledOnRavenwood(blockedBy = MemoryIntArray.class)
 public class MemoryIntArrayTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
 
     static {
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             System.loadLibrary("cutils");
             System.loadLibrary("memoryintarraytest");
         }
diff --git a/core/tests/utiltests/src/android/util/MetadataReaderTest.java b/core/tests/utiltests/src/android/util/MetadataReaderTest.java
index 14feed8..5981506 100644
--- a/core/tests/utiltests/src/android/util/MetadataReaderTest.java
+++ b/core/tests/utiltests/src/android/util/MetadataReaderTest.java
@@ -20,7 +20,7 @@
 
 import android.media.ExifInterface;
 import android.os.Bundle;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.provider.DocumentsContract;
 import android.provider.MetadataReader;
@@ -41,7 +41,7 @@
 import java.io.InputStream;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = MetadataReader.class)
+@DisabledOnRavenwood(blockedBy = MetadataReader.class)
 public class MetadataReaderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/utiltests/src/android/util/SystemConfigFileCommitEventLoggerTest.java b/core/tests/utiltests/src/android/util/SystemConfigFileCommitEventLoggerTest.java
index 3bb79ec..d64de68 100644
--- a/core/tests/utiltests/src/android/util/SystemConfigFileCommitEventLoggerTest.java
+++ b/core/tests/utiltests/src/android/util/SystemConfigFileCommitEventLoggerTest.java
@@ -21,7 +21,7 @@
 import static org.mockito.Mockito.times;
 
 import android.os.SystemClock;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -32,7 +32,7 @@
 import org.mockito.Mockito;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = SystemConfigFileCommitEventLogger.class)
+@DisabledOnRavenwood(blockedBy = SystemConfigFileCommitEventLogger.class)
 public class SystemConfigFileCommitEventLoggerTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
index 9888540..b86c815 100644
--- a/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
@@ -32,7 +32,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = CharSequences.class)
+@DisabledOnRavenwood(blockedBy = CharSequences.class)
 public class CharSequencesTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java b/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java
index f91172d..259d394 100644
--- a/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.provider.DeviceConfig;
 import android.util.Log;
@@ -145,7 +145,7 @@
 
     @Test
     @LargeTest
-    @IgnoreUnderRavenwood(reason = "Long test runtime")
+    @DisabledOnRavenwood(reason = "Long test runtime")
     public void testAllCharacters() throws Exception {
         boolean ok = true;
         for (int i = 0; i < 0xffff; i++) {
diff --git a/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java
index 7203b8c..1af58a3 100644
--- a/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/InlinePresentationStyleUtilsTest.java
@@ -25,7 +25,7 @@
 import android.graphics.Color;
 import android.os.Binder;
 import android.os.Bundle;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
@@ -39,7 +39,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-@IgnoreUnderRavenwood(blockedBy = InlinePresentationStyleUtils.class)
+@DisabledOnRavenwood(blockedBy = InlinePresentationStyleUtils.class)
 public class InlinePresentationStyleUtilsTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java
index 6c34797..7e523bb 100644
--- a/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -31,7 +31,7 @@
  * Tests for {@link MimeIconUtils}.
  */
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = MimeIconUtils.class)
+@DisabledOnRavenwood(blockedBy = MimeIconUtils.class)
 public class MimeIconUtilsTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java b/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java
index c852e9f..dc353a5 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ObservableServiceConnectionTest.java
@@ -29,7 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
@@ -52,7 +52,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ObservableServiceConnection.class)
+@DisabledOnRavenwood(blockedBy = ObservableServiceConnection.class)
 public class ObservableServiceConnectionTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java b/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java
index 096f303..a881873 100644
--- a/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/PersistentServiceConnectionTest.java
@@ -30,7 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -52,7 +52,7 @@
 import java.util.concurrent.Executor;
 
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = PersistentServiceConnection.class)
+@DisabledOnRavenwood(blockedBy = PersistentServiceConnection.class)
 public class PersistentServiceConnectionTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
index b0db8a1..c7e0bbd 100644
--- a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
@@ -24,8 +24,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
-import android.platform.test.ravenwood.RavenwoodRule;
+import android.platform.test.annotations.DisabledOnRavenwood;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -47,7 +46,7 @@
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = WakeupMessage.class)
+@DisabledOnRavenwood(blockedBy = WakeupMessage.class)
 public class WakeupMessageTest {
     private static final String TEST_CMD_NAME = "TEST cmd Name";
     private static final int TEST_CMD = 18;
@@ -55,11 +54,6 @@
     private static final int TEST_ARG2 = 182;
     private static final Object TEST_OBJ = "hello";
 
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     @Mock Context mContext;
     @Mock AlarmManager mAlarmManager;
     WakeupMessage mMessage;
diff --git a/core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java b/core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java
index 502d6b6..abedc4d 100644
--- a/core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/test/FakeSettingsProviderTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.fail;
 
 import android.content.ContentProvider;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
@@ -37,7 +37,7 @@
  * Unit tests for FakeSettingsProvider.
  */
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = ContentProvider.class)
+@DisabledOnRavenwood(blockedBy = ContentProvider.class)
 public class FakeSettingsProviderTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 3b40148..f25ceb1 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -139,4 +139,10 @@
     <install-in-user-type package="com.android.multiuser">
         <install-in user-type="FULL" />
     </install-in-user-type>
+
+    <!-- PrivateSpace App, only install in private profile -->
+    <install-in-user-type package="com.android.privatespace">
+        <install-in user-type="android.os.usertype.profile.PRIVATE" />
+    </install-in-user-type>
+
 </config>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index cd5a54c..a4ba2b3 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -755,6 +755,9 @@
         if (b != null) {
             b.setPremultiplied(mRequestPremultiplied);
             b.mDensity = mDensity;
+            if (hasGainmap()) {
+                b.setGainmap(getGainmap().asShared());
+            }
         }
         return b;
     }
@@ -767,7 +770,8 @@
      */
     @NonNull
     public Bitmap asShared() {
-        if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)) {
+        if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)
+                && (!hasGainmap() || getGainmap().asShared() == getGainmap())) {
             return this;
         }
         Bitmap shared = createAshmemBitmap();
@@ -2091,7 +2095,7 @@
      */
     public void setGainmap(@Nullable Gainmap gainmap) {
         checkRecycled("Bitmap is recycled");
-        mGainmap = null;
+        mGainmap = gainmap;
         nativeSetGainmap(mNativePtr, gainmap == null ? 0 : gainmap.mNativePtr);
     }
 
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
index 7fc13db..2417a12 100644
--- a/graphics/java/android/graphics/Gainmap.java
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -161,6 +161,18 @@
     }
 
     /**
+     * @hide
+     */
+    public Gainmap asShared() {
+        final Bitmap sharedContents = mGainmapContents.asShared();
+        if (sharedContents == mGainmapContents) {
+            return this;
+        } else {
+            return new Gainmap(sharedContents, nCreateCopy(mNativePtr));
+        }
+    }
+
+    /**
      * @return Returns the image data of the gainmap represented as a Bitmap. This is represented
      * as a Bitmap for broad API compatibility, however certain aspects of the Bitmap are ignored
      * such as {@link Bitmap#getColorSpace()} or {@link Bitmap#getGainmap()} as they are not
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index e141f70..da25da1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -54,6 +54,8 @@
 import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
 import static androidx.window.extensions.embedding.TaskFragmentContainer.OverlayContainerRestoreParams;
 
+import static com.android.window.flags.Flags.activityEmbeddingDelayTaskFragmentFinishForActivityLaunch;
+
 import android.annotation.CallbackExecutor;
 import android.app.Activity;
 import android.app.ActivityClient;
@@ -815,11 +817,17 @@
                         .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
                 mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
             } else if (!container.isWaitingActivityAppear()) {
-                // Do not finish the container before the expected activity appear until
-                // timeout.
-                mTransactionManager.getCurrentTransactionRecord()
-                        .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
-                mPresenter.cleanupContainer(wct, container, true /* shouldFinishDependent */);
+                if (activityEmbeddingDelayTaskFragmentFinishForActivityLaunch()
+                        && container.hasActivityLaunchHint()) {
+                    // If we have recently attempted to launch a new activity into this
+                    // TaskFragment, we schedule delayed cleanup. If the new activity appears in
+                    // this TaskFragment, we no longer need to finish the TaskFragment.
+                    container.scheduleDelayedTaskFragmentCleanup();
+                } else {
+                    mTransactionManager.getCurrentTransactionRecord()
+                            .setOriginType(TASK_FRAGMENT_TRANSIT_CLOSE);
+                    mPresenter.cleanupContainer(wct, container, true /* shouldFinishDependent */);
+                }
             }
         } else if (wasInPip && isInPip) {
             // No update until exit PIP.
@@ -3164,6 +3172,9 @@
                     // TODO(b/229680885): skip override launching TaskFragment token by split-rule
                     options.putBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
                             launchedInTaskFragment.getTaskFragmentToken());
+                    if (activityEmbeddingDelayTaskFragmentFinishForActivityLaunch()) {
+                        launchedInTaskFragment.setActivityLaunchHint();
+                    }
                     mCurrentIntent = intent;
                 } else {
                     transactionRecord.abort();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index b3e003e..6fa855e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -18,6 +18,8 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
+import static com.android.window.flags.Flags.activityEmbeddingDelayTaskFragmentFinishForActivityLaunch;
+
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.WindowConfiguration.WindowingMode;
@@ -53,6 +55,8 @@
 class TaskFragmentContainer {
     private static final int APPEAR_EMPTY_TIMEOUT_MS = 3000;
 
+    private static final int DELAYED_TASK_FRAGMENT_CLEANUP_TIMEOUT_MS = 500;
+
     /** Parcelable data of this TaskFragmentContainer. */
     @NonNull
     private final ParcelableTaskFragmentContainerData mParcelableData;
@@ -165,6 +169,18 @@
      */
     private boolean mLastDimOnTask;
 
+    /** The timestamp of the latest pending activity launch attempt. 0 means no pending launch. */
+    private long mLastActivityLaunchTimestampMs = 0;
+
+    /**
+     * The scheduled runnable for delayed TaskFragment cleanup. This is used when the TaskFragment
+     * becomes empty, but we expect a new activity to appear in it soon.
+     *
+     * It should be {@code null} when not scheduled.
+     */
+    @Nullable
+    private Runnable mDelayedTaskFragmentCleanupRunnable;
+
     /**
      * Creates a container with an existing activity that will be re-parented to it in a window
      * container transaction.
@@ -540,6 +556,10 @@
             mAppearEmptyTimeout = null;
         }
 
+        if (activityEmbeddingDelayTaskFragmentFinishForActivityLaunch()) {
+            clearActivityLaunchHintIfNecessary(mInfo, info);
+        }
+
         mHasCrossProcessActivities = false;
         mInfo = info;
         if (mInfo == null || mInfo.isEmpty()) {
@@ -1064,6 +1084,89 @@
         return isOverlay() && mParcelableData.mAssociatedActivityToken != null;
     }
 
+    /**
+     * Indicates whether there is possibly a pending activity launching into this TaskFragment.
+     *
+     * This should only be used as a hint because we cannot reliably determine if the new activity
+     * is going to appear into this TaskFragment.
+     *
+     * TODO(b/293800510) improve activity launch tracking in TaskFragment.
+     */
+    boolean hasActivityLaunchHint() {
+        if (mLastActivityLaunchTimestampMs == 0) {
+            return false;
+        }
+        if (System.currentTimeMillis() > mLastActivityLaunchTimestampMs + APPEAR_EMPTY_TIMEOUT_MS) {
+            // The hint has expired after APPEAR_EMPTY_TIMEOUT_MS.
+            mLastActivityLaunchTimestampMs = 0;
+            return false;
+        }
+        return true;
+    }
+
+    /** Records the latest activity launch attempt. */
+    void setActivityLaunchHint() {
+        mLastActivityLaunchTimestampMs = System.currentTimeMillis();
+    }
+
+    /**
+     * If we get a new info showing that the TaskFragment has more activities than the previous
+     * info, we clear the new activity launch hint.
+     *
+     * Note that this is not a reliable way and cannot cover situations when the attempted
+     * activity launch did not cause TaskFragment info activity count changes, such as trampoline
+     * launches or single top launches.
+     *
+     * TODO(b/293800510) improve activity launch tracking in TaskFragment.
+     */
+    private void clearActivityLaunchHintIfNecessary(
+            @Nullable TaskFragmentInfo oldInfo, @NonNull TaskFragmentInfo newInfo) {
+        final int previousActivityCount = oldInfo == null ? 0 : oldInfo.getRunningActivityCount();
+        if (newInfo.getRunningActivityCount() > previousActivityCount) {
+            mLastActivityLaunchTimestampMs = 0;
+            cancelDelayedTaskFragmentCleanup();
+        }
+    }
+
+    /**
+     * Schedules delayed TaskFragment cleanup due to pending activity launch. The scheduled cleanup
+     * will be canceled if a new activity appears in this TaskFragment.
+     */
+    void scheduleDelayedTaskFragmentCleanup() {
+        if (mDelayedTaskFragmentCleanupRunnable != null) {
+            // Remove the previous callback if there is already one scheduled.
+            mController.getHandler().removeCallbacks(mDelayedTaskFragmentCleanupRunnable);
+        }
+        mDelayedTaskFragmentCleanupRunnable = new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mController.mLock) {
+                    if (mDelayedTaskFragmentCleanupRunnable != this) {
+                        // The scheduled cleanup runnable has been canceled or rescheduled, so
+                        // skipping.
+                        return;
+                    }
+                    if (isEmpty()) {
+                        mLastActivityLaunchTimestampMs = 0;
+                        mController.onTaskFragmentAppearEmptyTimeout(
+                                TaskFragmentContainer.this);
+                    }
+                    mDelayedTaskFragmentCleanupRunnable = null;
+                }
+            }
+        };
+        mController.getHandler().postDelayed(
+                mDelayedTaskFragmentCleanupRunnable, DELAYED_TASK_FRAGMENT_CLEANUP_TIMEOUT_MS);
+    }
+
+    private void cancelDelayedTaskFragmentCleanup() {
+        if (mDelayedTaskFragmentCleanupRunnable == null) {
+            return;
+        }
+        mController.getHandler().removeCallbacks(mDelayedTaskFragmentCleanupRunnable);
+        mDelayedTaskFragmentCleanupRunnable = null;
+    }
+
     @Override
     public String toString() {
         return toString(true /* includeContainersToFinishOnExit */);
diff --git a/libs/WindowManager/Shell/aconfig/OWNERS b/libs/WindowManager/Shell/aconfig/OWNERS
index 9eba0f2..eacadea 100644
--- a/libs/WindowManager/Shell/aconfig/OWNERS
+++ b/libs/WindowManager/Shell/aconfig/OWNERS
@@ -1,3 +1,4 @@
 # Owners for flag changes
 madym@google.com
-hwwang@google.com
\ No newline at end of file
+hwwang@google.com
+sqsun@google.com
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 1e72d64..b6a1501 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -114,7 +114,7 @@
     name: "enable_shell_top_task_tracking"
     namespace: "multitasking"
     description: "Enables tracking top tasks from the shell"
-    bug: "342627272"
+    bug: "346588978"
     metadata {
         purpose: PURPOSE_BUGFIX
     }
@@ -194,3 +194,20 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "enable_gsf"
+    namespace: "multitasking"
+    description: "Applies GSF font styles to multitasking."
+    bug: "400534660"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "enable_magnetic_split_divider"
+    namespace: "multitasking"
+    description: "Makes the split divider snap 'magnetically' to available snap points during drag"
+    bug: "383631946"
+}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryScreenshotTest.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryScreenshotTest.kt
index 24f43d3..5777cb0 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryScreenshotTest.kt
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryScreenshotTest.kt
@@ -94,6 +94,7 @@
 
         private val splitScreenModeName =
             when (splitScreenMode) {
+                SplitScreenMode.UNSUPPORTED -> "_split_unsupported"
                 SplitScreenMode.NONE -> ""
                 SplitScreenMode.SPLIT_50_50 -> "_split_50_50"
                 SplitScreenMode.SPLIT_10_90 -> "_split_10_90"
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
index 03076c0..5066691 100644
--- a/libs/WindowManager/Shell/multivalentTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -51,6 +51,7 @@
         "androidx.test.ext.junit",
         "mockito-robolectric-prebuilt",
         "mockito-kotlin2",
+        "platform-parametric-runner-lib",
         "truth",
         "flag-junit-base",
         "flag-junit",
@@ -74,6 +75,7 @@
         "frameworks-base-testutils",
         "mockito-kotlin2",
         "mockito-target-extended-minus-junit4",
+        "platform-parametric-runner-lib",
         "truth",
         "platform-test-annotations",
         "platform-test-rules",
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleExpandedViewTest.kt
new file mode 100644
index 0000000..bdfaef2
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleExpandedViewTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles
+
+import android.content.ComponentName
+import android.content.Context
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.Flags
+import com.android.wm.shell.taskview.TaskView
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+/** Tests for [BubbleExpandedView] */
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+class BubbleExpandedViewTest(flags: FlagsParameterization) {
+
+    @get:Rule
+    val setFlagsRule = SetFlagsRule(flags)
+
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+    private val componentName = ComponentName(context, "TestClass")
+
+    @Test
+    fun getTaskId_onTaskCreated_returnsCorrectTaskId() {
+        val bubbleTaskView = BubbleTaskView(mock<TaskView>(), directExecutor())
+        val expandedView = BubbleExpandedView(context).apply {
+            initialize(
+                mock<BubbleExpandedViewManager>(),
+                mock<BubbleStackView>(),
+                mock<BubblePositioner>(),
+                false /* isOverflow */,
+                bubbleTaskView,
+            )
+            setAnimating(true) // Skips setContentVisibility for testing.
+        }
+
+        bubbleTaskView.listener.onTaskCreated(123, componentName)
+
+        assertThat(expandedView.getTaskId()).isEqualTo(123)
+    }
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams() = FlagsParameterization.allCombinationsOf(
+            Flags.FLAG_ENABLE_BUBBLE_TASK_VIEW_LISTENER,
+        )
+    }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
index 9087da3..636ff66 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
@@ -266,8 +266,6 @@
             optionsCaptor.capture(),
             any())
 
-        assertThat((intentCaptor.lastValue.flags
-                and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
         assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
         assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
         assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
@@ -295,8 +293,6 @@
             optionsCaptor.capture(),
             any())
 
-        assertThat((intentCaptor.lastValue.flags
-                and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
         assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
         assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
         assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
@@ -324,8 +320,6 @@
             optionsCaptor.capture(),
             any())
 
-        assertThat((intentCaptor.lastValue.flags
-                and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
         assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
         assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
         assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubject.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubject.kt
index 2d6df43..3597ce0 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubject.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubject.kt
@@ -22,14 +22,14 @@
 import com.google.common.truth.Truth
 
 /** Subclass of [Subject] to simplify verifying [FakeUiEvent] data */
-class UiEventSubject(metadata: FailureMetadata, private val actual: FakeUiEvent) :
+class UiEventSubject(metadata: FailureMetadata, private val actual: FakeUiEvent?) :
     Subject(metadata, actual) {
 
     /** Check that [FakeUiEvent] contains the expected data from the [bubble] passed id */
     fun hasBubbleInfo(bubble: Bubble) {
-        check("uid").that(actual.uid).isEqualTo(bubble.appUid)
-        check("packageName").that(actual.packageName).isEqualTo(bubble.packageName)
-        check("instanceId").that(actual.instanceId).isEqualTo(bubble.instanceId)
+        check("uid").that(actual?.uid).isEqualTo(bubble.appUid)
+        check("packageName").that(actual?.packageName).isEqualTo(bubble.packageName)
+        check("instanceId").that(actual?.instanceId).isEqualTo(bubble.instanceId)
     }
 
     companion object {
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_ic_settings.xml b/libs/WindowManager/Shell/res/drawable/bubble_ic_settings.xml
new file mode 100644
index 0000000..52b9ac2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/bubble_ic_settings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M433,880Q406,880 386.5,862Q367,844 363,818L354,752Q341,747 329.5,740Q318,733 307,725L245,751Q220,762 195,753Q170,744 156,721L109,639Q95,616 101,590Q107,564 128,547L181,507Q180,500 180,493.5Q180,487 180,480Q180,473 180,466.5Q180,460 181,453L128,413Q107,396 101,370Q95,344 109,321L156,239Q170,216 195,207Q220,198 245,209L307,235Q318,227 330,220Q342,213 354,208L363,142Q367,116 386.5,98Q406,80 433,80L527,80Q554,80 573.5,98Q593,116 597,142L606,208Q619,213 630.5,220Q642,227 653,235L715,209Q740,198 765,207Q790,216 804,239L851,321Q865,344 859,370Q853,396 832,413L779,453Q780,460 780,466.5Q780,473 780,480Q780,487 780,493.5Q780,500 778,507L831,547Q852,564 858,590Q864,616 850,639L802,721Q788,744 763,753Q738,762 713,751L653,725Q642,733 630,740Q618,747 606,752L597,818Q593,844 573.5,862Q554,880 527,880L433,880ZM482,620Q540,620 581,579Q622,538 622,480Q622,422 581,381Q540,340 482,340Q423,340 382.5,381Q342,422 342,480Q342,538 382.5,579Q423,620 482,620Z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_minimize.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_minimize.xml
index b35dc02..56e5dc7 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_minimize.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_minimize.xml
@@ -18,9 +18,9 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:viewportHeight="24"
-    android:viewportWidth="24">
+    android:viewportWidth="960"
+    android:viewportHeight="960">
     <path
         android:fillColor="#FF000000"
-        android:pathData="M6,21V19H18V21Z"/>
+        android:pathData="M160,800L160,720L800,720L800,800L160,800Z"/>
 </vector>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
index fc8b2991..ef30d89 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -31,9 +31,10 @@
         android:layout_height="@dimen/bubble_popup_icon_size"
         android:tint="@androidprv:color/materialColorOutline"
         android:contentDescription="@null"
-        android:src="@drawable/pip_ic_settings"/>
+        android:src="@drawable/bubble_ic_settings"/>
 
     <TextView
+        android:id="@+id/education_manage_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/bubble_popup_text_margin"
@@ -45,6 +46,7 @@
         android:text="@string/bubble_bar_education_manage_title"/>
 
     <TextView
+        android:id="@+id/education_manage_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/bubble_popup_text_margin"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
index 1616707..9076d6a 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -34,6 +34,7 @@
         android:src="@drawable/ic_floating_landscape"/>
 
     <TextView
+        android:id="@+id/education_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/bubble_popup_text_margin"
@@ -45,6 +46,7 @@
         android:text="@string/bubble_bar_education_stack_title"/>
 
     <TextView
+        android:id="@+id/education_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/bubble_popup_text_margin"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 4daaf9c..17ebac9 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -40,6 +40,7 @@
             android:tint="@color/bubbles_icon_tint"/>
 
         <TextView
+            android:id="@+id/manage_dismiss"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="16dp"
@@ -67,6 +68,7 @@
             android:tint="@color/bubbles_icon_tint"/>
 
         <TextView
+            android:id="@+id/manage_dont_bubble"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="16dp"
@@ -103,4 +105,35 @@
 
     </LinearLayout>
 
+    <!-- Menu option to move a bubble to fullscreen; only visible if bubble anything is enabled. -->
+    <LinearLayout
+        android:id="@+id/bubble_manage_menu_fullscreen_container"
+        android:background="@drawable/bubble_manage_menu_row"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:minHeight="@dimen/bubble_menu_item_height"
+        android:gravity="center_vertical"
+        android:paddingStart="@dimen/bubble_menu_padding"
+        android:paddingEnd="@dimen/bubble_menu_padding"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:id="@+id/bubble_manage_menu_fullscreen_icon"
+            android:layout_width="@dimen/bubble_menu_icon_size"
+            android:layout_height="@dimen/bubble_menu_icon_size"
+            android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
+            android:tint="@color/bubbles_icon_tint"/>
+
+        <TextView
+            android:id="@+id/bubble_manage_menu_fullscreen_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dp"
+            android:text="@string/bubble_fullscreen_text"
+            android:textColor="@androidprv:color/materialColorOnSurface"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
+
+    </LinearLayout>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index a754535..bfaa407 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -22,8 +22,8 @@
     android:layout_height="wrap_content"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation"
-    android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation"
+    android:paddingBottom="@dimen/desktop_mode_handle_menu_pill_elevation_padding"
+    android:paddingEnd="@dimen/desktop_mode_handle_menu_pill_elevation_padding"
     android:orientation="vertical">
 
     <LinearLayout
@@ -39,12 +39,11 @@
 
         <ImageView
             android:id="@+id/application_icon"
-            android:layout_width="@dimen/desktop_mode_caption_icon_radius"
-            android:layout_height="@dimen/desktop_mode_caption_icon_radius"
+            android:layout_width="@dimen/desktop_mode_handle_menu_icon_radius"
+            android:layout_height="@dimen/desktop_mode_handle_menu_icon_radius"
             android:layout_marginStart="10dp"
             android:layout_marginEnd="12dp"
-            android:contentDescription="@string/app_icon_text"
-            android:importantForAccessibility="no"/>
+            android:contentDescription="@string/app_icon_text" />
 
         <com.android.wm.shell.windowdecor.MarqueedTextView
             android:id="@+id/application_name"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
index 0163c01..35e7de0 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
@@ -22,8 +22,7 @@
     android:layout_height="match_parent"
     android:gravity="start|center_vertical"
     android:paddingHorizontal="16dp"
-    android:clickable="true"
-    android:focusable="true"
+    android:importantForAccessibility="yes"
     android:orientation="horizontal"
     android:background="?android:attr/selectableItemBackground">
 
@@ -34,5 +33,6 @@
 
     <com.android.wm.shell.windowdecor.MarqueedTextView
         android:id="@+id/label"
+        android:importantForAccessibility="no"
         style="@style/DesktopModeHandleMenuActionButtonTextView"/>
 </LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index c2aa146..3ebe87d 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -38,11 +38,11 @@
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:orientation="vertical"
-            android:layout_marginStart="4dp"
-            android:layout_marginEnd="4dp">
+            android:gravity="center_horizontal"
+            android:layout_marginHorizontal="4dp">
 
             <Button
-                android:layout_width="94dp"
+                android:layout_width="108dp"
                 android:layout_height="60dp"
                 android:id="@+id/maximize_menu_immersive_toggle_button"
                 style="?android:attr/buttonBarButtonStyle"
@@ -75,8 +75,7 @@
             android:layout_weight="1"
             android:orientation="vertical"
             android:gravity="center_horizontal"
-            android:layout_marginStart="4dp"
-            android:layout_marginEnd="4dp">
+            android:layout_marginHorizontal="4dp">
 
             <Button
                 android:layout_width="108dp"
@@ -112,8 +111,7 @@
             android:layout_weight="1"
             android:orientation="vertical"
             android:gravity="center_horizontal"
-            android:layout_marginStart="4dp"
-            android:layout_marginEnd="4dp">
+            android:layout_marginHorizontal="4dp">
             <LinearLayout
                 android:id="@+id/maximize_menu_snap_menu_layout"
                 android:layout_width="wrap_content"
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 4dffce5..1491c70 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Links 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Links 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Volskerm regs"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Ruil boonste app met onderste een"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Ruil linkerapp met regterapp"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Volskerm bo"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Bo 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bo 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Die appkieslys kan hier gevind word"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Maak rekenaaraansig oop om veelvuldige apps saam oop te maak"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Keer enige tyd terug na volskerm vanaf die appkieslys"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sien en doen meer"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Sleep ’n ander app in vir verdeelde skerm"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Herbegin"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Moenie weer wys nie"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Dubbeltik om\nhierdie app te skuif"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maksimeer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> terug"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimeer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Maak <xliff:g id="APP_NAME">%1$s</xliff:g> toe"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Terug"</string>
     <string name="handle_text" msgid="4419667835599523257">"Apphandvatsel"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Appikoon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Volskerm"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Rekenaaraansig"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Verdeelde skerm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Meer"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Sweef"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Verander aspekverhouding"</string>
     <string name="close_text" msgid="4986518933445178928">"Maak toe"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Rekenaaraansig)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Verander grootte"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App kan nie hierheen geskuif word nie"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Verander grootte van linkerkantse venster"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Verander grootte van regterkantse venster"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maksimeer of stel venstergrootte terug"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maksimeer appvenstergrootte"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Stel venstergrootte terug"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimeer appvenster"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Maak appvenster toe"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Maak By Verstek Oop-instellings"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Kies hoe om webskakels vir hierdie app oop te maak"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In die app"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 0881e77..8bd602c 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ግራ 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ግራ 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"የቀኝ ሙሉ ማያ ገፅ"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"ከላይ ያለውን መተግበሪያ ከታች ባለው ቀይር"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"በግራ ያለውን መተግበሪያን በቀኝ ባለው ቀይር"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"የላይ ሙሉ ማያ ገፅ"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ከላይ 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ከላይ 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"የመተግበሪያ ምናሌው እዚህ መገኘት ይችላል"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"በርካታ መተግበሪያዎችን በአንድ ላይ ለመክፈት ወደ የዴስክቶፕ እይታ ይግቡ"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"በማንኛውም ጊዜ ከመተግበሪያ ምናሌው ላይ ወደ ሙሉ ገጽ እይታ ይመለሱ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ተጨማሪ ይመልከቱ እና ያድርጉ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ለተከፈለ ማያ ገፅ ሌላ መተግበሪያ ይጎትቱ"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"እንደገና ያስጀምሩ"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ዳግም አታሳይ"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"ይህን መተግበሪያ\nለማንቀሳቀስ ሁለቴ መታ ያድርጉ"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g>ን አሳድግ"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g>ን ወደነበረበት መልስ"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g>ን አሳንስ"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g>ን ዝጋ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ተመለስ"</string>
     <string name="handle_text" msgid="4419667835599523257">"የመተግበሪያ መያዣ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"የመተግበሪያ አዶ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ሙሉ ማያ"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"የዴስክቶፕ ዕይታ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"የተከፈለ ማያ ገፅ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ተጨማሪ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ተንሳፋፊ"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ምጥጥነ ገፅታ ለውጥ"</string>
     <string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> የዴስክቶፕ ዕይታ"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"መጠን ቀይር"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"መተግበሪያ ወደዚህ መንቀሳቀስ አይችልም"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"መስኮትን ወደ ግራ መጠን ቀይር"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"መስኮትን ወደ ቀኝ መጠን ቀይር"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"የመስኮት መጠንን አሳድግ ወይም ወደነበረበት መልስ"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"የመተግበሪያ መስኮት መጠንን አሳድግ"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"የመስኮት መጠንን ወደነበረበት መልስ"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"የመተግበሪያ መስኮትን አሳንስ"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"የመተግበሪያ መስኮትን ዝጋ"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"በነባሪ ቅንብሮች ክፈት"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ለዚህ የድር መተግበሪያ አገናኙን እንዴት እንደሚከፍቱ ይምረጡ"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"በመተግበሪያው ውስጥ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 9cc49aa..70a23b7 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ضبط حجم النافذة اليسرى ليكون ٥٠%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ضبط حجم النافذة اليسرى ليكون ٣٠%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"عرض النافذة اليمنى بملء الشاشة"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"تبديل التطبيق العلوي بالسفلي"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"تبديل التطبيق الأيسر بالأيمن"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"عرض النافذة العلوية بملء الشاشة"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ضبط حجم النافذة العلوية ليكون ٧٠%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ضبط حجم النافذة العلوية ليكون ٥٠%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"يمكن العثور على قائمة التطبيقات هنا"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"يمكنك الدخول إلى وضع العرض المخصّص للكمبيوتر المكتبي لفتح عدة تطبيقات معًا"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"يمكنك الرجوع إلى وضع ملء الشاشة في أي وقت من قائمة التطبيقات"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"استخدام تطبيقات متعدّدة في وقت واحد"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"اسحب تطبيقًا آخر لاستخدام وضع تقسيم الشاشة."</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"إعادة التشغيل"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"عدم عرض مربّع حوار التأكيد مجددًا"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"انقر مرّتَين لنقل\nهذا التطبيق."</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"تكبير \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"استعادة \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"تصغير \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="close_button_text" msgid="4544839489310949894">"إغلاق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="back_button_text" msgid="1469718707134137085">"رجوع"</string>
     <string name="handle_text" msgid="4419667835599523257">"مقبض التطبيق"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"رمز التطبيق"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ملء الشاشة"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"العرض المخصّص للكمبيوتر المكتبي"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"تقسيم الشاشة"</string>
     <string name="more_button_text" msgid="3655388105592893530">"المزيد"</string>
     <string name="float_button_text" msgid="9221657008391364581">"نافذة عائمة"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغيير نسبة العرض إلى الارتفاع"</string>
     <string name="close_text" msgid="4986518933445178928">"إغلاق"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> (العرض المخصّص للكمبيوتر المكتبي)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"تغيير الحجم"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"لا يمكن نقل التطبيق إلى هنا"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"تغيير حجم النافذة بمحاذاتها إلى اليمين"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"تغيير حجم النافذة بمحاذاتها إلى اليسار"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"تكبير حجم النافذة أو استعادته"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"تكبير نافذة التطبيق"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"استعادة حجم النافذة"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"تصغير نافذة التطبيق"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"إغلاق نافذة التطبيق"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"إعدادات الفتح تلقائيًا"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"اختيار طريقة فتح روابط الويب لهذا التطبيق"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"في التطبيق"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index c59753c..6872df6 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"বাওঁফালৰ স্ক্ৰীনখন ৫০% কৰক"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"বাওঁফালৰ স্ক্ৰীনখন ৩০% কৰক"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"সোঁফালৰ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"একেবাৰে তলৰ সৈতে একেবাৰে ওপৰৰ এপ্‌টো সলনাসলনি কৰক"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"সোঁফালৰ সৈতে বাওঁফালৰ এপ্‌টো সলনাসলনি কৰক"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"শীৰ্ষ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"শীর্ষ স্ক্ৰীনখন ৭০% কৰক"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"শীর্ষ স্ক্ৰীনখন ৫০% কৰক"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"এপৰ মেনু ইয়াত বিচাৰি পোৱা যাব"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"একেলগে একাধিক এপ্‌ খুলিবলৈ ডেস্কটপ ভিউলৈ যাওক"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"এপৰ মেনুৰ পৰা যিকোনো সময়তে পূৰ্ণ স্ক্ৰীনলৈ উভতি যাওক"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"চাওক আৰু অধিক কৰক"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"বিভাজিত স্ক্ৰীনৰ বাবে অন্য এটা এপ্‌ টানি আনি এৰক"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"ৰিষ্টাৰ্ট কৰক"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"পুনৰাই নেদেখুৱাব"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"এই এপ্‌টো\nস্থানান্তৰ কৰিবলৈ দুবাৰ টিপক"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> মেক্সিমাইজ কৰক"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক পুনঃস্থাপন কৰক"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> মিনিমাইজ কৰক"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> বন্ধ কৰক"</string>
     <string name="back_button_text" msgid="1469718707134137085">"উভতি যাওক"</string>
     <string name="handle_text" msgid="4419667835599523257">"এপৰ হেণ্ডেল"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"এপৰ চিহ্ন"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"সম্পূৰ্ণ স্ক্ৰীন"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ডেস্কটপ ভিউ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"বিভাজিত স্ক্ৰীন"</string>
     <string name="more_button_text" msgid="3655388105592893530">"অধিক"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ওপঙা"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"আকাৰৰ অনুপাত সলনি কৰক"</string>
     <string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ ভিউ)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"আকাৰ সলনি কৰক"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ইয়ালৈ এপ্‌টো আনিব নোৱাৰি"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"সোঁফাললৈ ৱিণ্ড’ৰ আকাৰ সলনি কৰক"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"বাওঁফাললৈ ৱিণ্ড’ৰ আকাৰ সলনি কৰক"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"ৱিণ্ড’ৰ আকাৰ মেক্সিমাইজ বা পুনঃস্থাপন কৰক"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"এপ্‌ ৱিণ্ড’ৰ আকাৰ মেক্সিমাইজ কৰক"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"ৱিণ্ড’ৰ আকাৰ পুনঃস্থাপন কৰক"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"এপ্‌ ৱিণ্ড’ মিনিমাইজ কৰক"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"এপ্‌ ৱিণ্ড’ বন্ধ কৰক"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফ’ল্ট ছেটিং খোলক"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"এই এপ্‌টোৰ বাবে কিদৰে ৱেব লিংক খুলিব পাৰি সেয়া বাছনি কৰক"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"এপ্‌টোত"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 63e610b..4630941 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Sol 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Sol 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Sağ tam ekran"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Yuxarıdakı tətbiqi aşağıdakı ilə dəyişdirin"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Soldakı tətbiqi sağdakı ilə dəyişdirin"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Yuxarı tam ekran"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Yuxarı 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yuxarı 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Tətbiq menyusunu burada tapa bilərsiniz"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Bir neçə tətbiqi birlikdə açmaq üçün masaüstü görünüşə daxil olun"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"İstənilən vaxt tətbiq menyusundan tam ekrana qayıdın"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ardını görün və edin"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Bölünmüş ekran üçün başqa tətbiq sürüşdürün"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Yenidən başladın"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Yenidən göstərməyin"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Tətbiqi köçürmək üçün\niki dəfə toxunun"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Böyüdün: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Bərpa edin: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Kiçildin: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Bağlayın: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Geriyə"</string>
     <string name="handle_text" msgid="4419667835599523257">"Tətbiq ləqəbi"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Tətbiq ikonası"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Masaüstü Görünüş"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Bölünmüş Ekran"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Ardı"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Üzən pəncərə"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tərəflər nisbətini dəyişin"</string>
     <string name="close_text" msgid="4986518933445178928">"Bağlayın"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Masaüstü Görünüş)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ölçüsünü dəyişin"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Tətbiqi bura köçürmək mümkün deyil"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Pəncərə ölçüsünü sola dəyişin"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Pəncərə ölçüsünü sağa dəyişin"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Pəncərə ölçüsünü artırın və ya bərpa edin"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Tətbiq pəncərəsi ölçüsünü böyüdün"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Pəncərə ölçüsünü bərpa edin"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Tətbiq pəncərəsini kiçildin"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Tətbiq pəncərəsini bağlayın"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Defolt ayarlarla açın"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Bu tətbiq üçün veb-linklərin necə açılacağını seçin"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Tətbiqdə"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 4ee7d7e..4af5648 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -100,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meni aplikacije možete da pronađete ovde"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Uđite u prikaz za računare da biste istovremeno otvorili više aplikacija"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vratite se na ceo ekran bilo kada iz menija aplikacije"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vidite i uradite više"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite drugu aplikaciju da biste koristili podeljeni ekran"</string>
@@ -121,7 +122,8 @@
     <string name="handle_text" msgid="4419667835599523257">"Identifikator aplikacije"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Preko celog ekrana"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Prikaz za računare"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Podeljeni ekran"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Još"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Plutajuće"</string>
@@ -134,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promeni razmeru"</string>
     <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz za računare)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Prilagodi"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija ne može da se premesti ovde"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 4f3da2b..7719396 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Левы экран – 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Левы экран – 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Правы экран – поўнаэкранны рэжым"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Памяняць месцамі верхнюю і ніжнюю праграмы"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Памяняць месцамі левую і правую праграмы"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Верхні экран – поўнаэкранны рэжым"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Верхні экран – 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Верхні экран – 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Меню праграмы шукайце тут"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Каб адкрыць некалькі праграм адначасова, увайдзіце ў версію для камп’ютараў"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вы можаце вярнуцца ў поўнаэкранны рэжым у любы час з меню праграмы"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Адначасова выконвайце розныя задачы"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Перацягніце іншую праграму, каб выкарыстоўваць падзелены экран"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Перазапусціць"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Больш не паказваць"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Каб перамясціць праграму,\nнацісніце двойчы"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Разгарнуць праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Аднавіць праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Згарнуць праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Закрыць праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
     <string name="handle_text" msgid="4419667835599523257">"Маркер праграмы"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Значок праграмы"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"На ўвесь экран"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Версія для камп’ютараў"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Падзяліць экран"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Яшчэ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Зрабіць рухомым акном"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змяніць суадносіны бакоў"</string>
     <string name="close_text" msgid="4986518933445178928">"Закрыць"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (версія для камп’ютараў)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Змяніць памер"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Нельга перамясціць сюды праграму"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Змяніць памер акна і перамясціць да левага краю"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Змяніць памер акна і перамясціць да правага краю"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Разгарнуць акно ці аднавіць яго памер"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Разгарнуць акно праграмы"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Аднавіць памер акна"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Згарнуць акно праграмы"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Закрыць акно праграмы"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Налады параметра \"Адкрываць стандартна\""</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Выберыце, як гэта праграма будзе адкрываць вэб-спасылкі"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У праграме"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 3f867a22..514556e 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ляв екран: 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ляв екран: 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Десен екран: Показване на цял екран"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Размяна на горното и долното приложение"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Размяна на лявото и дясното приложение"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Горен екран: Показване на цял екран"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Горен екран: 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Горен екран: 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Можете да намерите менюто на приложението тук"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Активирайте изгледа за настолни компютри, за да отворите няколко приложения едновременно"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Преминете към цял екран по всяко време от менюто на приложението"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Преглеждайте и правете повече неща"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Преместете друго приложение с плъзгане, за да преминете в режим за разделен екран"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Рестартиране"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Да не се показва отново"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Докоснете двукратно, за да\nпреместите това приложение"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Увеличаване на <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Възстановяване на <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Намаляване на <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Затваряне на <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
     <string name="handle_text" msgid="4419667835599523257">"Манипулатор за приложението"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Икона на приложението"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Цял екран"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Изглед за настолни компютри"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Разделяне на екрана"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Още"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Плаващо"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промяна на съотношението"</string>
     <string name="close_text" msgid="4986518933445178928">"Затваряне"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (изглед за настолни компютри)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Нов размер"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложението не може да бъде преместено тук"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Преоразмеряване на прозореца наляво"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Преоразмеряване на прозореца надясно"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Увеличаване или възстановяване на размера на прозореца"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Увеличаване на размера на прозореца на приложението"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Възстановяване на размера на прозореца"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Намаляване на прозореца на приложението"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Затваряне на прозореца на приложението"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Отваряне на настройките по подразбиране"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Изберете как да се отварят уеб връзките за това приложение"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"В приложението"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 3967d4b..d4488a2 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"৫০% বাকি আছে"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"৩০% বাকি আছে"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ডান দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"নিচেরটির মাধ্যমে উপরের অ্যাপ অদল বদল করে নিন"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"ডানদিকের মাধ্যমে বাঁদিকের অ্যাপ অদল বদল করে নিন"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"উপর দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"শীর্ষ ৭০%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"শীর্ষ ৫০%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"অ্যাপ মেনু এখানে খুঁজে পাওয়া যাবে"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"একসাথে একাধিক অ্যাপ খোলার জন্য ডেস্কটপ ভিউতে যান"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"অ্যাপ মেনু থেকে ফুল-স্ক্রিন মোডে যেকোনও সময়ে ফিরে আসুন"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"দেখুন ও আরও অনেক কিছু করুন"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"স্প্লিট স্ক্রিনের ক্ষেত্রে অন্য কোনও অ্যাপ টেনে আনুন"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"রিস্টার্ট করুন"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"আর দেখতে চাই না"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"এই অ্যাপ সরাতে\nডবল ট্যাপ করুন"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> বড় করুন"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> আবার ফিরিয়ে আনুন"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> ছোট করুন"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> বন্ধ করুন"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ফিরে যান"</string>
     <string name="handle_text" msgid="4419667835599523257">"অ্যাপের হ্যান্ডেল"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"অ্যাপ আইকন"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ফুলস্ক্রিন"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ডেস্কটপ ভিউ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"স্প্লিট স্ক্রিন"</string>
     <string name="more_button_text" msgid="3655388105592893530">"আরও"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ফ্লোট"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
     <string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ ভিউ)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ছোট বড় করুন"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"অ্যাপটি এখানে সরানো যাবে না"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"বাঁদিকে উইন্ডো রিসাইজ করুন"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"ডানদিকে উইন্ডো রিসাইজ করুন"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"উইন্ডো সাইজ বড় বা রিস্টোর করুন"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"অ্যাপ উইন্ডোর সাইজ বাড়ান"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"উইন্ডোর সাইজ ফিরিয়ে আনুন"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"অ্যাপ উইন্ডো ছোট করুন"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"অ্যাপ উইন্ডো বন্ধ করুন"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফল্ট হিসেবে থাকা সেটিংস খুলুন"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"এই অ্যাপের জন্য কীভাবে ওয়েব লিঙ্ক খুলবেন তা বেছে নিন"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"অ্যাপের মধ্যে"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 6b59d91..23c467c 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Lijevo 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Lijevo 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Desno cijeli ekran"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Zamjena gornje aplikacije donjom"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Zamjena lijeve aplikacije desnom"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Gore cijeli ekran"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gore 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gore 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ovdje možete pronaći meni aplikacije"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Ulazak u prikaz na računaru radi istovremenog otvaranja više aplikacija"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Povratak na prikaz preko cijelog ekrana bilo kada putem menija aplikacije"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Pogledajte i učinite više"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite još jednu aplikaciju za podijeljeni ekran"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Ponovo pokreni"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne prikazuj ponovo"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Dodirnite dvaput da\npomjerite aplikaciju"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maksimiziranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Vraćanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimiziranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Zatvaranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Nazad"</string>
     <string name="handle_text" msgid="4419667835599523257">"Ručica aplikacije"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Cijeli ekran"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Prikaz na računaru"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Podijeljeni ekran"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Više"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Lebdeći"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promjena formata slike"</string>
     <string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz na računaru)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Promijeni veličinu"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ne možete premjestiti aplikaciju ovdje"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Promjena veličine prozora i poravnanje lijevo"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Promjena veličine prozora i poravnanje desno"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maksimiziranje ili vraćanje veličine prozora"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maksimiziranje veličine prozora aplikacije"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Vraćanje veličine prozora"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimiziranje prozora aplikacije"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Zatvaranje prozora aplikacije"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvaranje prema zadanim postavkama"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Odaberite način otvaranja web linkova za ovu aplikaciju"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 955e5cc..893d16e 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Pantalla esquerra al 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Pantalla esquerra al 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla dreta completa"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Intercanvia l\'aplicació superior amb la inferior"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Intercanvia l\'aplicació esquerra amb la dreta"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla superior completa"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Pantalla superior al 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Pantalla superior al 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Pots trobar el menú de l\'aplicació aquí"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Accedeix a la visualització per a ordinadors per obrir diverses aplicacions alhora"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Torna a la pantalla completa en qualsevol moment des del menú de l\'aplicació"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta i fes més coses"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrossega una altra aplicació per utilitzar la pantalla dividida"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reinicia"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"No ho tornis a mostrar"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Fes doble toc per\nmoure aquesta aplicació"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximitza <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restaura <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimitza <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Tanca <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Enrere"</string>
     <string name="handle_text" msgid="4419667835599523257">"Identificador de l\'aplicació"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icona de l\'aplicació"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Visualització per a ordinadors"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Més"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flotant"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Canvia la relació d\'aspecte"</string>
     <string name="close_text" msgid="4986518933445178928">"Tanca"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (visualització per a ordinadors)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Canvia la mida"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"L\'aplicació no es pot moure aquí"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Canvia la mida de la finestra a l\'esquerra"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Canvia la mida de la finestra a la dreta"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximitza o restaura la mida de la finestra"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximitza la mida de la finestra de l\'aplicació"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restaura la mida de la finestra"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimitza la finestra de l\'aplicació"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Tanca la finestra de l\'aplicació"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configuració d\'obertura predeterminada"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Tria com vols obrir els enllaços web per a aquesta aplicació"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"A l\'aplicació"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 673f7fc..a96344a 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % vlevo"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % vlevo"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pravá část na celou obrazovku"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Prohodit horní a dolní aplikaci"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Prohodit levou a pravou aplikaci"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Horní část na celou obrazovku"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % nahoře"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % nahoře"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Najdete tu nabídku aplikace"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Pokud chcete otevřít několik aplikací současně, přejděte na zobrazení na počítači"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Na celou obrazovku se můžete kdykoli vrátit z nabídky aplikace"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lepší zobrazení a více možností"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Přetáhnutím druhé aplikace použijete rozdělenou obrazovku"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restartovat"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Tuto zprávu příště nezobrazovat"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Dvojitým klepnutím\npřesunete aplikaci"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximalizovat aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Obnovit aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimalizovat aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Zavřít aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Zpět"</string>
     <string name="handle_text" msgid="4419667835599523257">"Popisovač aplikace"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikace"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Zobrazení na počítači"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Rozdělená obrazovka"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Více"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Plovoucí"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Změnit poměr stran"</string>
     <string name="close_text" msgid="4986518933445178928">"Zavřít"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (zobrazení na počítači)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Změnit velikost"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikaci sem nelze přesunout"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Přichytit okno vlevo"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Přichytit okno vpravo"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximalizovat nebo obnovit velikost okna"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximalizovat velikost okna aplikace"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Obnovit velikost okna"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimalizovat okno aplikace"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Zavřít okno aplikace"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otevírat podle výchozího nastavení"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Určete, jak se v této aplikaci mají otevírat webové odkazy"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikaci"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 635df33..7d28fc8 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Venstre 50 %"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Venstre 30 %"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Vis højre del i fuld skærm"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Byt om på den øverste og nederste app"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Byt om på den venstre og højre app"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Vis øverste del i fuld skærm"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Øverste 70 %"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Øverste 50 %"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Appmenuen kan findes her"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Gå til computervenlig visning for at åbne flere apps på én gang"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Gå tilbage til fuld skærm når som helst via appmenuen"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gør mere"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Træk en anden app hertil for at bruge opdelt skærm"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Genstart"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Vis ikke igen"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Tryk to gange\nfor at flytte appen"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maksimer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Gendan <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Luk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tilbage"</string>
     <string name="handle_text" msgid="4419667835599523257">"Apphåndtag"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Fuld skærm"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Computervenlig visning"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Opdelt skærm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mere"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Svævende"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Skift billedformat"</string>
     <string name="close_text" msgid="4986518933445178928">"Luk"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (computervenlig visning)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Tilpas størrelse"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apps kan ikke flyttes hertil"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Juster størrelsen på vinduet til venstre"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Juster størrelsen på vinduet til højre"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maksimer eller gendan vinduesstørrelse"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maksimer størrelsen på appvinduet"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Gendan størrelsen på vinduet"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimer appvindue"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Luk appvindue"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Indstillinger for automatisk åbning"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Vælg, hvordan denne app skal åben weblinks"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 5be8b0b..4cc1124 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % links"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % links"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Vollbild rechts"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Obere und untere App vertauschen"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Linke und rechte App vertauschen"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Vollbild oben"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % oben"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % oben"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Das App-Menü findest du hier"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Über die Desktop-Ansicht kannst du mehrere Apps gleichzeitig öffnen"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Über das App-Menü kannst du jederzeit zum Vollbildmodus zurückkehren"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Mehr sehen und erledigen"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Für Splitscreen-Modus weitere App hineinziehen"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Neu starten"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Nicht mehr anzeigen"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Zum Verschieben\ndoppeltippen"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> maximieren"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> wiederherstellen"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> minimieren"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> schließen"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Zurück"</string>
     <string name="handle_text" msgid="4419667835599523257">"App-Ziehpunkt"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App-Symbol"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Vollbild"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Desktop-Ansicht"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Splitscreen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mehr"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Frei schwebend"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Seitenverhältnis ändern"</string>
     <string name="close_text" msgid="4986518933445178928">"Schließen"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop-Ansicht)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Größe ändern"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Die App kann nicht hierher verschoben werden"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Fenstergröße nach links anpassen"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Fenstergröße nach rechts anpassen"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Fenstergröße maximieren oder wiederherstellen"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"App-Fenster maximieren"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Fenstergröße wiederherstellen"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"App-Fenster minimieren"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"App-Fenster schließen"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Einstellungen für die Option „Standardmäßig öffnen“"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Festlegen, wie Weblinks für diese App geöffnet werden"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In der App"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index bd3cf05..0fb17ec 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Αριστερή 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Αριστερή 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Δεξιά πλήρης οθόνη"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Εναλλαγή της εφαρμογής στην κορυφή με αυτή στο κάτω μέρος"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Εναλλαγή της εφαρμογής στα αριστερά με αυτή στα δεξιά"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Πάνω πλήρης οθόνη"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Πάνω 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Πάνω 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Μπορείτε να βρείτε το μενού εφαρμογών εδώ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Μεταβείτε στην προβολή για υπολογιστές, για να ανοίξετε πολλές εφαρμογές μαζί"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Επιστρέψτε στην πλήρη οθόνη ανά πάσα στιγμή από το μενού της εφαρμογής"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Δείτε και κάντε περισσότερα"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Σύρετε σε μια άλλη εφαρμογή για διαχωρισμό οθόνης."</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Επανεκκίνηση"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Να μην εμφανιστεί ξανά"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Πατήστε δύο φορές για\nμετακίνηση αυτής της εφαρμογής"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Μεγιστοποίηση <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Επαναφορά <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Ελαχιστοποίηση <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Κλείσιμο <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Πίσω"</string>
     <string name="handle_text" msgid="4419667835599523257">"Λαβή εφαρμογής"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Εικονίδιο εφαρμογής"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Πλήρης οθόνη"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Προβολή για υπολογιστές"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Διαχωρισμός οθόνης"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Περισσότερα"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Κινούμενο"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Αλλαγή λόγου διαστάσεων"</string>
     <string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Προβολή για υπολογιστές)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Αλλαγή μεγέθους"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Δεν είναι δυνατή η μετακίνηση της εφαρμογής εδώ"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Αλλαγή μεγέθους παραθύρου προς τα αριστερά"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Αλλαγή μεγέθους παραθύρου προς τα δεξιά"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Μεγιστοποίηση ή επαναφορά μεγέθους παραθύρου"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Μεγιστοποίηση μεγέθους παραθύρου εφαρμογής"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Επαναφορά μεγέθους παραθύρου"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Ελαχιστοποίηση παραθύρου εφαρμογής"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Κλείσιμο παραθύρου εφαρμογής"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Άνοιγμα ρυθμίσεων από προεπιλογή"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Επιλογή τρόπου ανοίγματος συνδέσμων ιστού για την εφαρμογή"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Στην εφαρμογή"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index b137d80..2087bb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Swap top app with bottom"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Swap left app with right"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restart"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Don\'t show again"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Double-tap to\nmove this app"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximise <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restore <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimise <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Close <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
     <string name="handle_text" msgid="4419667835599523257">"App handle"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Desktop view"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"More"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktop view)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Resize window to left"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Resize window to right"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximise or restore window size"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximise app window size"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restore window size"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimise app window"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Close app window"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 9b9294d..0802f83 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -100,7 +100,7 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Enter desktop windowing to open multiple apps together"</string>
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen anytime from the app menu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
@@ -121,7 +121,7 @@
     <string name="handle_text" msgid="4419667835599523257">"App handle"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App Icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Desktop View"</string>
+    <string name="desktop_text" msgid="9058641752519570266">"Desktop windowing"</string>
     <string name="split_screen_text" msgid="1396336058129570886">"Split Screen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"More"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
@@ -134,7 +134,7 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop View)"</string>
+    <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximize Screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index b137d80..2087bb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Swap top app with bottom"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Swap left app with right"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restart"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Don\'t show again"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Double-tap to\nmove this app"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximise <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restore <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimise <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Close <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
     <string name="handle_text" msgid="4419667835599523257">"App handle"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Desktop view"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"More"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktop view)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Resize window to left"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Resize window to right"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximise or restore window size"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximise app window size"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restore window size"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimise app window"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Close app window"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index b137d80..2087bb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Swap top app with bottom"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Swap left app with right"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Enter desktop view to open multiple apps together"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restart"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Don\'t show again"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Double-tap to\nmove this app"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximise <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restore <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimise <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Close <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Back"</string>
     <string name="handle_text" msgid="4419667835599523257">"App handle"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Desktop view"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"More"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktop view)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Resize window to left"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Resize window to right"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximise or restore window size"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximise app window size"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restore window size"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimise app window"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Close app window"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 1d5d385..bb70f6e 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Izquierda: 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Izquierda: 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla derecha completa"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Intercambiar la app superior con la inferior"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Intercambiar la app de la izquierda con la de la derecha"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla superior completa"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Superior: 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior: 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"El menú de la app se encuentra aquí"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entra a la vista de escritorio para abrir varias apps a la vez"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Regresa a pantalla completa en cualquier momento desde el menú de la app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Aprovecha más"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra otra app para el modo de pantalla dividida"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"No volver a mostrar"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Presiona dos veces\npara mover esta app"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restablecer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Cerrar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
     <string name="handle_text" msgid="4419667835599523257">"Controlador de la app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ícono de la app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Vista para computadoras de escritorio"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Más"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
     <string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (vista para computadoras de escritorio)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Dividir pantalla"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"No se puede mover la app aquí"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Ajustar el tamaño de la ventana hacia la izquierda"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Ajustar el tamaño de la ventana hacia la derecha"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximizar o restablecer el tamaño de la ventana"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximizar el tamaño de la ventana de la app"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restablecer el tamaño de la ventana"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimizar ventana de la app"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Cerrar ventana de la app"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir con la configuración predeterminada"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Elige cómo abrir vínculos web para esta app"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"En la app"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index dfa7869..a6595aa 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Izquierda 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Izquierda 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla derecha completa"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Intercambiar aplicación superior con inferior"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Intercambiar aplicación izquierda con derecha"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla superior completa"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Superior 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"El menú de la aplicación se encuentra aquí"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entra en la vista para ordenador si quieres abrir varias aplicaciones a la vez"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vuelve a la pantalla completa en cualquier momento desde el menú de aplicaciones"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta más información y haz más"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra otra aplicación para activar la pantalla dividida"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"No volver a mostrar"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Toca dos veces para\nmover esta aplicación"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restaurar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Cerrar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
     <string name="handle_text" msgid="4419667835599523257">"Controlador de la aplicación"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icono de la aplicación"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Vista para ordenadores"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Más"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
     <string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (vista para ordenadores)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Dividir pantalla"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"La aplicación no se puede mover aquí"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Cambiar tamaño de la ventana a la izquierda"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Cambiar tamaño de la ventana a la derecha"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximizar o restaurar tamaño de la ventana"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximizar tamaño de la ventana de la aplicación"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restaurar tamaño de la ventana"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimizar ventana de la aplicación"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Cerrar ventana de la aplicación"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir con los ajustes predeterminados"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Elige cómo quieres abrir los enlaces web de esta aplicación"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"En la aplicación"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 294b7fe..3608657 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vasak: 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vasak: 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Parem täisekraan"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Ülemise ja alumise rakenduse vahetamine"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Vasaku ja parema rakenduse vahetamine"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ülemine täisekraan"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Ülemine: 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Ülemine: 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Rakenduse menüü leiate siit"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Mitme rakenduse koos avamiseks avage arvutivaade"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Saate rakenduse menüüst igal ajal täisekraanile naasta"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vaadake ja tehke rohkem"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Lohistage muusse rakendusse, et jagatud ekraanikuva kasutada"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Taaskäivita"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ära kuva uuesti"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Rakenduse teisaldamiseks\ntopeltpuudutage"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> maksimeerimine"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> taastamine"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> minimeerimine"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> sulgemine"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tagasi"</string>
     <string name="handle_text" msgid="4419667835599523257">"Rakenduse element"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Rakenduse ikoon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Täisekraan"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Arvutivaade"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Jagatud ekraanikuva"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Rohkem"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Hõljuv"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Kuvasuhte muutmine"</string>
     <string name="close_text" msgid="4986518933445178928">"Sule"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (arvutivaade)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Suuruse muutmine"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Rakendust ei saa siia teisaldada"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Akna suuruse muutmine, vasakule"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Akna suuruse muutmine, paremale"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Akna suuruse maksimeerimine või taastamine"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Rakenduse akna suuruse maksimeerimine"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Akna suuruse taastamined"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Rakenduse akna minimeerimine"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Rakenduse akna sulgemine"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Avamisviisi vaikeseaded"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Valige, kuidas avada selle rakenduse puhul veebilinke"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Rakenduses"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index a8f9212..446839a 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ezarri ezkerraldea % 50en"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ezarri ezkerraldea % 30en"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Ezarri eskuinaldea pantaila osoan"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Aldatu goiko aplikazioa behekoagatik"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Aldatu ezkerreko aplikazioa eskuinekoagatik"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ezarri goialdea pantaila osoan"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Ezarri goialdea % 70en"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Ezarri goialdea % 50en"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Aplikazioaren menua dago hemen"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Sartu ordenagailuetarako ikuspegian aplikazio bat baino gehiago batera irekitzeko"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Pantaila osoko modura itzultzeko, erabili aplikazioaren menua"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ikusi eta egin gauza gehiago"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Pantaila zatitua ikusteko, arrastatu beste aplikazio bat"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Berrabiarazi"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ez erakutsi berriro"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Sakatu birritan\naplikazioa mugitzeko"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximizatu <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Leheneratu <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimizatu <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Itxi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atzera"</string>
     <string name="handle_text" msgid="4419667835599523257">"Aplikazioaren kontrol-puntua"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Aplikazioaren ikonoa"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantaila osoa"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Ordenagailuetarako ikuspegia"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Pantaila zatitzea"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Gehiago"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Leiho gainerakorra"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Aldatu aspektu-erlazioa"</string>
     <string name="close_text" msgid="4986518933445178928">"Itxi"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ordenagailuetarako ikuspegia)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Aldatu tamaina"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikazioa ezin da hona ekarri"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Aldatu leihoaren tamaina eta eraman ezkerrera"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Aldatu leihoaren tamaina eta eraman eskuinera"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximizatu edo leheneratu leihoaren tamaina"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximizatu aplikazioaren leihoaren tamaina"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Leheneratu leihoaren tamaina"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimizatu aplikazioaren leihoa"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Itxi aplikazioaren leihoa"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Modu lehenetsian irekitzearen ezarpenak"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Aukeratu nola ireki sareko estekak aplikazio honetan"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Aplikazioan"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 59affd7..bf5c8f9 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"٪۵۰ چپ"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"٪۳۰ چپ"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"تمام‌صفحه راست"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"جابه‌جا کردن برنامه بالا با پایین"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"جابه‌جا کردن برنامه چپ با راست"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"تمام‌صفحه بالا"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"٪۷۰ بالا"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"٪۵۰ بالا"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن تک‌ضرب بزنید"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن تک‌ضرب بزنید."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"منو برنامه را می‌توانید اینجا ببینید"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"برای باز کردن هم‌زمان چند برنامه، وارد نمای ویژه رایانه شوید"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"هروقت خواستید از منو برنامه به حالت تمام‌صفحه برگردید"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"از چندین برنامه به‌طور هم‌زمان استفاده کنید"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"برای حالت صفحهٔ دونیمه، در برنامه‌ای دیگر بکشید"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"بازراه‌اندازی"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"دوباره نشان داده نشود"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"برای جابه‌جا کردن این برنامه\nدو تک‌ضرب بزنید"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"بزرگ کردن <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"بازیابی <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"کوچک کردن <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"بستن <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"برگشتن"</string>
     <string name="handle_text" msgid="4419667835599523257">"دستگیره برنامه"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"نماد برنامه"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"تمام‌صفحه"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"نمای ویژه رایانه رومیزی"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"صفحهٔ دونیمه"</string>
     <string name="more_button_text" msgid="3655388105592893530">"بیشتر"</string>
     <string name="float_button_text" msgid="9221657008391364581">"شناور"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغییر نسبت ابعادی"</string>
     <string name="close_text" msgid="4986518933445178928">"بستن"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> (نمای ویژه رایانه رومیزی)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"تغییر اندازه"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"برنامه را نمی‌توان به اینجا منتقل کرد"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"تغییر اندازه پنجره به چپ"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"تغییر اندازه پنجره به راست"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"بیشینه‌سازی یا بازیابی اندازه پنجره"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"بزرگ کردن اندازه پنجره برنامه"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"بازیابی اندازه پنجره"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"کوچک کردن پنجره برنامه"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"بستن پنجره برنامه"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"تنظیمات باز کردن به‌طور پیش‌فرض"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"انتخاب روش باز کردن پیوندهای وب مربوط به این برنامه"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"در برنامه"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index b1d8431..a32e4fa 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vasen 50 %"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vasen 30 %"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Oikea koko näytölle"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Vaihda ylä- ja alareunan sovellukset"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Vaihda vasen sovellus oikealla olevaan sovellukseen"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Yläosa koko näytölle"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Yläosa 70 %"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yläosa 50 %"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Sovellusvalikko löytyy täältä"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Siirry työpöytänäkymään, niin voit avata useita sovelluksia kerralla"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Voit palata koko näytön tilaan milloin tahansa sovellusvalikosta"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Näe ja tee enemmän"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Käytä jaettua näyttöä vetämällä tähän toinen sovellus"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Käynnistä uudelleen"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Älä näytä uudelleen"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Kaksoisnapauta, jos\nhaluat siirtää sovellusta"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Suurenna <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Palauta <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Pienennä <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Sulje <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Takaisin"</string>
     <string name="handle_text" msgid="4419667835599523257">"Sovelluksen tunnus"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Sovelluskuvake"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Koko näyttö"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Tietokonenäkymä"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Jaettu näyttö"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Lisää"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Kelluva ikkuna"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Vaihda kuvasuhdetta"</string>
     <string name="close_text" msgid="4986518933445178928">"Sulje"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (tietokonenäkymä)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Suurenna näyttö"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Muuta kokoa"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Sovellusta ei voi siirtää tänne"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Muuta vasemmanpuoleisen ikkunan kokoa"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Muuta vasemmanpuoleisen ikkunan kokoa"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Suurenna ikkuna tai palauta ikkunan koko"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Suurenna sovellusikkunan koko"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Palauta ikkunan koko"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Pienennä sovellusikkuna"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Sulje sovellusikkuna"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Avaa oletusasetusten mukaan"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Valitse, miten verkkolinkit avataan tässä sovelluksessa"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Sovelluksessa"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index c58f4b0..7037377 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % à la gauche"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % à la gauche"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Plein écran à la droite"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Inverser l\'appli du haut avec celle du bas"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Inverser l\'appli de gauche avec celle de droite"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Plein écran dans le haut"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % dans le haut"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % dans le haut"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Le menu de l\'appli se trouve ici"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Accéder à l\'affichage sur un ordinateur de bureau pour ouvrir plusieurs applis simultanément"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revenir au mode Plein écran à tout moment à partir du menu de l\'appli"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et en faire plus"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Redémarrer"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne plus afficher"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Toucher deux fois pour\ndéplacer cette appli"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Agrandir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restaurer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Réduire <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Fermer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Retour"</string>
     <string name="handle_text" msgid="4419667835599523257">"Poignée de l\'appli"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icône de l\'appli"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Affichage sur un ordinateur de bureau"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Écran divisé"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Plus"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flottant"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier les proportions"</string>
     <string name="close_text" msgid="4986518933445178928">"Fermer"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (affichage sur un ordinateur de bureau)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionner"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Redimensionner la fenêtre vers la gauche"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Redimensionner la fenêtre vers la droite"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Agrandir ou restaurer la taille de la fenêtre"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Agrandir la taille de la fenêtre de l\'appli"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restaurer la taille de la fenêtre"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Réduire la fenêtre de l\'appli"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Fermer la fenêtre de l\'appli"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ouvrir les paramètres par défaut"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choisissez comment ouvrir les liens Web pour cette appli"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Dans l\'appli"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index e8db4c9..fb2ebfd 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Écran de gauche à 50 %"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Écran de gauche à 30 %"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Écran de droite en plein écran"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Inverser les applis du haut et du bas"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Inverser les applis de gauche et de droite"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Écran du haut en plein écran"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Écran du haut à 70 %"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Écran du haut à 50 %"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Le menu de l\'application se trouve ici"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Passer à l\'affichage sur ordinateur pour ouvrir plusieurs applications simultanément"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revenir en plein écran à tout moment depuis le menu de l\'application"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et interagir plus"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Redémarrer"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne plus afficher"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Appuyez deux fois\npour déplacer cette appli"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Agrandir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restaurer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Réduire <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Fermer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Retour"</string>
     <string name="handle_text" msgid="4419667835599523257">"Poignée de l\'appli"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icône d\'application"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Affichage sur ordinateur"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Écran partagé"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Plus"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flottante"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier le format"</string>
     <string name="close_text" msgid="4986518933445178928">"Fermer"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (affichage sur ordinateur)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionner"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Redimensionner la fenêtre vers la gauche"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Redimensionner la fenêtre vers la droite"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Agrandir ou restaurer la taille de la fenêtre"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Agrandir la taille de la fenêtre de l\'application"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restaurer la taille de la fenêtre"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Réduire la fenêtre de l\'application"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Fermer la fenêtre de l\'application"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ouvrir les paramètres par défaut"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choisir comment ouvrir les liens Web pour cette appli"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Dans l\'application"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index ba1c4a4..895cf47 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % á esquerda"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % á esquerda"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla completa á dereita"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Cambiar a aplicación de arriba pola de abaixo"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Cambiar a aplicación da esquerda pola da dereita"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla completa arriba"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % arriba"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % arriba"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Aquí podes ver o menú da aplicación"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Vai á vista para ordenadores se queres abrir varias aplicacións á vez"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volve á pantalla completa en calquera momento desde o menú da aplicación"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ver e facer máis"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra outra aplicación para usar a pantalla dividida"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Non mostrar outra vez"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Toca dúas veces para\nmover esta aplicación"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restaurar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Pechar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atrás"</string>
     <string name="handle_text" msgid="4419667835599523257">"Controlador da aplicación"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icona de aplicación"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Vista para ordenadores"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Máis"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar a proporción"</string>
     <string name="close_text" msgid="4986518933445178928">"Pechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (vista para ordenadores)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Cambiar tamaño"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Non se pode mover aquí a aplicación"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Axustar o tamaño da ventá á esquerda"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Axustar o tamaño da ventá á dereita"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximizar ou restaurar o tamaño da ventá"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximizar o tamaño da ventá da aplicación"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restaurar o tamaño da ventá"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimizar a ventá da aplicación"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Pechar a ventá da aplicación"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir coa configuración predeterminada"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escoller como abrir as ligazóns web para esta aplicación"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Na aplicación"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index cd75508..9c8cf96 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -102,7 +102,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ઍપ મેનૂ અહીં જોવા મળી શકે છે"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"એકથી વધુ ઍપ એકસાથે ખોલવા માટે ડેસ્કટૉપ વ્યૂ દાખલ કરો"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ઍપ મેનૂમાંથી કોઈપણ સમયે પૂર્ણ સ્ક્રીન પર પાછા ફરો"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"જુઓ અને બીજું ઘણું કરો"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"વિભાજિત સ્ક્રીન માટે કોઈ અન્ય ઍપમાં ખેંચો"</string>
@@ -127,7 +128,8 @@
     <string name="handle_text" msgid="4419667835599523257">"ઍપનું હૅન્ડલ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ઍપનું આઇકન"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"પૂર્ણસ્ક્રીન"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ડેસ્કટૉપ વ્યૂ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"સ્ક્રીનને વિભાજિત કરો"</string>
     <string name="more_button_text" msgid="3655388105592893530">"વધુ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ફ્લોટિંગ વિન્ડો"</string>
@@ -140,7 +142,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"સાપેક્ષ ગુણોત્તર બદલો"</string>
     <string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ડેસ્કટૉપ વ્યૂ)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"કદ બદલો"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ઍપ અહીં ખસેડી શકાતી નથી"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index d0be7d5..985daff 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"बाईं स्क्रीन को 50% बनाएं"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"बाईं स्क्रीन को 30% बनाएं"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"दाईं स्क्रीन को फ़ुल स्क्रीन बनाएं"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"सबसे ऊपर मौजूद ऐप्लिकेशन को सबसे नीचे मौजूद ऐप्लिकेशन से बदलें"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"बाईं ओर मौजूद ऐप्लिकेशन को दाईं ओर मौजूद ऐप्लिकेशन से बदलें"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ऊपर की स्क्रीन को फ़ुल स्क्रीन बनाएं"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ऊपर की स्क्रीन को 70% बनाएं"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ऊपर की स्क्रीन को 50% बनाएं"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ऐप्लिकेशन मेन्यू यहां पाया जा सकता है"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"एक साथ कई ऐप्लिकेशन खोलने के लिए, डेस्कटॉप व्यू में जाएं"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ऐप्लिकेशन मेन्यू से फ़ुल स्क्रीन मोड पर किसी भी समय वापस जाएं"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पूरी जानकारी लेकर, बेहतर तरीके से काम करें"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रीन का इस्तेमाल करने के लिए, किसी अन्य ऐप्लिकेशन को खींचें और छोड़ें"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"रीस्टार्ट करें"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"फिर से न दिखाएं"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"ऐप्लिकेशन की जगह बदलने के लिए\nदो बार टैप करें"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> की विंडो को बड़ा करें"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> की विंडो को पहले जैसा करें"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> की विंडो को छोटा करें"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> को बंद करें"</string>
     <string name="back_button_text" msgid="1469718707134137085">"वापस जाएं"</string>
     <string name="handle_text" msgid="4419667835599523257">"ऐप्लिकेशन का हैंडल"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ऐप्लिकेशन आइकॉन"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"फ़ुलस्क्रीन"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"डेस्कटॉप व्यू"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"स्प्लिट स्क्रीन मोड"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ज़्यादा देखें"</string>
     <string name="float_button_text" msgid="9221657008391364581">"फ़्लोट"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
     <string name="close_text" msgid="4986518933445178928">"बंद करें"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप व्यू)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"साइज़ बदलें"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ऐप्लिकेशन को यहां मूव नहीं किया जा सकता"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"विंडो का साइज़ बाईं ओर से बदलें"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"विंडो का साइज़ दाईं ओर से बढ़ाएं"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"विंडो को बड़ा करें या उसका साइज़ पहले जैसा करें"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"ऐप्लिकेशन की विंडो का साइज़ बड़ा करें"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"विंडो का साइज़ पहले जैसा करें"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"ऐप्लिकेशन की विंडो को छोटा करें"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"ऐप्लिकेशन की विंडो बंद करें"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"डिफ़ॉल्ट सेटिंग के हिसाब से खोलें"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"इस ऐप्लिकेशन के लिए वेब लिंक खोलने का तरीका चुनें"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ऐप्लिकेशन में"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index d3e7599..4640bf3 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Lijevi zaslon na 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Lijevi zaslon na 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Desni zaslon u cijeli zaslon"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Zamijeni gornju aplikaciju donjom"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Zamijeni lijevu aplikaciju desnom"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Gornji zaslon u cijeli zaslon"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gornji zaslon na 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gornji zaslon na 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Izbornik aplikacije možete pronaći ovdje"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Otvorite prikaz na računalu da biste otvorili više aplikacija zajedno"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vratite se na cijeli zaslon bilo kad iz izbornika aplikacije"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Gledajte i učinite više"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Povucite drugu aplikaciju unutra da biste podijelili zaslon"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Pokreni ponovno"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne prikazuj ponovno"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Dvaput dodirnite da biste\npremjestili ovu aplikaciju"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maksimiziraj aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Vrati aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimiziraj aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Zatvori aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Natrag"</string>
     <string name="handle_text" msgid="4419667835599523257">"Pokazivač aplikacije"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Puni zaslon"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Prikaz na računalu"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Razdvojeni zaslon"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Više"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Plutajući"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promijeni omjer slike"</string>
     <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz na računalu)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Promijeni veličinu"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija se ne može premjestiti ovdje"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Promijeni veličinu prozora ulijevo"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Promijeni veličinu prozora udesno"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maksimiziraj ili vrati veličinu prozora"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maksimiziraj prozor aplikacije"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Vrati veličinu prozora"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimiziraj prozor aplikacije"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Zatvori prozor aplikacije"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvori prema zadanim postavkama"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Odaberite način otvaranja web-veza za ovu aplikaciju"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 2f7a218..711e2f0 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Bal oldali 50%-ra"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Bal oldali 30%-ra"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Jobb oldali teljes képernyőre"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"A felső alkalmazás és az alsó alkalmazás felcserélése"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Bal és jobb alkalmazás felcserélése"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Felső teljes képernyőre"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Felső 70%-ra"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Felső 50%-ra"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Az alkalmazásmenü itt található"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Asztali nézetbe lépve több alkalmazást nyithat meg egyidejűleg"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Az alkalmazásmenüből bármikor visszatérhet a teljes képernyőre"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Több mindent láthat és tehet"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Húzzon ide egy másik alkalmazást az osztott képernyő használatához"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Újraindítás"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne jelenjen meg többé"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Koppintson duplán\naz alkalmazás áthelyezéséhez"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> teljes méretűre állítása"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> visszaállítása"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> kis méretűre állítása"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> bezárása"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Vissza"</string>
     <string name="handle_text" msgid="4419667835599523257">"App fogópontja"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Alkalmazásikon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Teljes képernyő"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Asztali nézet"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Osztott képernyő"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Továbbiak"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Lebegő"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Méretarány módosítása"</string>
     <string name="close_text" msgid="4986518933445178928">"Bezárás"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Asztali nézet)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Átméretezés"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Az alkalmazás nem helyezhető át ide"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Ablak átméretezése balra"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Ablak átméretezése jobbra"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Ablak teljes méretre állítása vagy visszaállítása"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Alkalmazásablak teljes méretre állítása"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Ablak méretének visszaállítása"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Alkalmazásablak kis méretre állítása"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Alkalmazás ablakának bezárása"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Alapértelmezett beállítások megnyitása"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Az app webes linkjeinek megnyitásához használt módszer"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Az alkalmazásban"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 2898dcc..d7b1b07 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ձախ էկրանը՝ 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ձախ էկրանը՝ 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Աջ էկրանը՝ լիաէկրան"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Փոխեք վերևի հավելվածը վերևից"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Փոխեք ձախ հավելվածը աջից"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Վերևի էկրանը՝ լիաէկրան"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Վերևի էկրանը՝ 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Վերևի էկրանը՝ 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Հավելվածի ընտրացանկն այստեղ է"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Անցեք համակարգչային տարբերակին՝ միաժամանակ մի քանի հավելված բացելու համար"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ցանկացած ժամանակ հավելվածի ընտրացանկից վերադարձեք լիաէկրան ռեժիմ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Միաժամանակ կատարեք մի քանի առաջադրանք"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Քաշեք մյուս հավելվածի մեջ՝ էկրանի տրոհումն օգտագործելու համար"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Վերագործարկել"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Այլևս ցույց չտալ"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Կրկնակի հպեք՝\nհավելվածը տեղափոխելու համար"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Ծավալել <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Վերականգնել «<xliff:g id="APP_NAME">%1$s</xliff:g>» միջոցառումը"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Ծալել <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Փակել՝ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Հետ"</string>
     <string name="handle_text" msgid="4419667835599523257">"Հավելվածի կեղծանուն"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Հավելվածի պատկերակ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Լիաէկրան"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Համակարգչային տարբերակ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Տրոհված էկրան"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Ավելին"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Լողացող պատուհան"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Փոխել կողմերի հարաբերակցությունը"</string>
     <string name="close_text" msgid="4986518933445178928">"Փակել"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (համակարգչային տարբերակ)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Փոխել չափը"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Հավելվածը հնարավոր չէ տեղափոխել այստեղ"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Ձգել պատուհանը դեպի ձախ"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Ձգել պատուհանը դեպի աջ"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Ծավալել կամ վերականգնել պատուհանի չափսը"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Առավելագույնի հասցրեք հավելվածի պատուհանի չափը"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Վերականգնել պատուհանի չափը"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Ծալել հավելվածի պատուհանը"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Փակեք հավելվածի պատուհանը"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Բացել կարգավորումներն ըստ կանխադրման"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Ընտրեք՝ ինչպես բացել այս հավելվածի վեբ հղումները"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Հավելվածում"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 617f9c9..1d1927f 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kiri 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kiri 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Layar penuh di kanan"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Tukar aplikasi atas dengan aplikasi bawah"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Tukar aplikasi kiri dengan aplikasi kanan"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Layar penuh di atas"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Atas 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Atas 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menu aplikasi dapat ditemukan di sini"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Buka tampilan desktop untuk membuka beberapa aplikasi secara bersamaan"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kembali ke layar penuh kapan saja dari menu aplikasi"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih banyak hal"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Tarik aplikasi lain untuk menggunakan layar terpisah"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Mulai ulang"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Jangan tampilkan lagi"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Ketuk dua kali untuk\nmemindahkan aplikasi ini"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maksimalkan <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Pulihkan <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimalkan <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Tutup <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Kembali"</string>
     <string name="handle_text" msgid="4419667835599523257">"Penanganan aplikasi"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikon Aplikasi"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Layar Penuh"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Tampilan Desktop"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Layar Terpisah"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Lainnya"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Mengambang"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ubah rasio aspek"</string>
     <string name="close_text" msgid="4986518933445178928">"Tutup"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Tampilan Desktop)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ubah ukuran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikasi tidak dapat dipindahkan ke sini"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Ubah ukuran jendela ke kiri"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Ubah ukuran jendela ke kanan"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maksimalkan atau pulihkan ukuran jendela"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maksimalkan ukuran jendela aplikasi"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Pulihkan ukuran jendela"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimalkan jendela aplikasi"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Tutup jendela aplikasi"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buka dengan setelan default"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Pilih cara membuka link web untuk aplikasi ini"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Di aplikasi"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 797a4cc..e94a4c8 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vinstri 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vinstri 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Hægri á öllum skjánum"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Víxla efsta og neðsta forriti"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Víxla hægra og vinstra forriti"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Efri á öllum skjánum"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Efri 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Efri 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Hér finnurðu forritavalmyndina"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Veldu tölvuútgáfu til að opna mörg forrit samtímis"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Þú getur skipt aftur í allan skjáinn hvenær sem er af forritavalmyndinni"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sjáðu og gerðu meira"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dragðu annað forrit inn til að nota skjáskiptingu"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Endurræsa"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ekki sýna þetta aftur"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Ýttu tvisvar til\nað færa þetta forrit"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Stækka <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Endurheimta <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minnka <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Loka <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Til baka"</string>
     <string name="handle_text" msgid="4419667835599523257">"Handfang forrits"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Tákn forrits"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Allur skjárinn"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Tölvuútgáfa"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Skjáskipting"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Meira"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Reikult"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Breyta myndhlutfalli"</string>
     <string name="close_text" msgid="4986518933445178928">"Loka"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (tölvuútgáfa)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Breyta stærð"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ekki er hægt að færa forritið hingað"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Breyta stærð glugga til vinstri"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Breyta stærð glugga til hægri"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Hámarka eða endurheimta stærð glugga"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Hámarka stærð forritsglugga"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Endurheimta gluggastærð"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Lágmarka stærð forritsglugga"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Loka forritsglugga"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Stillingar sjálfvirkrar opnunar"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Veldu hvernig veftenglar opnast í forritinu"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Í forritinu"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index a2787a7..5f8334f 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -100,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Il menu dell\'app si trova qui"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entra nella visualizzazione desktop per aprire più app contemporaneamente"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Torna allo schermo intero in qualsiasi momento dal menu dell\'app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Visualizza più contenuti e fai di più"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trascina in un\'altra app per usare lo schermo diviso"</string>
@@ -121,7 +122,8 @@
     <string name="handle_text" msgid="4419667835599523257">"Punto di manipolazione app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icona dell\'app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Schermo intero"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Visualizzazione desktop"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Schermo diviso"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Altro"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Mobile"</string>
@@ -134,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambia proporzioni"</string>
     <string name="close_text" msgid="4986518933445178928">"Chiudi"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (visualizzazione desktop)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Massimizza schermo"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ridimensiona"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossibile spostare l\'app qui"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 36ac620..c3e8523 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"שמאלה 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"שמאלה 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"מסך ימני מלא"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"החלפה בין האפליקציה העליונה לבין האפליקציה התחתונה"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"החלפה בין האפליקציה השמאלית לבין האפליקציה הימנית"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"מסך עליון מלא"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"עליון 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"עליון 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר ללחוץ כדי לחזור לגרסה הקודמת"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר ללחוץ כדי לסגור."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"תפריט האפליקציה נמצא כאן"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"כדי לפתוח כמה אפליקציות יחד, צריך לעבור למצב תצוגה למחשב"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"אפשר לחזור למסך מלא בכל שלב מתפריט האפליקציה"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"רוצה לראות ולעשות יותר?"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"צריך לגרור אפליקציה אחרת כדי להשתמש במסך המפוצל"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"הפעלה מחדש"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"לא להציג שוב"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"אפשר ללחוץ לחיצה כפולה כדי\nלהעביר את האפליקציה למקום אחר"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"הגדלה של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"שחזור של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"מזעור של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"סגירה של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"חזרה"</string>
     <string name="handle_text" msgid="4419667835599523257">"נקודת אחיזה לאפליקציה"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"סמל האפליקציה"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"מסך מלא"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"תצוגה למחשב"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"מסך מפוצל"</string>
     <string name="more_button_text" msgid="3655388105592893530">"עוד"</string>
     <string name="float_button_text" msgid="9221657008391364581">"בלונים"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"שינוי יחס הגובה-רוחב"</string>
     <string name="close_text" msgid="4986518933445178928">"סגירה"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> (תצוגה למחשב)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"שינוי הגודל"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"לא ניתן להעביר את האפליקציה לכאן"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"שינוי גודל החלון שמשמאל"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"שינוי גודל החלון שמימין"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"שחזור של גודל החלון או הגדלת החלון"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"הגדלת החלון של האפליקציה"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"שחזור הגודל של החלון"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"מזעור החלון של האפליקציה"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"סגירת החלון של האפליקציה"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"הגדרות לפתיחה כברירת מחדל"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"כאן בוחרים איך לפתוח באפליקציה הזו קישורים לדפי אינטרנט"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"באפליקציה"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index d68581b..1a73dd3 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -100,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"アプリメニューはここにあります"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"デスクトップ ビューに切り替えて複数のアプリを同時に開けます"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"アプリメニューからいつでも全画面表示に戻れます"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"表示を拡大して機能を強化"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"分割画面にするにはもう 1 つのアプリをドラッグしてください"</string>
@@ -121,7 +122,8 @@
     <string name="handle_text" msgid="4419667835599523257">"アプリハンドル"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"アプリのアイコン"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"全画面表示"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"デスクトップ ビュー"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"分割画面"</string>
     <string name="more_button_text" msgid="3655388105592893530">"その他"</string>
     <string name="float_button_text" msgid="9221657008391364581">"フローティング"</string>
@@ -134,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"アスペクト比を変更"</string>
     <string name="close_text" msgid="4986518933445178928">"閉じる"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g>(デスクトップ ビュー)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"サイズ変更"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"アプリはここに移動できません"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 2fbe1a7..4bbfaef 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"მარცხენა ეკრანი — 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"მარცხენა ეკრანი — 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"მარჯვენა ნაწილის სრულ ეკრანზე გაშლა"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"ზედა და ქვედა აპების მდებარეობის გაცვლა"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"მარცხენა და მარჯვენა აპების მდებარეობის გაცვლა"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ზედა ნაწილის სრულ ეკრანზე გაშლა"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ზედა ეკრანი — 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ზედა ეკრანი — 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"აპის მენიუ შეგიძლიათ იხილოთ აქ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"რამდენიმე აპის ერთდროულად გასახსნელად შედით დესკტოპის ხედში"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"სრულ ეკრანზე ნებისმიერ დროს შეგიძლიათ დაბრუნდეთ აპის მენიუდან"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"მეტის ნახვა და გაკეთება"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ეკრანის გასაყოფად ჩავლებით გადაიტანეთ სხვა აპში"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"გადატვირთვა"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"აღარ გამოჩნდეს"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"ამ აპის გადასატანად\nორმაგად შეეხეთ მას"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის მაქსიმალურად გაშლა"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის აღდგენა"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის ჩაკეცვა"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის დახურვა"</string>
     <string name="back_button_text" msgid="1469718707134137085">"უკან"</string>
     <string name="handle_text" msgid="4419667835599523257">"აპის იდენტიფიკატორი"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"აპის ხატულა"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"სრულ ეკრანზე"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"დესკტოპის ხედი"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"ეკრანის გაყოფა"</string>
     <string name="more_button_text" msgid="3655388105592893530">"სხვა"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ფარფატი"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"თანაფარდობის შეცვლა"</string>
     <string name="close_text" msgid="4986518933445178928">"დახურვა"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (დესკტოპის ხედი)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"აპლიკაციის გაშლა სრულ ეკრანზე"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ზომის შეცვლა"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"აპის აქ გადატანა შეუძლებელია"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"ფანჯრის ზომის შეცვლა მარცხნივ"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"ფანჯრის ზომის შეცვლა მარჯვნივ"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"ფანჯრის მაქსიმალურ ზომამდე გაზრდა ან აღდგენა"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"აპის ფანჯრის მაქსიმალურ ზომამდე გაზრდა"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"ფანჯრის ზომის აღდგენა"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"აპის ფანჯრის ზომის შემცირება"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"აპის ფანჯრის დახურვა"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"პარამეტრების ნაგულისხმევად გახსნა"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ამ აპისთვის ვებ ბმულების გახსნის წესის არჩევა"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"აპში"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index c494b16..2f6404d 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% сол жақта"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% сол жақта"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Оң жағын толық экранға шығару"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Жоғарыдағы қолданбаны төмендегімен орнын ауыстыру"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Сол жақтағы қолданбаны оң жақтағымен орнын ауыстыру"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Жоғарғы жағын толық экранға шығару"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% жоғарғы жақта"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% жоғарғы жақта"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Қолданба мәзірін осы жерден табуға болады."</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Бірнеше қолданбаны бірге ашу үшін компьютерлік нұсқаны қосыңыз."</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Қолданба мәзірінен кез келген уақытта толық экранға оралыңыз."</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Қосымша ақпаратты қарап, әрекеттер жасау"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлу үшін басқа қолданбаға өтіңіз."</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Өшіріп қосу"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Қайта көрсетілмесін"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Бұл қолданбаны басқа орынға\nжылжыту үшін екі рет түртіңіз."</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын ұлғайту"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын қалпына келтіру"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын кішірейту"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын жабу"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Артқа"</string>
     <string name="handle_text" msgid="4419667835599523257">"Қолданба идентификаторы"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Қолданба белгішесі"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Толық экран"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Компьютерлік нұсқа"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Экранды бөлу"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Қосымша"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Қалқыма"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Арақатынасты өзгерту"</string>
     <string name="close_text" msgid="4986518933445178928">"Жабу"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (компьютерлік нұсқа)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Өлшемін өзгерту"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Қолданба бұл жерге қойылмайды."</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Терезе өлшемін сол жаққа өзгерту"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Терезе өлшемін оң жаққа өзгерту"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Терезе өлшемін ұлғайту не қалпына келтіру"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Қолданба терезесінің өлшемін ұлғайту"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Терезе өлшемін қалпына келтіру"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Қолданба терезесін кішірейту"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Қолданба терезесін жабу"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Әдепкісінше ашу параметрлері"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Осы қолданбадағы веб-сілтемелерді ашу жолын таңдаңыз"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Қолданбада"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 234c355..d3942fd 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ឆ្វេង 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ឆ្វេង 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"អេក្រង់ពេញខាងស្តាំ"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"ប្ដូរកម្មវិធីខាងលើគេទៅខាងក្រោម"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"ប្ដូរកម្មវិធីខាងឆ្វេងទៅខាងស្ដាំ"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"អេក្រង់ពេញខាងលើ"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ខាងលើ 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ខាងលើ 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបាន​ដោះស្រាយ​បញ្ហានេះទេឬ?\nចុចដើម្បី​ត្រឡប់"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមាន​បញ្ហាពាក់ព័ន្ធនឹង​កាមេរ៉ាទេឬ? ចុចដើម្បី​ច្រានចោល។"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"អាចរកឃើញម៉ឺនុយកម្មវិធីនៅទីនេះ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ចូលទិដ្ឋភាព​លើកុំព្យូទ័រ ដើម្បីបើកកម្មវិធីច្រើនជាមួយគ្នា"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ត្រឡប់ទៅអេក្រង់ពេញវិញនៅពេលណាក៏បានពីម៉ឺនុយកម្មវិធី"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"មើលឃើញ និងធ្វើបានកាន់តែច្រើន"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"អូស​កម្មវិធី​មួយ​ទៀត​ចូល ដើម្បី​ប្រើ​មុខងារ​បំបែកអេក្រង់"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"ចាប់ផ្ដើម​ឡើង​វិញ"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"កុំ​បង្ហាញ​ម្ដង​ទៀត"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"ចុចពីរដងដើម្បី\nផ្លាស់ទីកម្មវិធីនេះ"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"ពង្រីក <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"ស្ដារ <xliff:g id="APP_NAME">%1$s</xliff:g> ឡើងវិញ"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"បង្រួម <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"បិទ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ថយក្រោយ"</string>
     <string name="handle_text" msgid="4419667835599523257">"ឈ្មោះអ្នកប្រើប្រាស់កម្មវិធី"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"រូប​កម្មវិធី"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"អេក្រង់​ពេញ"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ទិដ្ឋភាព​លើកុំព្យូទ័រ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"មុខងារ​បំបែក​អេក្រង់"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ច្រើនទៀត"</string>
     <string name="float_button_text" msgid="9221657008391364581">"អណ្ដែត"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ប្ដូរ​​សមាមាត្រ"</string>
     <string name="close_text" msgid="4986518933445178928">"បិទ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"បិទ​ម៉ឺនុយ"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ទិដ្ឋភាព​លើកុំព្យូទ័រ)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ប្ដូរ​ទំហំ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"មិនអាចផ្លាស់ទីកម្មវិធីមកទីនេះបានទេ"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"ប្ដូរទំហំវិនដូទៅឆ្វេង"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"ប្ដូរទំហំវិនដូទៅស្ដាំ"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"ស្ដារ ឬបង្កើនទំហំវិនដូជាអតិបរមា"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"ពង្រីកទំហំវិនដូកម្មវិធី"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"ស្ដារទំហំវិនដូ"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"បង្រួមវិនដូកម្មវិធី"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"បិទវិនដូកម្មវិធី"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ការកំណត់បើកតាមលំនាំដើម"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ជ្រើសរើសរបៀបបើកតំណបណ្ដាញសម្រាប់កម្មវិធីនេះ"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"នៅក្នុងកម្មវិធី"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 61ee3c3..fb15e34 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% ಎಡಕ್ಕೆ"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% ಎಡಕ್ಕೆ"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ಬಲ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"ಮೇಲಿನ ಮತ್ತು ಕೆಳಗಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ಅದಲು-ಬದಲು ಮಾಡಿ"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"ಎಡ ಮತ್ತು ಬಲದ ಆ್ಯಪ್‌ಗಳನ್ನು ಅದಲು-ಬದಲು ಮಾಡಿ"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ಮೇಲಿನ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% ಮೇಲಕ್ಕೆ"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% ಮೇಲಕ್ಕೆ"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ಆ್ಯಪ್ ಮೆನುವನ್ನು ಇಲ್ಲಿ ಕಾಣಬಹುದು"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ಹಲವು ಆ್ಯಪ್‌ಗಳನ್ನು ಒಟ್ಟಿಗೆ ತೆರೆಯಲು ಡೆಸ್ಕ್‌ಟಾಪ್ ವೀಕ್ಷಣೆಯನ್ನು ನಮೂದಿಸಿ"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ಆ್ಯಪ್ ಮೆನುವಿನಿಂದ ಯಾವಾಗ ಬೇಕಾದರೂ ಫುಲ್‌ಸ್ಕ್ರೀನ್‌ಗೆ ಹಿಂತಿರುಗಿ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ನೋಡಿ ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ಮಾಡಿ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್‌ಗಾಗಿ ಮತ್ತೊಂದು ಆ್ಯಪ್‌ನಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"ಮರುಪ್ರಾರಂಭಿಸಿ"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ಮತ್ತೊಮ್ಮೆ ತೋರಿಸಬೇಡಿ"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಸರಿಸಲು\nಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಿ"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ಮಿನಿಮೈಸ್ ಮಾಡಿ"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ಮುಚ್ಚಿ"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ಹಿಂದಕ್ಕೆ"</string>
     <string name="handle_text" msgid="4419667835599523257">"ಆ್ಯಪ್ ಹ್ಯಾಂಡಲ್"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ಆ್ಯಪ್ ಐಕಾನ್"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ಫುಲ್‌ಸ್ಕ್ರೀನ್"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ಡೆಸ್ಕ್‌ಟಾಪ್ ವೀಕ್ಷಣೆ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ಇನ್ನಷ್ಟು"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ಫ್ಲೋಟ್"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
     <string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ಡೆಸ್ಕ್‌ಟಾಪ್ ವೀಕ್ಷಣೆ)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ಆ್ಯಪ್ ಅನ್ನು ಇಲ್ಲಿಗೆ ಸರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"ಮರುಗಾತ್ರಗೊಳಿಸಿ ವಿಂಡೋವನ್ನು ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"ಮರುಗಾತ್ರಗೊಳಿಸಿ ವಿಂಡೋವನ್ನು ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"ವಿಂಡೋ ಗಾತ್ರವನ್ನು ಗರಿಷ್ಠಗೊಳಿಸಿ ಅಥವಾ ಮರುಸ್ಥಾಪಿಸಿ"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"ಆ್ಯಪ್ ವಿಂಡೋದ ಗಾತ್ರವನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"ವಿಂಡೋ ಗಾತ್ರವನ್ನು ಮರುಸ್ಥಾಪಿಸಿ"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"ಆ್ಯಪ್ ವಿಂಡೋವನ್ನು ಮಿನಿಮೈಸ್ ಮಾಡಿ"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"ಆ್ಯಪ್ ವಿಂಡೋವನ್ನು ಮುಚ್ಚಿರಿ"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ಡೀಫಾಲ್ಟ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಂದ ತೆರೆಯಿರಿ"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ಈ ಆ್ಯಪ್‌ಗೆ ವೆಬ್ ಲಿಂಕ್‌ಗಳನ್ನು ಹೇಗೆ ತೆರೆಯಬೇಕು ಎಂಬುದನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ಆ್ಯಪ್‌ನಲ್ಲಿ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 2b36ba1..dbfd32a 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"왼쪽 화면 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"왼쪽 화면 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"오른쪽 화면 전체화면"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"상단 앱과 하단 앱 바꾸기"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"왼쪽 앱과 오른쪽 앱 바꾸기"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"위쪽 화면 전체화면"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"위쪽 화면 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"위쪽 화면 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"앱 메뉴는 여기에서 찾을 수 있습니다."</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"데스크톱 뷰를 실행하여 여러 앱을 함께 열 수 있습니다."</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"언제든지 앱 메뉴에서 전체 화면으로 돌아갈 수 있습니다."</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"더 많은 정보를 보고 더 많은 작업을 처리하세요"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"화면 분할을 사용하려면 다른 앱을 드래그해 가져옵니다."</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"다시 시작"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"다시 표시 안함"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"두 번 탭하여\n이 앱 이동"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> 최대화"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> 복원"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> 최소화"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> 닫기"</string>
     <string name="back_button_text" msgid="1469718707134137085">"뒤로"</string>
     <string name="handle_text" msgid="4419667835599523257">"앱 핸들"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"앱 아이콘"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"전체 화면"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"데스크톱 뷰"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"화면 분할"</string>
     <string name="more_button_text" msgid="3655388105592893530">"더보기"</string>
     <string name="float_button_text" msgid="9221657008391364581">"플로팅"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"가로세로 비율 변경"</string>
     <string name="close_text" msgid="4986518933445178928">"닫기"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g>(데스크톱 뷰)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"크기 조절"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"앱을 여기로 이동할 수 없음"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"창 크기 왼쪽으로 조절"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"창 크기 오른쪽으로 조절"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"창 최대화 또는 크기 복원"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"앱 창 크기 최대화"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"창 크기 복원"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"앱 창 최소화"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"앱 창 닫기"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"기본값으로 열기 설정"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"이 앱에서 웹 링크를 여는 방법을 선택하세요"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"앱에서"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index bb3c2fd..36194cd 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Сол жактагы экранды 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Сол жактагы экранды 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Оң жактагы экранды толук экран режимине өткөрүү"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Өйдө жактагы колдонмону ылдый жактагы менен алмаштыруу"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Сол жактагы колдонмону оң жактагы менен алмаштыруу"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Үстүнкү экранды толук экран режимине өткөрүү"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Үстүнкү экранды 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Үстүнкү экранды 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Колдонмонун менюсун ушул жерден таба аласыз"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Бир убакта бир нече колдонмону ачуу үчүн компьютердик версияга өтүңүз"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Каалаган убакта колдонмонун менюсунан толук экранга кайта аласыз"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Көрүп, көбүрөөк нерселерди жасаңыз"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлүү үчүн башка колдонмону сүйрөңүз"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Өчүрүп күйгүзүү"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Экинчи көрүнбөсүн"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Бул колдонмону жылдыруу үчүн\nэки жолу таптаңыз"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> чоңойтуу"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> калыбына келтирүү"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> кичирейтүү"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> жабуу"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Артка"</string>
     <string name="handle_text" msgid="4419667835599523257">"Колдонмонун маркери"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Колдонмонун сүрөтчөсү"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Толук экран"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Компьютердик версия"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Экранды бөлүү"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Дагы"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Калкыма"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Тараптардын катнашын өзгөртүү"</string>
     <string name="close_text" msgid="4986518933445178928">"Жабуу"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (компьютердик версия)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Өлчөмүн өзгөртүү"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Колдонмону бул жерге жылдырууга болбойт"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Терезенин өлчөмүн солго өзгөртүү"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Терезенин өлчөмүн оңго өзгөртүү"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Терезенин өлчөмүн чоңойтуу же калыбына келтирүү"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Колдонмонун терезесинин өлчөмүн чоңойтуу"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Колдонмонун терезесинин өлчөмүн калыбына келтирүү"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Колдонмонун терезесин кичирейтүү"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Колдонмонун терезесин жабуу"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Демейки шартта ачылуучу шилтемелердин параметрлери"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Колдонмодо шилтемелер кантип ачыларын тандаңыз"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Колдонмодо"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 8850923..671f924 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ຊ້າຍ 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ຊ້າຍ 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ເຕັມໜ້າຈໍຂວາ"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"ສະຫຼັບແອັບທາງເທິງກັບທາງລຸ່ມ"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"ສະຫຼັບແອັບທາງຊ້າຍກັບທາງຂວາ"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ເຕັມໜ້າຈໍເທິງສຸດ"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ເທິງສຸດ 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ເທິງສຸດ 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອ​ປິດ​ໄວ້."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ສາມາດເບິ່ງເມນູແອັບໄດ້ບ່ອນນີ້"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ເຂົ້າສູ່ມຸມມອງເດັສທັອບເພື່ອເປີດຫຼາຍແອັບພ້ອມກັນ"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ກັບຄືນໄປຫາໂໝດເຕັມຈໍໄດ້ທຸກເວລາຈາກເມນູແອັບ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ເບິ່ງ ແລະ ເຮັດຫຼາຍຂຶ້ນ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ລາກໄປໄວ້ໃນແອັບອື່ນເພື່ອແບ່ງໜ້າຈໍ"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"ຣີສະຕາດ"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ບໍ່ຕ້ອງສະແດງອີກ"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"ແຕະສອງເທື່ອເພື່ອ\nຍ້າຍແອັບນີ້"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"ຂະຫຍາຍ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"ກູ້ຄືນ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"ຫຍໍ້ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"ປິດ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ກັບຄືນ"</string>
     <string name="handle_text" msgid="4419667835599523257">"ຊື່ຜູ້ໃຊ້ແອັບ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ໄອຄອນແອັບ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ເຕັມຈໍ"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ມຸມມອງຢູ່ເດັສທັອບ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"ແບ່ງໜ້າຈໍ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ເພີ່ມເຕີມ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ລອຍ"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ປ່ຽນອັດຕາສ່ວນຮູບ"</string>
     <string name="close_text" msgid="4986518933445178928">"ປິດ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ມຸມມອງຢູ່ເດັສທັອບ)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ປັບຂະໜາດ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ບໍ່ສາມາດຍ້າຍແອັບມາບ່ອນນີ້ໄດ້"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"ປັບຂະໜາດໜ້າຈໍໄປທາງຊ້າຍ"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"ປັບຂະໜາດໜ້າຈໍໄປທາງຂວາ"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"ຂະຫຍາຍ ຫຼື ຄືນຄ່າຂະໜາດໜ້າຈໍ"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"ຂະຫຍາຍຂະໜາດໜ້າຈໍແອັບໃຫ້ໃຫຍ່ສຸດ"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"ກູ້ຄືນຂະໜາດໜ້າຈໍ"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"ຫຍໍ້ໜ້າຈໍແອັບ"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"ປິດໜ້າຈໍແອັບ"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ເປີດຕາມການຕັ້ງຄ່າເລີ່ມຕົ້ນ"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ເລືອກວິທີເປີດລິ້ງເວັບສຳລັບແອັບນີ້"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ໃນແອັບ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 8ed826a..794c5ab 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kairysis ekranas 50 %"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kairysis ekranas 30 %"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Dešinysis ekranas viso ekrano režimu"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Sukeisti viršutiniąją programą su apatiniąja"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Sukeisti kairiąją programą su dešiniąja"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Viršutinis ekranas viso ekrano režimu"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Viršutinis ekranas 70 %"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Viršutinis ekranas 50 %"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Programos meniu rasite čia"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Įjunkite rodinio versiją staliniams kompiuteriams, kad vienu metu atidarytumėte kelias programas"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Bet kada iš programos meniu grįžkite į viso ekrano režimą"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daugiau turinio ir funkcijų"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Vilkite kitoje programoje, kad galėtumėte naudoti išskaidyto ekrano režimą"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Paleisti iš naujo"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Daugiau neberodyti"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Dukart palieskite, kad\nperkeltumėte šią programą"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Padidinti „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Atkurti „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Sumažinti „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Uždaryti „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atgal"</string>
     <string name="handle_text" msgid="4419667835599523257">"Programos kreipinys"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Programos piktograma"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Visas ekranas"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Rodinio versija staliniams kompiuteriams"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Išskaidyto ekrano režimas"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Daugiau"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Slankusis langas"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Keisti kraštinių santykį"</string>
     <string name="close_text" msgid="4986518933445178928">"Uždaryti"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ (rodinio versija staliniams kompiuteriams)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Pakeisti dydį"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Programos negalima perkelti čia"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Pakeisti lango dydį kairėje"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Pakeisti lango dydį dešinėje"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Padidinti arba atkurti lango dydį"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Padidinti programos lango dydį"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Atkurti lango dydį"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Sumažinti programos langą"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Uždaryti programos langą"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Atidaryti pagal numatytuosius nustatymus"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Pasirinkite, kaip atidaryti šios programos žiniatinklio nuorodas"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Programoje"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 1227055..5b44112 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Pa kreisi 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Pa kreisi 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Labā daļa pa visu ekrānu"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Apmainīt vietām lietotni augšpusē ar lietotni apakšpusē"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Apmainīt vietām lietotni kreisajā pusē ar lietotni labajā pusē"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Augšdaļa pa visu ekrānu"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Augšdaļa 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Augšdaļa 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Šeit ir pieejama lietotņu izvēlne"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Lai atvērtu vairākas lietotnes vienlaikus, pārejiet uz skatu datorā"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"No lietotnes izvēlnes varat jebkurā brīdī atgriezties pilnekrāna režīmā."</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Uzziniet un paveiciet vairāk"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Lai izmantotu sadalītu ekrānu, ievelciet vēl vienu lietotni"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Restartēt"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Vairs nerādīt"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Veiciet dubultskārienu,\nlai pārvietotu šo lietotni"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maksimizēt lietotni <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Atjaunot lietotni <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimizēt lietotni <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Aizvērt lietotni <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Atpakaļ"</string>
     <string name="handle_text" msgid="4419667835599523257">"Lietotnes turis"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Lietotnes ikona"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pilnekrāna režīms"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Skats datorā"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Sadalīt ekrānu"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Vairāk"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Peldošs"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mainīt malu attiecību"</string>
     <string name="close_text" msgid="4986518933445178928">"Aizvērt"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (skats datorā)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Mainīt lielumu"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Lietotni nevar pārvietot šeit."</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Mainīt loga lielumu uz kreiso pusi"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Mainīt loga lielumu uz labo pusi"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maksimizēt vai atjaunot loga lielumu"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maksimizēt lietotnes loga lielumu"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Atjaunot loga lielumu"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimizēt lietotnes logu"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Aizvērt lietotnes logu"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Atvērt pēc noklusējuma iestatījumiem"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Izvēlieties, kā atvērt šajā lietotnē norādītās saites"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Lietotnē"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index eb51374..96bf9b6 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Левиот 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Левиот 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Десниот на цел екран"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Заменете ги местата на горната и долната апликација"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Заменете ги местата на левата и десната апликација"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Горниот на цел екран"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Горниот 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Горниот 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Менито со апликации може да го најдете овде"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Влезете во приказот на компјутер за да отворите повеќе апликации заедно"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вратете се на цел екран од менито со апликации кога сакате"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Погледнете и направете повеќе"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Повлечете друга апликација за поделен екран"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Рестартирај"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Не прикажувај повторно"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Допрете двапати за да ја\nпоместите апликацијава"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Максимизирај <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Врати <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Минимизирај <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Затвори <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
     <string name="handle_text" msgid="4419667835599523257">"Прекар на апликацијата"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Икона на апликацијата"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Цел екран"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Приказ на компјутер"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Поделен екран"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Повеќе"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Лебдечко"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени го соодносот"</string>
     <string name="close_text" msgid="4986518933445178928">"Затворете"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (приказ на компјутер)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Смени големина"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликацијата не може да се премести овде"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Променете ја големината на прозорецот налево"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Променете ја големината на прозорецот надесно"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Максимизирајте или вратете ја големината на прозорецот"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Максимизирај ја големината на прозорецот на апликацијата"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Врати ја големината на прозорецот"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Минимизирај го прозорецот на апликацијата"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Затвори го прозорецот на апликацијата"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Отвори според стандардните поставки"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Изберете како да се отвораат линковите за апликацијава"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Во апликацијата"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index cb84dec8..8085601 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -100,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ആപ്പ് മെനു ഇവിടെ കണ്ടെത്താനാകും"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ഒന്നിലധികം ആപ്പുകൾ ഒരുമിച്ച് തുറക്കാൻ ഡെസ്‌ക്‌‌ടോപ്പ് വ്യൂവിൽ പ്രവേശിക്കുക"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ആപ്പ് മെനുവിൽ നിന്ന് ഏതുസമയത്തും പൂർണ്ണ സ്‌ക്രീനിലേക്ക് മടങ്ങുക"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"കൂടുതൽ കാണുക, ചെയ്യുക"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"സ്‌ക്രീൻ വിഭജന മോഡിന്, മറ്റൊരു ആപ്പ് വലിച്ചിടുക"</string>
@@ -121,7 +122,8 @@
     <string name="handle_text" msgid="4419667835599523257">"ആപ്പ് ഹാൻഡിൽ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ആപ്പ് ഐക്കൺ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"പൂർണ്ണസ്ക്രീൻ"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ഡെസ്‌ക്ടോപ്പ് വ്യൂ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"സ്‌ക്രീൻ വിഭജനം"</string>
     <string name="more_button_text" msgid="3655388105592893530">"കൂടുതൽ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ഫ്ലോട്ട്"</string>
@@ -134,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"വീക്ഷണ അനുപാതം മാറ്റുക"</string>
     <string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ഡെസ്‌ക്ടോപ്പ് വ്യൂ)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്‌ക്രീൻ വലുതാക്കുക"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"വലുപ്പം മാറ്റുക"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ആപ്പ് ഇവിടേക്ക് നീക്കാനാകില്ല"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index fca2f7c..5efe62d 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Зүүн 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Зүүн 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Баруун талын бүтэн дэлгэц"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Дээд талын аппыг доод талынхаар солих"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Зүүн талын аппыг баруун талынхаар солих"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Дээд талын бүтэн дэлгэц"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Дээд 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Дээд 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Аппын цэсийг эндээс олох боломжтой"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Олон аппыг хамтад нь нээхийн тулд дэлгэц дээр харагдах байдалд орно уу"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Аппын цэсээс бүтэн дэлгэц рүү хүссэн үедээ буцна уу"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Харж илүү ихийг хий"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Дэлгэц хуваах горимд ашиглахын тулд өөр аппыг чирнэ үү"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Дахин эхлүүлэх"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Дахиж бүү харуул"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Энэ аппыг зөөхийн тулд\nхоёр товшино уу"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г томруулах"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г сэргээх"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г жижгэрүүлэх"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г хаах"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Буцах"</string>
     <string name="handle_text" msgid="4419667835599523257">"Аппын бариул"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Aппын дүрс тэмдэг"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Бүтэн дэлгэц"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Дэлгэц дээр харагдах байдал"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Дэлгэцийг хуваах"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Бусад"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Хөвөгч"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Аспектын харьцааг өөрчлөх"</string>
     <string name="close_text" msgid="4986518933445178928">"Хаах"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (дэлгэц дээр харагдах байдал)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Хэмжээг өөрчлөх"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Аппыг ийш зөөх боломжгүй"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Цонхны хэмжээг зүүн тал руу өөрчлөх"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Цонхны хэмжээг баруун тал руу өөрчлөх"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Цонхны хэмжээг томруулах эсвэл сэргээх"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Аппын цонхны хэмжээг томруулах"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Цонхны хэмжээг сэргээх"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Аппын цонхыг жижгэрүүлэх"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Аппын цонхыг хаах"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Өгөгдмөл тохиргоогоор нээх"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Энэ аппад веб холбоосыг хэрхэн нээхийг сонгоно уу"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Аппад"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 8881187..e10c8d8 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"डावी 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"डावी 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"उजवी फुल स्क्रीन"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"सर्वात वरचे अ‍ॅप हे तळाशी असलेल्या अ‍ॅपने स्वॅप करा"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"डावीकडील अ‍ॅप हे उजवीकडील अ‍ॅपने स्वॅप करा"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"शीर्ष फुल स्क्रीन"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"शीर्ष 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"शीर्ष 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्‍यासाठी टॅप करा."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ॲप मेनू इथे आढळू शकतो"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"एकाहून अधिक ॲप्स एकत्र उघडण्यासाठी डेस्कटॉप दृश्यात एंटर करा"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ॲप मेनूमधून कधीही फुल स्क्रीनवर परत या"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पहा आणि आणखी बरेच काही करा"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रीन वापरण्यासाठी दुसरे ॲप ड्रॅग करा"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"रीस्टार्ट करा"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"पुन्हा दाखवू नका"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"हे ॲप हलवण्यासाठी\nदोनदा टॅप करा"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> मोठे करा"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> रिस्टोअर करा"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> लहान करा"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> बंद करा"</string>
     <string name="back_button_text" msgid="1469718707134137085">"मागे जा"</string>
     <string name="handle_text" msgid="4419667835599523257">"अ‍ॅपचे हँडल"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"अ‍ॅप आयकन"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"फुलस्‍क्रीन"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"डेस्कटॉप दृश्य"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"स्प्लिट स्क्रीन"</string>
     <string name="more_button_text" msgid="3655388105592893530">"आणखी"</string>
     <string name="float_button_text" msgid="9221657008391364581">"फ्लोट"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"आस्पेक्ट रेशो बदला"</string>
     <string name="close_text" msgid="4986518933445178928">"बंद करा"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप दृश्य)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"आकार बदला"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"अ‍ॅप इथे हलवू शकत नाही"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"अ‍ॅप विंडोचा डावीकडे आकार बदला"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"अ‍ॅप विंडोचा उजवीकडे आकार बदला"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"विंडोचा आकार मोठा करा किंवा रिस्टोअर करा"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"अ‍ॅप विंडोचा आकार मोठा करा"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"विंडोचा आकार रिस्टोअर करा"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"अ‍ॅप विंडो लहान करा"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"अ‍ॅप विंडो बंद करा"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"बाय डीफॉल्ट सेटिंग्ज उघडा"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"या अ‍ॅपसाठीच्या वेब लिंक कशा उघडाव्यात हे निवडा"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ॲपमध्ये"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index e954b0f..e18e7ec 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -100,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menu apl boleh ditemukan di sini"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Masuki paparan desktop untuk membuka berbilang apl serentak"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kembali kepada skrin penuh pada bila-bila masa daripada menu apl"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Seret masuk apl lain untuk menggunakan skrin pisah"</string>
@@ -121,7 +122,8 @@
     <string name="handle_text" msgid="4419667835599523257">"Pengendalian apl"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikon Apl"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Skrin penuh"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Paparan Desktop"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Skrin Pisah"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Lagi"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Terapung"</string>
@@ -134,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tukar nisbah bidang"</string>
     <string name="close_text" msgid="4986518933445178928">"Tutup"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Paparan Desktop)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ubah saiz"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apl tidak boleh dialihkan ke sini"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 3451701..334a797 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ဘယ်ဘက် မျက်နှာပြင် ၅၀%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ဘယ်ဘက် မျက်နှာပြင် ၃၀%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ညာဘက် မျက်နှာပြင်အပြည့်"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"အပေါ်အက်ပ်ကို အောက်သို့ ပြောင်းရန်"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"ဘယ်ဘက်အက်ပ်ကို ညာဘက်သို့ ပြောင်းရန်"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"အပေါ်ဘက် မျက်နှာပြင်အပြည့်"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"အပေါ်ဘက် မျက်နှာပြင် ၇၀%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"အပေါ်ဘက် မျက်နှာပြင် ၅၀%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"အက်ပ်မီနူးကို ဤနေရာတွင် တွေ့နိုင်သည်"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"အက်ပ်များစွာကို အတူတကွဖွင့်ရန်အတွက် ဒက်စ်တော့မြင်ကွင်းသို့ ဝင်ရောက်နိုင်သည်"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"အက်ပ်မီနူးမှ ဖန်သားပြင်အပြည့်သို့ အချိန်မရွေး ပြန်သွားနိုင်သည်"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ကြည့်ပြီး ပိုမိုလုပ်ဆောင်ပါ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းအတွက် အက်ပ်နောက်တစ်ခုကို ဖိဆွဲပါ"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"ပြန်စရန်"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"နောက်ထပ်မပြပါနှင့်"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"ဤအက်ပ်ကို ရွှေ့ရန်\nနှစ်ချက်တို့ပါ"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> ချဲ့ရန်"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ပြန်ယူရန်"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> ချုံ့ရန်"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> ပိတ်ရန်"</string>
     <string name="back_button_text" msgid="1469718707134137085">"နောက်သို့"</string>
     <string name="handle_text" msgid="4419667835599523257">"အက်ပ်သုံးသူအမည်"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"အက်ပ်သင်္ကေတ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ဖန်သားပြင်အပြည့်"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ဒက်စ်တော့ မြင်ကွင်း"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"မျက်နှာပြင် ခွဲ၍ပြသရန်"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ပိုပြပါ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"မျှောရန်"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"အချိုးအစား ပြောင်းရန်"</string>
     <string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ဒက်စ်တော့ မြင်ကွင်း)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"အရွယ်ပြင်ရန်"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"အက်ပ်ကို ဤနေရာသို့ ရွှေ့၍မရပါ"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"ဝင်းဒိုးကို ဘယ်ဘက်သို့ အရွယ်ပြင်ရန်"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"ဝင်းဒိုးကို ညာဘက်သို့ အရွယ်ပြင်ရန်"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"ဝင်းဒိုးအရွယ်အစားကို ချဲ့ရန် (သို့) ပြန်ပြောင်းရန်"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"အက်ပ်ဝင်းဒိုး အရွယ်အစားကို ချဲ့ရန်"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"ဝင်းဒိုးအရွယ်အစား ပြန်ပြောင်းရန်"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"အက်ပ်ဝင်းဒိုးကို ချုံ့ရန်"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"အက်ပ်ဝင်းဒိုးကို ပိတ်ရန်"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"မူရင်းဆက်တင်ဖြင့် ဖွင့်ရန်"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ဤအက်ပ်အတွက် ဝဘ်လင့်ခ်များ မည်သို့ဖွင့်မည်ကို ရွေးပါ"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"အက်ပ်တွင်"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 3a4100c..7e21b47 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Sett størrelsen på den venstre delen av skjermen til 50 %"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Sett størrelsen på den venstre delen av skjermen til 30 %"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Utvid den høyre delen av skjermen til hele skjermen"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Bytt om på den øvre og nedre appen"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Bytt om på venstre og høyre app"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Utvid den øverste delen av skjermen til hele skjermen"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Sett størrelsen på den øverste delen av skjermen til 70 %"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Sett størrelsen på den øverste delen av skjermen til 50 %"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Her finner du appmenyen"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Gå til skrivebordsvisningen for å åpne flere apper samtidig"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Du kan gå tilbake til fullskjermmodusen når som helst fra appmenyen"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gjør mer"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dra inn en annen app for å bruke delt skjerm"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Start på nytt"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ikke vis dette igjen"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Dobbelttrykk for\nå flytte denne appen"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maksimer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Gjenopprett <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimer <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Lukk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tilbake"</string>
     <string name="handle_text" msgid="4419667835599523257">"Apphåndtak"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Fullskjerm"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Datamaskinvisning"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Delt skjerm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mer"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Svevende"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Endre høyde/bredde-forholdet"</string>
     <string name="close_text" msgid="4986518933445178928">"Lukk"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (datamaskinvisning)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Endre størrelse"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Appen kan ikke flyttes hit"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Endre størrelsen på vinduet til venstre"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Endre størrelsen på vinduet til høyre"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maksimer eller gjenopprett størrelsen på vinduet"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maksimer størrelsen på appvinduet"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Gjenopprett vindusstørrelsen"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimer appvinduet"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Lukk appvinduet"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Innstillinger for åpning som standard"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Velg hvordan nettlinker skal åpnes for denne appen"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index cc31e38..976f837 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"बायाँ भाग ५०%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"बायाँ भाग ३०%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"दायाँ भाग फुल स्क्रिन"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"सिरान र पुछारको एप अदलबदल गर्नुहोस्"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"दायाँ र बायाँतिरको एप अदलबदल गर्नुहोस्"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"माथिल्लो भाग फुल स्क्रिन"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"माथिल्लो भाग ७०%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"माथिल्लो भाग ५०%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"एपको मेनु यहाँ भेट्टाउन सकिन्छ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"एकभन्दा बढी एपहरू सँगै देखाउन डेस्कटप भ्यू हाल्नुहोस्"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"जुनसुकै बेला एपको मेनुबाट फुल स्क्रिनमा फर्कनुहोस्"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"थप कुरा हेर्नुहोस् र गर्नुहोस्"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रिन मोड प्रयोग गर्न अर्को एप ड्रयाग एन्ड ड्रप गर्नुहोस्"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"रिस्टार्ट गर्नुहोस्"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"फेरि नदेखाउनुहोस्"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"यो एप सार्न डबल\nट्याप गर्नुहोस्"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> म्याक्सिमाइज गर्नुहोस्"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> रिस्टोर गर्नुहोस्"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> मिनिमाइज गर्नुहोस्"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> बन्द गर्नुहोस्"</string>
     <string name="back_button_text" msgid="1469718707134137085">"पछाडि"</string>
     <string name="handle_text" msgid="4419667835599523257">"एपको ह्यान्डल"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"एपको आइकन"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"फुल स्क्रिन"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"डेस्कटप भ्यू"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"स्प्लिट स्क्रिन"</string>
     <string name="more_button_text" msgid="3655388105592893530">"थप"</string>
     <string name="float_button_text" msgid="9221657008391364581">"फ्लोट"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
     <string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटप भ्यू)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रिन ठुलो बनाउनुहोस्"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"आकार बदल्नुहोस्"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"एप सारेर यहाँ ल्याउन सकिएन"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"विन्डोको आकार बदलेर बायाँतिर लैजानुहोस्"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"विन्डोको आकार बदलेर दायाँतिर लैजानुहोस्"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"विन्डोको आकार म्याक्सिमाइज गर्नुहोस् वा रिस्टोर गर्नुहोस्"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"एपको विन्डोको आकार म्याक्सिमाइज गर्नुहोस्"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"विन्डोको आकार रिस्टोर गर्नुहोस्"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"एपको विन्डो मिनिमाइज गर्नुहोस्"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"एपको विन्डो बन्द गर्नुहोस्"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"डिफल्ट सेटिङअनुसार खोल्नुहोस्"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"यो एपका वेब लिंकहरू खोल्ने तरिका छनौट गर्नुहोस्"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"एपमा"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 4234ddf..7178e41 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Linkerscherm 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Linkerscherm 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Rechterscherm op volledig scherm"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Apps bovenaan en onderaan omwisselen"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Apps links en rechts omwisselen"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Bovenste scherm op volledig scherm"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Bovenste scherm 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bovenste scherm 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Het app-menu vind je hier"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Ga naar de desktopweergave om meerdere apps tegelijk te openen"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ga wanneer je wilt terug naar volledig scherm vanuit het app-menu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zie en doe meer"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Sleep een andere app hier naartoe om het scherm te splitsen"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Opnieuw opstarten"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Niet opnieuw tonen"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Dubbeltik om\ndeze app te verplaatsen"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> maximaliseren"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> herstellen"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> minimaliseren"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> sluiten"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Terug"</string>
     <string name="handle_text" msgid="4419667835599523257">"App-handgreep"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"App-icoon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Volledig scherm"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Desktopweergave"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Gesplitst scherm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Meer"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Zwevend"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Beeldverhouding wijzigen"</string>
     <string name="close_text" msgid="4986518933445178928">"Sluiten"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktopweergave)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Formaat aanpassen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Kan de app niet hierheen verplaatsen"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Formaat van venster naar links aanpassen"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Formaat van venster naar rechts aanpassen"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Formaat van venster maximaliseren of herstellen"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Formaat van app-venster maximaliseren"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Vensterformaat herstellen"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"App-venster minimaliseren"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"App-venster sluiten"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Instellingen voor Standaard openen"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Kies hoe je weblinks voor deze app wilt openen"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In de app"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 62ee4a0..19cc8ced 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -100,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ଆପ ମେନୁ ଏଠାରେ ମିଳିପାରିବ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ଏକାଠି ଏକାଧିକ ଆପ୍ସ ଖୋଲିବାକୁ ଡେସ୍କଟପ ଭ୍ୟୁରେ ପ୍ରବେଶ କରନ୍ତୁ"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ଆପ ମେନୁରୁ ଯେ କୌଣସି ସମୟରେ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ଫେରନ୍ତୁ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ଦେଖନ୍ତୁ ଏବଂ ଆହୁରି ଅନେକ କିଛି କରନ୍ତୁ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ପାଇଁ ଅନ୍ୟ ଏକ ଆପକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string>
@@ -121,7 +122,8 @@
     <string name="handle_text" msgid="4419667835599523257">"ଆପର ହେଣ୍ଡେଲ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ଆପ ଆଇକନ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ପୂର୍ଣ୍ଣସ୍କ୍ରିନ"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ଡେସ୍କଟପ ଭ୍ୟୁ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ଅଧିକ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ଫ୍ଲୋଟ"</string>
@@ -134,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ଡେସ୍କଟପ ଭ୍ୟୁ)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ରିସାଇଜ କରନ୍ତୁ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ଆପକୁ ଏଠାକୁ ମୁଭ କରାଯାଇପାରିବ ନାହିଁ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 4dde6d2..7e89290 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -100,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ਐਪ ਮੀਨੂ ਇੱਥੇ ਮਿਲ ਸਕਦਾ ਹੈ"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"ਕਈ ਐਪਾਂ ਨੂੰ ਇਕੱਠੇ ਖੋਲ੍ਹਣ ਲਈ ਡੈਸਕਟਾਪ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ਐਪ ਮੀਨੂ ਤੋਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਵਾਪਸ ਜਾਓ"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ਦੇਖੋ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਕਰੋ"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੇ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਵਿੱਚ ਘਸੀਟੋ"</string>
@@ -121,7 +122,8 @@
     <string name="handle_text" msgid="4419667835599523257">"ਐਪ ਹੈਂਡਲ"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ਐਪ ਪ੍ਰਤੀਕ"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ਪੂਰੀ-ਸਕ੍ਰੀਨ"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ਡੈਸਕਟਾਪ ਦ੍ਰਿਸ਼"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"ਹੋਰ"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ਫ਼ਲੋਟ"</string>
@@ -134,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string>
     <string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ਡੈਸਕਟਾਪ ਦ੍ਰਿਸ਼)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ਆਕਾਰ ਬਦਲੋ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ਐਪ ਨੂੰ ਇੱਥੇ ਨਹੀਂ ਲਿਜਾਇਆ ਜਾ ਸਕਦਾ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 59deb01..58a691f 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% lewej części ekranu"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% lewej części ekranu"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Prawa część ekranu na pełnym ekranie"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Zamień aplikację na górze z tą na dole"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Zamień aplikację po lewej z tą po prawej"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Górna część ekranu na pełnym ekranie"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% górnej części ekranu"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% górnej części ekranu"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Tu znajdziesz menu aplikacji"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Aby otworzyć kilka aplikacji jednocześnie, przejdź do widoku pulpitu"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Z menu aplikacji w każdej chwili możesz wrócić do pełnego ekranu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobacz i zrób więcej"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Aby podzielić ekran, przeciągnij drugą aplikację"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Uruchom ponownie"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Nie pokazuj ponownie"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Aby przenieść aplikację,\nkliknij dwukrotnie"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maksymalizuj okno aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Przywróć rozmiar okna aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimalizuj okno aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Zamknij <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Wstecz"</string>
     <string name="handle_text" msgid="4419667835599523257">"Uchwyt aplikacji"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacji"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Pełny ekran"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Wersja na komputery"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Podzielony ekran"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Więcej"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Pływające"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmień format obrazu"</string>
     <string name="close_text" msgid="4986518933445178928">"Zamknij"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zamknij menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (wersja na komputery)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Zmień rozmiar"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Nie można przenieść aplikacji tutaj"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Zmień rozmiar okna do lewej"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Zmień rozmiar okna do prawej"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Zmaksymalizuj lub przywróć rozmiar okna"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maksymalizuj rozmiar okna aplikacji"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Przywróć rozmiar okna"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimalizuj okno aplikacji"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Zamknij okno aplikacji"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ustawienia domyślnego otwierania"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Wybierz, gdzie chcesz otwierać linki z tej aplikacji"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"W aplikacji"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 593f830..2d9bc2b 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Esquerda a 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Esquerda a 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Lado direito em tela cheia"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Trocar o app de cima pelo de baixo"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Trocar o app da esquerda pelo da direita"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Parte superior em tela cheia"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Parte superior a 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Parte superior a 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu do app está aqui"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Acesse a versão para computadores para abrir vários apps ao mesmo tempo"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volte para a tela cheia a qualquer momento no menu do app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outro app para dividir a tela"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Não mostrar novamente"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Toque duas vezes para\nmover o app"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restaurar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Fechar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Voltar"</string>
     <string name="handle_text" msgid="4419667835599523257">"Identificador do app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ícone do app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Versão para computadores"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string>
     <string name="close_text" msgid="4986518933445178928">"Fechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (versão para computadores)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionar"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Redimensionar janela para a esquerda"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Redimensionar janela para a direita"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximizar ou restaurar o tamanho da janela"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximizar o tamanho da janela do app"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restaurar o tamanho da janela"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimizar janela do app"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Fechar janela do app"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configurações \"Abrir por padrão\""</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escolha como abrir links da Web para este app"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"No app"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index fcf5916..765a207 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% no ecrã esquerdo"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% no ecrã esquerdo"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Ecrã direito inteiro"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Trocar app superior pela inferior"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Trocar app da esquerda pela da direita"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ecrã superior inteiro"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% no ecrã superior"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% no ecrã superior"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu da app pode ser encontrado aqui"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Entre na vista de computador para abrir várias apps em conjunto"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Regresse ao ecrã inteiro em qualquer altura a partir do menu da app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outra app para usar o ecrã dividido"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Não mostrar de novo"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Toque duas vezes\npara mover esta app"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restaurar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Fechar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Anterior"</string>
     <string name="handle_text" msgid="4419667835599523257">"Indicador da app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ícone da app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Ecrã inteiro"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Vista de computador"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Ecrã dividido"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flutuar"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Alterar formato"</string>
     <string name="close_text" msgid="4986518933445178928">"Fechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (vista de computador)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar ecrã"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionar"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover a app para aqui"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Redimensionar janela para a esquerda"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Redimensionar janela para a direita"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximizar ou restaurar tamanho da janela"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximizar tamanho da janela da app"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restaurar tamanho da janela"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimizar janela da app"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Fechar janela da app"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Definições de Abrir por predefinição"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escolha como abrir links da Web para esta app"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Na app"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 593f830..2d9bc2b 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Esquerda a 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Esquerda a 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Lado direito em tela cheia"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Trocar o app de cima pelo de baixo"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Trocar o app da esquerda pelo da direita"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Parte superior em tela cheia"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Parte superior a 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Parte superior a 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu do app está aqui"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Acesse a versão para computadores para abrir vários apps ao mesmo tempo"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volte para a tela cheia a qualquer momento no menu do app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outro app para dividir a tela"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reiniciar"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Não mostrar novamente"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Toque duas vezes para\nmover o app"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restaurar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimizar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Fechar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Voltar"</string>
     <string name="handle_text" msgid="4419667835599523257">"Identificador do app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ícone do app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Versão para computadores"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string>
     <string name="close_text" msgid="4986518933445178928">"Fechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (versão para computadores)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionar"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Redimensionar janela para a esquerda"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Redimensionar janela para a direita"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximizar ou restaurar o tamanho da janela"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximizar o tamanho da janela do app"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restaurar o tamanho da janela"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimizar janela do app"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Fechar janela do app"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configurações \"Abrir por padrão\""</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escolha como abrir links da Web para este app"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"No app"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 81db82a..68d045f 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Partea stângă: 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Partea stângă: 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Partea dreaptă pe ecran complet"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Schimbă aplicația de sus cu cea de jos"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Schimbă aplicația din stânga cu cea din dreapta"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Partea de sus pe ecran complet"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Partea de sus: 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Partea de sus: 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ai remediat problema?\nAtinge pentru a reveni"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu ai probleme cu camera foto? Atinge pentru a închide."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meniul aplicației poate fi găsit aici"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Accesează afișarea pe desktop pentru a deschide mai multe aplicații simultan"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revino oricând la ecranul complet din meniul aplicației"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vezi și fă mai multe"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trage în altă aplicație pentru a folosi ecranul împărțit"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Repornește"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Nu mai afișa"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Atinge de două ori\nca să muți aplicația"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximizează <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restabilește <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimizează <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Închide <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Înapoi"</string>
     <string name="handle_text" msgid="4419667835599523257">"Handle de aplicație"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Pictograma aplicației"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Ecran complet"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Afișare pe desktop"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Ecran împărțit"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mai multe"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Flotantă"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Schimbă raportul de dimensiuni"</string>
     <string name="close_text" msgid="4986518933445178928">"Închide"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (afișare pe desktop)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizează fereastra"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionează"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplicația nu poate fi mutată aici"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Redimensionează fereastra la stânga"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Redimensionează fereastra la dreapta"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximizează sau restabilește dimensiunea ferestrei"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximizează dimensiunea ferestrei aplicației"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restabilește dimensiunea ferestrei"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimizează fereastra aplicației"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Închide fereastra aplicației"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Setări de deschidere în mod prestabilit"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Alege modul de deschidere a linkurilor web pentru aplicație"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"În aplicație"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index aa6f484..8e683b8 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Левый на 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Левый на 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Правый во весь экран"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Поменять местами приложения сверху и снизу"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Поменять местами приложения слева и справа"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Верхний во весь экран"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Верхний на 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Верхний на 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Здесь вы найдете меню приложения"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Чтобы открыть сразу несколько приложений, перейдите в режим компьютера"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вернуться в полноэкранный режим можно из меню приложения"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Выполняйте несколько задач одновременно"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Перетащите сюда другое приложение, чтобы использовать разделение экрана."</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Перезапустить"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Больше не показывать"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Дважды нажмите, чтобы\nпереместить приложение."</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Развернуть окно приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Восстановить окно приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Свернуть окно приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Закрыть окно приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
     <string name="handle_text" msgid="4419667835599523257">"Обозначение приложения"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Значок приложения"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Полноэкранный режим"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Версия для ПК"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Разделить экран"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Ещё"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Плавающее окно"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Изменить соотношение сторон"</string>
     <string name="close_text" msgid="4986518933445178928">"Закрыть"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Закрыть меню"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (версия для ПК)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Развернуть на весь экран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Изменить размер"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложение нельзя сюда переместить"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Растянуть окно влево"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Растянуть окно вправо"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Развернуть окно или восстановить его размер"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Развернуть окно приложения"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Восстановить размер окна"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Свернуть окно приложения"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Закрыть окно приложения"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Настройки, регулирующие, как по умолчанию открываются ссылки"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Выберите, где будут открываться ссылки из этого приложения"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"В приложении"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index efa978a..6d54839 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"වම් 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"වම් 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"දකුණු පූර්ණ තිරය"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"ඉහළ යෙදුම පහළ සමග මාරු කරන්න"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"වම් යෙදුම දකුණ සමග මාරු කරන්න"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ඉහළම පූර්ණ තිරය"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ඉහළම 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ඉහළම 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්‍රතිවර්තනය කිරීමට තට්ටු කරන්න"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"යෙදුම් මෙනුව මෙතැනින් සොයා ගත හැක"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"යෙදුම් කිහිපයක් එකට විවෘත කිරීමට ඩෙස්ක්ටොප් දසුනට ඇතුළු වන්න"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"යෙදුම් මෙනුවෙන් ඕනෑම වේලාවක පූර්ණ තිරය වෙත ආපසු යන්න"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"බලන්න සහ තවත් දේ කරන්න"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"බෙදුම් තිරය සඳහා වෙනත් යෙදුමකට අදින්න"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"යළි අරඹන්න"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"නැවත නොපෙන්වන්න"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"මෙම යෙදුම ගෙන යාමට\nදෙවරක් තට්ටු කරන්න"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> විහිදන්න"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> ප්‍රතිසාධනය කරන්න"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> කුඩා කරන්න"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> වසන්න"</string>
     <string name="back_button_text" msgid="1469718707134137085">"ආපසු"</string>
     <string name="handle_text" msgid="4419667835599523257">"යෙදුම් හසුරුව"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"යෙදුම් නිරූපකය"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"පූර්ණ තිරය"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ඩෙස්ක්ටොප් දසුන"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"බෙදුම් තිරය"</string>
     <string name="more_button_text" msgid="3655388105592893530">"තව"</string>
     <string name="float_button_text" msgid="9221657008391364581">"පාවෙන"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"දර්ශන අනුපාතය වෙනස් කරන්න"</string>
     <string name="close_text" msgid="4986518933445178928">"වසන්න"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ඩෙස්ක්ටොප් දසුන)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"තිරය උපරිම කරන්න"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ප්‍රතිප්‍රමාණය කරන්න"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"යෙදුම මෙතැනට ගෙන යා නොහැක"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"කවුළුව වමට ප්‍රතිප්‍රමාණ කරන්න"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"කවුළුව දකුණට ප්‍රතිප්‍රමාණ කරන්න"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"කවුළු ප්‍රමාණය උපරිම කරන්න හෝ ප්‍රතිසාධනය කරන්න"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"යෙදුම් කවුළු ප්‍රමාණය උපරිම කරන්න"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"කවුළු ප්‍රමාණය ප්‍රතිසාධනය කරන්න"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"යෙදුම් කවුළුව අවම කරන්න"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"යෙදුම් කවුළුව වසන්න"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"පෙරනිමි සැකසීම් මඟින් විවෘත කරන්න"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"මෙම යෙදුම සඳහා වෙබ් සබැඳි විවෘත කරන ආකාරය තෝරා ගන්න"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"යෙදුම තුළ"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index fac26b4..c04b038 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ľavá – 50 %"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ľavá – 30 %"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pravá– na celú obrazovku"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Vymeniť hornú aplikáciu za dolnú"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Vymeniť ľavú aplikáciu za pravú"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Horná – na celú obrazovku"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Horná – 70 %"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Horná – 50 %"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ponuku aplikácie nájdete tu"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Prejdite do zobrazenia v počítači a otvorte viac aplikácií naraz"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Z ponuky aplikácie sa môžete kedykoľvek vrátiť na celú obrazovku"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobrazte si a zvládnite toho viac"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Rozdelenú obrazovku môžete použiť presunutím do inej aplikácie"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Reštartovať"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Už nezobrazovať"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Túto aplikáciu\npresuniete dvojitým klepnutím"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maximalizovať <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Obnoviť <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimalizovať <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Zavrieť <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Späť"</string>
     <string name="handle_text" msgid="4419667835599523257">"Rukoväť aplikácie"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikácie"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Zobrazenie v počítači"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Rozdelená obrazovka"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Viac"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Plávajúce"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmeniť pomer strán"</string>
     <string name="close_text" msgid="4986518933445178928">"Zavrieť"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (zobrazenie v počítači)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovať obrazovku"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Zmeniť veľkosť"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikácia sa sem nedá presunúť"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Zmeniť veľkosť okna vľavo"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Zmeniť veľkosť okna vpravo"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximalizovať alebo obnoviť veľkosť okna"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximalizovať veľkosť okna aplikácie"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Obnoviť veľkosť okna"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimalizovať okno aplikácie"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Zavrieť okno aplikácie"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvárať podľa predvolených nastavení"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Vyberte, ako sa majú v tejto aplikácii otvárať webové odkazy"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikácii"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index f8dacc4..22d7bfe 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Levi 50 %"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Levi 30 %"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Desni v celozaslonski način"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Zamenjava zgornje aplikacije s spodnjo"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Zamenjava leve aplikacije z desno"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Zgornji v celozaslonski način"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Zgornji 70 %"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Zgornji 50 %"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meni aplikacije najdete tukaj"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Preklopite v pogled za namizni računalnik, če želite odpreti več aplikacij hkrati"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"V meniju aplikacije se lahko kadar koli vrnete v celozaslonski način"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Oglejte si in naredite več"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Za razdeljeni zaslon povlecite sem še eno aplikacijo."</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Znova zaženi"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ne prikaži več"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Dvakrat se dotaknite\nza premik te aplikacije"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Povečanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Obnovitev aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Pomanjšava aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Zapiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Nazaj"</string>
     <string name="handle_text" msgid="4419667835599523257">"Identifikator aplikacije"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Celozaslonsko"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Pogled za namizni računalnik"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Razdeljen zaslon"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Več"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Lebdeče"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Sprememba razmerja stranic"</string>
     <string name="close_text" msgid="4986518933445178928">"Zapri"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (pogled za namizni računalnik)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Spremeni velikost"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacije ni mogoče premakniti sem"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Sprememba velikosti okna na levi"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Sprememba velikosti okna na desni"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Povečava ali obnovitev velikosti okna"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Povečanje velikosti okna aplikacije"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Obnovitev velikosti okna"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Pomanjšava okna aplikacije"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Zapiranje okna aplikacije"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Nastavitve privzetega odpiranja"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Izberite način odpiranja spletnih povezav za to aplikacijo"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikaciji"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 3e88ed1..9330ec3 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Majtas 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Majtas 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Ekrani i plotë djathtas"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Ndërro aplikacionin lart me atë poshtë"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Ndërro aplikacionin majtas me atë djathtas"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ekrani i plotë lart"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Lart 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Lart 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menyja e aplikacioneve mund të gjendet këtu"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Kalo te pamja e desktopit për të hapur disa aplikacione së bashku"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kthehu tek ekrani i plotë në çdo kohë nga menyja e aplikacioneve"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Shiko dhe bëj më shumë"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Zvarrite në një aplikacion tjetër për ekranin e ndarë"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Rinis"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Mos e shfaq përsëri"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Trokit dy herë për të\nlëvizur këtë aplikacion"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Maksimizo \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Restauro \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimizo \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Mbyll \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="back_button_text" msgid="1469718707134137085">"Pas"</string>
     <string name="handle_text" msgid="4419667835599523257">"Emërtimi i aplikacionit"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ikona e aplikacionit"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Ekrani i plotë"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Pamja e desktopit"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Ekrani i ndarë"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Më shumë"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Pluskuese"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ndrysho raportin e pamjes"</string>
     <string name="close_text" msgid="4986518933445178928">"Mbyll"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Pamja e desktopit)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizo ekranin"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ndrysho përmasat"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacioni nuk mund të zhvendoset këtu"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Ndrysho përmasat e dritares në të majtë"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Ndrysho përmasat e dritares në të djathtë"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maksimizo ose restauro madhësinë e dritares"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maksimizo madhësinë e dritares së aplikacionit"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Restauro madhësinë e dritares"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimizo dritaren e aplikacionit"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Mbyll dritaren e aplikacionit"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Hap sipas cilësimeve të parazgjedhura"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Zgjidh si do t\'i hapësh lidhjet e uebit për këtë aplikacion"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Në aplikacion"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 287d34f..5bdad15 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -100,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Мени апликације можете да пронађете овде"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Уђите у приказ за рачунаре да бисте истовремено отворили више апликација"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вратите се на цео екран било када из менија апликације"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Видите и урадите више"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Превуците другу апликацију да бисте користили подељени екран"</string>
@@ -121,7 +122,8 @@
     <string name="handle_text" msgid="4419667835599523257">"Идентификатор апликације"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Икона апликације"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Преко целог екрана"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Приказ за рачунаре"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Подељени екран"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Још"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Плутајуће"</string>
@@ -134,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени размеру"</string>
     <string name="close_text" msgid="4986518933445178928">"Затворите"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (приказ за рачунаре)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Прилагоди"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликација не може да се премести овде"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 0105e15..40a0a5a 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vänster 50 %"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vänster 30 %"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Helskärm på höger skärm"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Byt plats på den översta och understa appen"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Byt plats på den vänstra och högra appen"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Helskärm på övre skärm"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Övre 70 %"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Övre 50 %"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Appmenyn finns här"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Starta datorvyn för att öppna flera appar samtidigt"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Återgå till helskärm när som helst från appmenyn"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se och gör mer"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dra till en annan app för att dela upp skärmen"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Starta om"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Visa inte igen"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Tryck snabbt två gånger\nför att flytta denna app"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Utöka <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Återställ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Minimera <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Stäng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Tillbaka"</string>
     <string name="handle_text" msgid="4419667835599523257">"Apphandtag"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Helskärm"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Datorvy"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Delad skärm"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Mer"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Svävande"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ändra bildformat"</string>
     <string name="close_text" msgid="4986518933445178928">"Stäng"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (datorvy)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ändra storlek"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Det går inte att flytta appen hit"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Ändra storlek på fönstret åt vänster"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Ändra storlek på fönstret åt höger"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Maximera eller återställ fönsterstorleken"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Maximera appfönstrets storlek"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Återställ fönsterstorlek"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Minimera appfönstret"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Stäng appfönstret"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Inställningar för Öppna som standard"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Välj hur webblänkar ska öppnas för den här appen"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index b4415cb..20429e1 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kushoto 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kushoto 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Skrini nzima ya kulia"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Badilisha nafasi ya programu ya juu na ya chini"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Badilisha nafasi ya programu ya kulia na ya kushoto"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Skrini nzima ya juu"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Juu 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Juu 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Unaweza kupata menyu ya programu hapa"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Tumia mwonekano wa kompyuta ili ufungue programu nyingi pamoja"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Rudi kwenye skrini nzima wakati wowote ukitumia menyu ya programu"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Angalia na ufanye zaidi"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Buruta katika programu nyingine ili utumie skrini iliyogawanywa"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Zima kisha uwashe"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Usionyeshe tena"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Gusa mara mbili ili\nusogeze programu hii"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Panua <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Rejesha <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Punguza <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Funga <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Rudi nyuma"</string>
     <string name="handle_text" msgid="4419667835599523257">"Utambulisho wa programu"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Aikoni ya Programu"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Skrini nzima"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Mwonekano wa Kompyuta"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Gawa Skrini"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Zaidi"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Inayoelea"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Badilisha uwiano"</string>
     <string name="close_text" msgid="4986518933445178928">"Funga"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Mwonekano wa Kompyuta)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Panua Dirisha kwenye Skrini"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Badilisha ukubwa"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Imeshindwa kuhamishia programu hapa"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Badilisha ukubwa wa dirisha kushoto"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Badilisha ukubwa wa dirisha kulia"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Panua au urejeshe ukubwa wa dirisha"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Panua ukubwa wa dirisha la programu"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Rejesha ukubwa wa dirisha"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Punguza dirisha la programu"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Funga dirisha la programu"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Fungua kwa mipangilio chaguomsingi"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Chagua jinsi ya kufungua viungo vya wavuti vya programu hii"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Kwenye programu"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index ccc9f94..74ecfdc 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"இடது புறம் 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"இடது புறம் 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"வலது புறம் முழுத் திரை"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"மேலுள்ள ஆப்ஸைக் கீழுள்ள ஆப்ஸ் கொண்டு மாற்றும்"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"இடதுபுற ஆப்ஸை வலதுபுற ஆப்ஸ் கொண்டு மாற்றும்"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"மேற்புறம் முழுத் திரை"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"மேலே 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"மேலே 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ஆப்ஸ் மெனுவை இங்கே பார்க்கலாம்"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"பல ஆப்ஸை ஒன்றாகத் திறக்க டெஸ்க்டாப் காட்சிக்குச் செல்லலாம்"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ஆப்ஸ் மெனுவிலிருந்து எப்போது வேண்டுமானாலும் முழுத்திரைக்குத் திரும்பலாம்"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"பலவற்றைப் பார்த்தல் மற்றும் செய்தல்"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"திரைப் பிரிப்புக்கு மற்றொரு ஆப்ஸை இழுக்கலாம்"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"மீண்டும் தொடங்கு"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"மீண்டும் காட்டாதே"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"இந்த ஆப்ஸை நகர்த்த\nஇருமுறை தட்டவும்"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸைப் பெரிதாக்கும்"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸை மீட்டெடுக்கும்"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸைச் சிறிதாக்கும்"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸை மூடும்"</string>
     <string name="back_button_text" msgid="1469718707134137085">"பின்செல்லும்"</string>
     <string name="handle_text" msgid="4419667835599523257">"ஆப்ஸ் ஹேண்டில்"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ஆப்ஸ் ஐகான்"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"முழுத்திரை"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"டெஸ்க்டாப் காட்சி"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"திரையைப் பிரிக்கும்"</string>
     <string name="more_button_text" msgid="3655388105592893530">"கூடுதல் விருப்பத்தேர்வுகள்"</string>
     <string name="float_button_text" msgid="9221657008391364581">"மிதக்கும் சாளரம்"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"தோற்ற விகிதத்தை மாற்று"</string>
     <string name="close_text" msgid="4986518933445178928">"மூடும்"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (டெஸ்க்டாப் காட்சி)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"திரையைப் பெரிதாக்கு"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"அளவை மாற்று"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ஆப்ஸை இங்கே நகர்த்த முடியாது"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"சாளரத்தை இடதுபுறமாக அளவு மாற்றும்"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"சாளரத்தை வலதுபுறமாக அளவு மாற்றும்"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"சாளரத்தின் அளவைப் பெரிதாக்கும்/மீட்டெடுக்கும்"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"ஆப்ஸ் சாளரத்தின் அளவைப் பெரிதாக்கும்"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"சாளரத்தின் அளவை மீட்டெடுக்கும்"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"ஆப்ஸ் சாளரத்தைச் சிறிதாக்கும்"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"ஆப்ஸ் சாளரத்தை மூடும்"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"இயல்பாக அமைப்புகளைத் திறக்கும்"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"இந்த ஆப்ஸில் வலை இணைப்புகளைத் திறக்கும் வழிமுறையைத் தேர்வுசெய்யுங்கள்"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ஆப்ஸில்"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 96ce40a..f46c9b6 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ఎడమవైపు 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ఎడమవైపు 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"కుడివైపు ఫుల్-స్క్రీన్‌"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"పైన ఉన్న యాప్‌ను కింద ఉన్న యాప్‌తో మార్చండి"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"ఎడమ వైపు ఉన్న యాప్‌ను కుడి వైపు ఉన్న యాప్‌తో మార్చండి"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ఎగువ ఫుల్-స్క్రీన్‌"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ఎగువ 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ఎగువ 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"యాప్ మెనూను ఇక్కడ పొందవచ్చు"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"పలు యాప్‌లను ఒకేసారి తెరవడానికి డెస్క్‌టాప్ వీక్షణకు ఎంటర్ అవ్వండి"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"యాప్ మెనూ నుండి ఏ సమయంలోనైనా ఫుల్ స్క్రీన్‌కు తిరిగి రండి"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"చూసి, మరిన్ని చేయండి"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"స్ప్లిట్ స్క్రీన్ కోసం మరొక యాప్‌లోకి లాగండి"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"రీస్టార్ట్ చేయండి"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"మళ్లీ చూపవద్దు"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"ఈ యాప్‌ను తరలించడానికి\nడబుల్-ట్యాప్ చేయండి"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g>ను పెద్దదిగా చేయండి"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g>ను రీస్టోర్ చేయండి"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g>ను చిన్నదిగా చేయండి"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g>‌ను మూసివేయండి"</string>
     <string name="back_button_text" msgid="1469718707134137085">"వెనుకకు"</string>
     <string name="handle_text" msgid="4419667835599523257">"యాప్ హ్యాండిల్"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"యాప్ చిహ్నం"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"ఫుల్-స్క్రీన్"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"డెస్క్‌టాప్ వీక్షణ"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"స్ప్లిట్ స్క్రీన్"</string>
     <string name="more_button_text" msgid="3655388105592893530">"మరిన్ని"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ఫ్లోట్"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ఆకార నిష్పత్తిని మార్చండి"</string>
     <string name="close_text" msgid="4986518933445178928">"మూసివేయండి"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"మెనూను మూసివేయండి"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (డెస్క్‌టాప్ వీక్షణ)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"స్క్రీన్ సైజ్‌ను పెంచండి"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"సైజ్ మార్చండి"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"యాప్‌ను ఇక్కడకి తరలించడం సాధ్యం కాదు"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"విండో ఎడమ వైపునకు సైజ్‌ను మార్చండి"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"విండో కుడి వైపునకు సైజ్‌ను మార్చండి"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"విండో సైజ్‌ను మ్యాగ్జిమైజ్ చేయండి లేదా రీస్టోర్ చేయండి"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"యాప్ విండో సైజ్‌ను పెద్దదిగా చేయండి"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"విండో సైజ్‌ను రీస్టోర్ చేయండి"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"యాప్ విండోను చిన్నదిగా చేయండి"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"యాప్ విండోను మూసివేయండి"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"ఆటోమేటిక్ సెట్టింగ్‌ల ద్వారా తెరవండి"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ఈ యాప్‌నకు సంబంధించిన వెబ్ లింక్‌లను ఎలా తెరవాలో ఎంచుకోండి"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"యాప్‌లో"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 594ee6c9..60632ad 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -100,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ดูเมนูแอปที่นี่ได้"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"เข้าสู่มุมมองบนเดสก์ท็อปเพื่อเปิดหลายแอปพร้อมกัน"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"กลับไปที่โหมดเต็มหน้าจอได้ทุกเมื่อจากเมนูแอป"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"รับชมและทำสิ่งต่างๆ ได้มากขึ้น"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ลากไปไว้ในแอปอื่นเพื่อแยกหน้าจอ"</string>
@@ -121,7 +122,8 @@
     <string name="handle_text" msgid="4419667835599523257">"แฮนเดิลแอป"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ไอคอนแอป"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"เต็มหน้าจอ"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"มุมมองบนเดสก์ท็อป"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"แยกหน้าจอ"</string>
     <string name="more_button_text" msgid="3655388105592893530">"เพิ่มเติม"</string>
     <string name="float_button_text" msgid="9221657008391364581">"ล่องลอย"</string>
@@ -134,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"เปลี่ยนสัดส่วนการแสดงผล"</string>
     <string name="close_text" msgid="4986518933445178928">"ปิด"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ปิดเมนู"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (มุมมองบนเดสก์ท็อป)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ปรับขนาด"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ย้ายแอปมาที่นี่ไม่ได้"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index ad38be8..ae6df04 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Gawing 50% ang nasa kaliwa"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Gawing 30% ang nasa kaliwa"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"I-full screen ang nasa kanan"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Pagpalitin ang app sa itaas at ibaba"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Pagpalitin ang app sa kaliwa at kanan"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"I-full screen ang nasa itaas"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gawing 70% ang nasa itaas"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gawing 50% ang nasa itaas"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Makikita rito ang menu ng app"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Pumasok sa desktop view para magbukas ng maraming app nang sabay-sabay"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Bumalik sa full screen anumang oras mula sa menu ng app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Tumingin at gumawa ng higit pa"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Mag-drag ng isa pang app para sa split screen"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"I-restart"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Huwag nang ipakita ulit"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"I-double tap para\nilipat ang app na ito"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"I-maximize ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"I-restore ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"I-minimize ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Isara ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Bumalik"</string>
     <string name="handle_text" msgid="4419667835599523257">"Handle ng app"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Icon ng App"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Desktop View"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Split Screen"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Higit pa"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Float"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Baguhin ang aspect ratio"</string>
     <string name="close_text" msgid="4986518933445178928">"Isara"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Isara ang Menu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop View)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"I-resize"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Hindi mailipat dito ang app"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"I-resize pakaliwa ang window"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"I-resize pakanan ang window"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"I-maximize o i-restore ang laki ng window"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"I-maximize ang laki ng window ng app"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"I-restore ang laki ng window"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"I-minimize ang window ng app"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Isara ang window ng app"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buksan sa pamamagitan ng mga default na setting"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Piliin kung paano magbukas ng web link para sa app na ito"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Sa app"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 2bc0a96..116740e 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Solda %50"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Solda %30"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Sağda tam ekran"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Üstteki uygulamayı alttaki uygulamayla değiştir"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Soldaki uygulamayı sağdakiyle değiştir"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Üstte tam ekran"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Üstte %70"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Üstte %50"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Uygulama menüsünü burada bulabilirsiniz"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Birden fazla uygulamayı birlikte açmak için masaüstü görünümüne geçin"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Uygulama menüsünden dilediğiniz zaman tam ekrana dönebilirsiniz"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daha fazlasını görün ve yapın"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Bölünmüş ekran için başka bir uygulamayı sürükleyin"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Yeniden başlat"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Bir daha gösterme"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Bu uygulamayı taşımak için\niki kez dokunun"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasını büyüt"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasını geri yükle"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasını küçült"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasını kapat"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Geri"</string>
     <string name="handle_text" msgid="4419667835599523257">"Uygulama tanıtıcısı"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Uygulama Simgesi"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Masaüstü görünümü"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Bölünmüş Ekran"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Daha Fazla"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Havada Süzülen"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"En boy oranını değiştir"</string>
     <string name="close_text" msgid="4986518933445178928">"Kapat"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (masaüstü görünümü)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı Büyüt"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Yeniden boyutlandır"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Uygulama buraya taşınamıyor"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Pencereyi sola yeniden boyutlandır"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Pencereyi sağa yeniden boyutlandır"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Pencereyi ekranı kaplayacak şekilde büyüt veya önceki boyutuna döndür"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Uygulama penceresini ekranı kaplayacak şekilde büyüt"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Pencere boyutunu geri yükle"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Uygulama penceresini küçült"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Uygulama penceresini kapat"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Varsayılan olarak açma ayarları"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Bu uygulama için web bağlantılarının nasıl açılacağını seçin"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Uygulamada"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index c1aa82e..54af067 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ліве вікно на 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ліве вікно на 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Праве вікно на весь екран"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Поміняти місцями додатки зверху й знизу"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Поміняти місцями додатки ліворуч і праворуч"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Верхнє вікно на весь екран"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Верхнє вікно на 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Верхнє вікно на 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Тут ви знайдете меню додатка"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Щоб відкрити кілька додатків одночасно, перейдіть у режим робочого стола"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"З меню додатка можна будь-коли повернутися в повноекранний режим"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Більше простору та можливостей"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Щоб перейти в режим розділення екрана, перетягніть сюди інший додаток"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Перезапустити"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Більше не показувати"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Двічі торкніться, щоб\nперемістити цей додаток"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Розгорнути додаток <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Відновити додаток <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Згорнути додаток <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Закрити додаток <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Назад"</string>
     <string name="handle_text" msgid="4419667835599523257">"Дескриптор додатка"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Значок додатка"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"На весь екран"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Версія для ПК"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Розділити екран"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Більше"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Плаваюче вікно"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змінити формат"</string>
     <string name="close_text" msgid="4986518933445178928">"Закрити"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Закрити меню"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (версія для ПК)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Розгорнути екран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Змінити розмір"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Сюди не можна перемістити додаток"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Змінити розмір вікна ліворуч"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Змінити розмір вікна праворуч"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Розгорнути вікно або відновити його розмір"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Розгорнути вікно додатка"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Відновити розмір вікна"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Згорнути вікно додатка"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Закрити вікно додатка"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Налаштування \"Відкривати за умовчанням\""</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Виберіть, як відкривати вебпосилання в цьому додатку"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У додатку"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 1afb48d..635bc40 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"بائیں %50"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"بائیں %30"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"دائیں فل اسکرین"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"اوپری ایپ کو نیچے کے ساتھ سویپ کریں"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"بائیں ایپ کو دائیں کے ساتھ سویپ کریں"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"بالائی فل اسکرین"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"اوپر %70"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"اوپر %50"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ایپ کا مینو یہاں پایا جا سکتا ہے"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"متعدد ایپس کو ایک ساتھ کھولنے کے لیے ڈیسک ٹاپ منظر میں داخل ہوں"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ایپ مینو سے کسی بھی وقت فُل اسکرین پر واپس جائیں"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"دیکھیں اور بہت کچھ کریں"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"اسپلٹ اسکرین کے ليے دوسری ایپ میں گھسیٹیں"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"ری اسٹارٹ کریں"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"دوبارہ نہ دکھائیں"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"اس ایپ کو منتقل کرنے کیلئے\nدو بار تھپتھپائیں"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> بڑا کریں"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> کو بحال کریں"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> چھوٹا کریں"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> بند کریں"</string>
     <string name="back_button_text" msgid="1469718707134137085">"پیچھے"</string>
     <string name="handle_text" msgid="4419667835599523257">"ایپ ہینڈل"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"ایپ کا آئیکن"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"مکمل اسکرین"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"ڈیسک ٹاپ منظر"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"اسپلٹ اسکرین"</string>
     <string name="more_button_text" msgid="3655388105592893530">"مزید"</string>
     <string name="float_button_text" msgid="9221657008391364581">"فلوٹ"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تناسبی شرح کو تبدیل کریں"</string>
     <string name="close_text" msgid="4986518933445178928">"بند کریں"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"مینیو بند کریں"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> (ڈیسک ٹاپ منظر)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"اسکرین کو بڑا کریں"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"سائز تبدیل کریں"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ایپ کو یہاں منتقل نہیں کیا جا سکتا"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"دائیں طرف ونڈو کا سائز تبدیل کریں"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"ونڈو کا سائز بائیں طرف تبدیل کریں"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"ونڈو کا سائز زیادہ سے زیادہ یا بحال کریں"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"ایپ ونڈو سائز بڑا کریں"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"ونڈو سائز بحال کریں"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"ایپ ونڈو چھوٹی کریں"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"ایپ ونڈو بند کریں"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"بطور ڈیفالٹ ترتیبات کھولیں"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"اس ایپ کے لیے ویب لنکس کھولنے کا طریقہ منتخب کریں"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ایپ میں"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 04fd429..6ce2bd8 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Chapda 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Chapda 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"O‘ngda to‘liq ekran"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Tepadagi ilovani pastkisi bilan almashtirish"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Chap ilovani oʻngdagisi bilan almashtirish"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Tepada to‘liq ekran"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Tepada 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Tepada 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ilova menyusi shu yerda chiqadi"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Bir nechta ilovani birga ochish uchun kompyuter versiyasiga kiring"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ilova menyusi orqali istalganda butun ekranga qaytish mumkin"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Yana boshqa amallar"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Ekranni ikkiga ajratish uchun boshqa ilovani bu yerga torting"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Qaytadan"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Boshqa chiqmasin"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Bu ilovani siljitish uchun\nikki marta bosing"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Yoyish: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Tiklash: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Kichraytirish: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Yopish: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Orqaga"</string>
     <string name="handle_text" msgid="4419667835599523257">"Ilova identifikatori"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Ilova belgisi"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Butun ekran"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Desktop versiya"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Ekranni ikkiga ajratish"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Yana"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Pufakli"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tomonlar nisbatini oʻzgartirish"</string>
     <string name="close_text" msgid="4986518933445178928">"Yopish"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktop versiya)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Oʻlchamini oʻzgartirish"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ilova bu yerga surilmaydi"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Oyna oʻlchamini chapga oʻzgartirish"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Oyna oʻlchamini oʻngga oʻzgartirish"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Oyna oʻlchamini kengaytirish yoki asliga qaytarish"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Ilova oynasini kattartirish"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Oyna hajmini tiklash"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Ilova oynasini kichraytirish"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Ilova oynasini yopish"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Birlamchi sozlamalar asosida ochish"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Bu ilovalardagi veb havolalar qanday ochilishini tanlang"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Ilovada"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 169c2b7..67f80c1 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Trái 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Trái 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Toàn màn hình bên phải"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Hoán đổi ứng dụng ở trên cùng với ứng dụng ở dưới cùng"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Hoán đổi ứng dụng bên trái với ứng dụng bên phải"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Toàn màn hình phía trên"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Trên 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Trên 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Bạn có thể tìm thấy trình đơn ứng dụng tại đây"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Chuyển sang chế độ xem trên máy tính để bàn để mở nhiều ứng dụng cùng lúc"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Trở về chế độ toàn màn hình bất cứ lúc nào từ trình đơn ứng dụng"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Xem và làm được nhiều việc hơn"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Kéo một ứng dụng khác vào để chia đôi màn hình"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Khởi động lại"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Không hiện lại"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Nhấn đúp để\ndi chuyển ứng dụng này"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Phóng to <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Khôi phục <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Thu nhỏ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Đóng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Quay lại"</string>
     <string name="handle_text" msgid="4419667835599523257">"Ô điều khiển ứng dụng"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Biểu tượng ứng dụng"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Toàn màn hình"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Chế độ xem trên máy tính để bàn"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Chia đôi màn hình"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Tuỳ chọn khác"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Nổi"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Thay đổi tỷ lệ khung hình"</string>
     <string name="close_text" msgid="4986518933445178928">"Đóng"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Chế độ xem trên máy tính để bàn)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mở rộng màn hình"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Đổi kích thước"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Không di chuyển được ứng dụng đến đây"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Đổi kích thước và chuyển cửa sổ sang trái"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Đổi kích thước và chuyển cửa sổ sang phải"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Phóng to hoặc khôi phục kích thước cửa sổ"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Phóng to kích thước cửa sổ ứng dụng"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Khôi phục kích thước cửa sổ"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Thu nhỏ cửa sổ ứng dụng"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Đóng cửa sổ ứng dụng"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Mở các chế độ cài đặt theo mặc định"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Chọn cách mở đường liên kết trang web cho ứng dụng này"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Trong ứng dụng"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 942734a..b29711d 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"左侧 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"左侧 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"右侧全屏"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"将顶部应用与底部应用互换"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"将左侧应用与右侧应用互换"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"顶部全屏"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"顶部 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"顶部 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"您可以在此处找到应用菜单"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"进入桌面版视图可同时打开多个应用"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"随时从应用菜单返回全屏模式"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"查看和处理更多任务"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一个应用,即可使用分屏模式"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"重启"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"不再显示"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"点按两次\n即可移动此应用"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"将“<xliff:g id="APP_NAME">%1$s</xliff:g>”窗口最大化"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"恢复“<xliff:g id="APP_NAME">%1$s</xliff:g>”窗口大小"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"将“<xliff:g id="APP_NAME">%1$s</xliff:g>”窗口最小化"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"关闭“<xliff:g id="APP_NAME">%1$s</xliff:g>”"</string>
     <string name="back_button_text" msgid="1469718707134137085">"返回"</string>
     <string name="handle_text" msgid="4419667835599523257">"应用手柄"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"应用图标"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"全屏"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"桌面版视图"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"分屏"</string>
     <string name="more_button_text" msgid="3655388105592893530">"更多"</string>
     <string name="float_button_text" msgid="9221657008391364581">"悬浮"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"更改宽高比"</string>
     <string name="close_text" msgid="4986518933445178928">"关闭"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"关闭菜单"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g>(桌面版视图)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"最大化屏幕"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"调整大小"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"无法将应用移至此处"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"调整窗口大小并贴靠左侧"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"调整窗口大小并贴靠右侧"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"将窗口最大化或恢复大小"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"将应用窗口最大化"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"恢复窗口大小"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"将应用窗口最小化"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"关闭应用窗口"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"默认打开设置"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"选择如何打开此应用中的网页链接"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"在此应用内"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index f897922..74e6b5c 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"左邊 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"左邊 30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"右邊全螢幕"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"調換上下應用程式"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"調換左右應用程式"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"頂部全螢幕"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"頂部 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"頂部 50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可在這裡找到應用程式選單"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"進入桌面電腦檢視模式以同時開啟多個應用程式"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你可隨時從應用程式選單返回全螢幕"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一個應用程式即可分割螢幕"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"重新啟動"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"不要再顯示"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"輕按兩下\n即可移動此應用程式"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"將 <xliff:g id="APP_NAME">%1$s</xliff:g> 放到最大"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"還原 <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"將 <xliff:g id="APP_NAME">%1$s</xliff:g> 縮到最細"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"閂 <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"返去"</string>
     <string name="handle_text" msgid="4419667835599523257">"應用程式控點"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"應用程式圖示"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"全螢幕"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"桌面電腦檢視模式"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"分割螢幕"</string>
     <string name="more_button_text" msgid="3655388105592893530">"更多"</string>
     <string name="float_button_text" msgid="9221657008391364581">"浮動"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更長寬比"</string>
     <string name="close_text" msgid="4986518933445178928">"關閉"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (桌面電腦檢視模式)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"調整大小"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至這裡"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"將視窗移去左邊調整大小"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"將視窗移去右邊調整大小"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"將視窗放到最大或者還原視窗大小"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"將應用程式視窗放到最大"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"還原視窗大細"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"將應用程式視窗縮到最細"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"關閉應用程式視窗"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"採用預設設定打開"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"選擇此應用程式開啟網絡連結的方式"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"在應用程式內"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 3c6abec..575b217 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"以 50% 的螢幕空間顯示左側畫面"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"以 30% 的螢幕空間顯示左側畫面"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"以全螢幕顯示右側畫面"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"將頂端與底部的應用程式對調"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"將左側與右側的應用程式對調"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"以全螢幕顯示頂端畫面"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"以 70% 的螢幕空間顯示頂端畫面"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"以 50% 的螢幕空間顯示頂端畫面"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可以在這裡查看應用程式選單"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"進入電腦檢視畫面可以同時開啟多個應用程式"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你隨時可以從應用程式選單返回全螢幕模式"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖進另一個應用程式即可使用分割畫面模式"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"重新啟動"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"不要再顯示"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"輕觸兩下即可\n移動這個應用程式"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"最大化「<xliff:g id="APP_NAME">%1$s</xliff:g>」視窗"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"還原「<xliff:g id="APP_NAME">%1$s</xliff:g>」視窗"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"最小化「<xliff:g id="APP_NAME">%1$s</xliff:g>」視窗"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"關閉「<xliff:g id="APP_NAME">%1$s</xliff:g>」"</string>
     <string name="back_button_text" msgid="1469718707134137085">"返回"</string>
     <string name="handle_text" msgid="4419667835599523257">"應用程式控制代碼"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"應用程式圖示"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"全螢幕"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"電腦檢視畫面"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"分割畫面"</string>
     <string name="more_button_text" msgid="3655388105592893530">"更多"</string>
     <string name="float_button_text" msgid="9221657008391364581">"浮動"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更顯示比例"</string>
     <string name="close_text" msgid="4986518933445178928">"關閉"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (電腦檢視畫面)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"調整大小"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至此處"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"調整應用程式視窗大小並向左貼齊"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"調整應用程式視窗大小並向右貼齊"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"將視窗最大化或還原大小"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"最大化應用程式視窗"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"還原視窗大小"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"最小化應用程式視窗"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"關閉應用程式視窗"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"開啟連結的預設設定"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"選擇如何開啟這個應用程式的網頁連結"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"使用應用程式"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index b304299..30403cd 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -43,10 +43,8 @@
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kwesokunxele ngo-50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kwesokunxele ngo-30%"</string>
     <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Isikrini esigcwele esingakwesokudla"</string>
-    <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
-    <skip />
+    <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"Shintshanisa i-app ephezulu ngengaphansi"</string>
+    <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"Shintshanisa i-app engakwesokunxele naleyo engakwesokudla"</string>
     <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Isikrini esigcwele esiphezulu"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Okuphezulu okungu-70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Okuphezulu okungu-50%"</string>
@@ -102,7 +100,8 @@
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string>
     <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string>
     <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Imenyu ye-app ingatholakala lapha"</string>
-    <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="2523468503353474095">"Faka ukubuka kwedeskithophu ukuze uvule ama-app amaningi ndawonye"</string>
+    <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
+    <skip />
     <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Buyela esikrinini esigcwele noma nini ukusuka kumenyu ye-app"</string>
     <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Bona futhi wenze okuningi"</string>
     <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Hudula kwenye i-app mayelana nokuhlukanisa isikrini"</string>
@@ -115,19 +114,16 @@
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"Qala kabusha"</string>
     <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"Ungabonisi futhi"</string>
     <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"Thepha kabili ukuze\nuhambise le-app"</string>
-    <!-- no translation found for maximize_button_text (8106849394538234709) -->
-    <skip />
-    <!-- no translation found for restore_button_text (5377571986086775288) -->
-    <skip />
-    <!-- no translation found for minimize_button_text (5213953162664451152) -->
-    <skip />
-    <!-- no translation found for close_button_text (4544839489310949894) -->
-    <skip />
+    <string name="maximize_button_text" msgid="8106849394538234709">"Khulisa i-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="restore_button_text" msgid="5377571986086775288">"Buyisela i-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="minimize_button_text" msgid="5213953162664451152">"Nciphisa i-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="close_button_text" msgid="4544839489310949894">"Vala i-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="back_button_text" msgid="1469718707134137085">"Emuva"</string>
     <string name="handle_text" msgid="4419667835599523257">"Inkomba ye-App"</string>
     <string name="app_icon_text" msgid="2823268023931811747">"Isithonjana Se-app"</string>
     <string name="fullscreen_text" msgid="1162316685217676079">"Isikrini esigcwele"</string>
-    <string name="desktop_text" msgid="1582173066857454541">"Ukubuka Kwedeskithophu"</string>
+    <!-- no translation found for desktop_text (9058641752519570266) -->
+    <skip />
     <string name="split_screen_text" msgid="1396336058129570886">"Hlukanisa isikrini"</string>
     <string name="more_button_text" msgid="3655388105592893530">"Okwengeziwe"</string>
     <string name="float_button_text" msgid="9221657008391364581">"Iflowuthi"</string>
@@ -140,7 +136,8 @@
     <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Shintsha ukubukeka kwesilinganiselo"</string>
     <string name="close_text" msgid="4986518933445178928">"Vala"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Vala Imenyu"</string>
-    <string name="desktop_mode_app_header_chip_text" msgid="8300164817452574565">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Ukubuka Kwedeskithophu)"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Khulisa Isikrini Sifike Ekugcineni"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Shintsha usayizi"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"I-app ayikwazi ukuhanjiswa lapha"</string>
@@ -158,14 +155,10 @@
     <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"Shintsha usayizi wewindi ngakwesokunxele"</string>
     <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"Shintsha usayizi wewindi ngakwesokudla"</string>
     <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"Khulisa noma buyisela usayizi wewindi"</string>
-    <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
-    <skip />
-    <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
-    <skip />
+    <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"Khulisa usayizi wewindi le-app"</string>
+    <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"Buyisela usayizi wewindi"</string>
+    <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"Nciphisa iwindi le-app"</string>
+    <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"Vala iwindi le-app"</string>
     <string name="open_by_default_settings_text" msgid="2526548548598185500">"Vula amasethingi ngokuzenzakalela"</string>
     <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Khetha indlela yokuvula amalinki ewebhu ale app"</string>
     <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Ku-app"</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 60044ad..e1bf663 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -291,6 +291,9 @@
     <!-- Corner radius for expanded view drop target -->
     <dimen name="bubble_bar_expanded_view_drop_target_corner">28dp</dimen>
     <dimen name="bubble_bar_expanded_view_drop_target_padding">24dp</dimen>
+    <dimen name="bubble_bar_expanded_view_drop_target_padding_top">60dp</dimen>
+    <dimen name="bubble_bar_expanded_view_drop_target_padding_bottom">24dp</dimen>
+    <dimen name="bubble_bar_expanded_view_drop_target_padding_horizontal">48dp</dimen>
     <!-- Width of the box around bottom center of the screen where drag only leads to dismiss -->
     <dimen name="bubble_bar_dismiss_zone_width">192dp</dimen>
     <!-- Height of the box around bottom center of the screen where drag only leads to dismiss -->
@@ -437,9 +440,6 @@
     <!-- Height of button (32dp)  + 2 * margin (5dp each). -->
     <dimen name="freeform_decor_caption_height">42dp</dimen>
 
-    <!-- Height of desktop mode caption for freeform tasks. -->
-    <dimen name="desktop_mode_freeform_decor_caption_height">40dp</dimen>
-
     <!-- Height of desktop mode caption for fullscreen tasks. -->
     <dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen>
 
@@ -528,17 +528,21 @@
     <!-- The radius of the Maximize menu shadow. -->
     <dimen name="desktop_mode_maximize_menu_shadow_radius">8dp</dimen>
 
-    <!-- The width of the handle menu in desktop mode.  -->
-    <dimen name="desktop_mode_handle_menu_width">216dp</dimen>
+    <!-- The width of the handle menu in desktop mode plus the 2dp added for padding to account for
+         pill elevation. -->
+    <dimen name="desktop_mode_handle_menu_width">218dp</dimen>
 
-    <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each,
-         additional actions pill 208dp, plus 2dp spacing between them plus 4dp top padding.
-         52*3 + 52*4 + (4-1)*2 + 4 = 374 -->
-    <dimen name="desktop_mode_handle_menu_height">374dp</dimen>
+    <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each plus
+         additional actions pill 208dp plus 2dp spacing between them plus 4dp top padding
+         plus 2dp bottom padding: 52*3 + 52*4 + (4-1)*2 + 4 + 2 = 376 -->
+    <dimen name="desktop_mode_handle_menu_height">376dp</dimen>
 
     <!-- The elevation set on the handle menu pills. -->
     <dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
 
+    <!-- The padding added to account for the handle menu's pills' elevation. -->
+    <dimen name="desktop_mode_handle_menu_pill_elevation_padding">2dp</dimen>
+
     <!-- The height of the handle menu's "App Info" pill in desktop mode. -->
     <dimen name="desktop_mode_handle_menu_app_info_pill_height">52dp</dimen>
 
@@ -573,7 +577,10 @@
     <dimen name="desktop_mode_handle_menu_corner_radius">26dp</dimen>
 
     <!-- The radius of the caption menu icon. -->
-    <dimen name="desktop_mode_caption_icon_radius">32dp</dimen>
+    <dimen name="desktop_mode_caption_icon_radius">24dp</dimen>
+
+    <!-- The radius of the icon in the header menu's app info pill. -->
+    <dimen name="desktop_mode_handle_menu_icon_radius">32dp</dimen>
 
     <!-- The radius of the caption menu shadow. -->
     <dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 2179128..5ef8382 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -226,7 +226,7 @@
     <string name="windowing_app_handle_education_tooltip">The app menu can be found here</string>
 
     <!-- App handle education tooltip text for tooltip pointing to windowing image button -->
-    <string name="windowing_desktop_mode_image_button_education_tooltip">Enter desktop view to open multiple apps together</string>
+    <string name="windowing_desktop_mode_image_button_education_tooltip">Enter desktop windowing to open multiple apps together</string>
 
     <!-- App handle education tooltip text for tooltip pointing to app chip -->
     <string name="windowing_desktop_mode_exit_education_tooltip">Return to full screen anytime from the app menu</string>
@@ -293,7 +293,7 @@
     <!-- Accessibility text for the handle fullscreen button [CHAR LIMIT=NONE] -->
     <string name="fullscreen_text">Fullscreen</string>
     <!-- Accessibility text for the handle desktop button [CHAR LIMIT=NONE] -->
-    <string name="desktop_text">Desktop View</string>
+    <string name="desktop_text">Desktop windowing</string>
     <!-- Accessibility text for the handle split screen button [CHAR LIMIT=NONE] -->
     <string name="split_screen_text">Split Screen</string>
     <!-- Accessibility text for the handle more options button [CHAR LIMIT=NONE] -->
@@ -319,7 +319,7 @@
     <!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
     <string name="collapse_menu_text">Close Menu</string>
     <!-- Accessibility text for the App Header's App Chip [CHAR LIMIT=NONE] -->
-    <string name="desktop_mode_app_header_chip_text"><xliff:g id="app_name" example="Chrome">%1$s</xliff:g> (Desktop View)</string>
+    <string name="desktop_mode_app_header_chip_text"><xliff:g id="app_name" example="Chrome">%1$s</xliff:g> (Desktop windowing)</string>
     <!-- Maximize menu maximize button string. -->
     <string name="desktop_mode_maximize_menu_maximize_text">Maximize Screen</string>
     <!-- Maximize menu snap buttons string. -->
@@ -348,7 +348,7 @@
     <!-- Accessibility action replacement for caption handle app chip buttons [CHAR LIMIT=NONE] -->
     <string name="app_handle_chip_accessibility_announce">Open Menu</string>
     <!-- Accessibility action replacement for caption handle menu buttons [CHAR LIMIT=NONE] -->
-    <string name="app_handle_menu_accessibility_announce">Enter <xliff:g id="windowing_mode" example="Desktop View">%1$s</xliff:g></string>
+    <string name="app_handle_menu_accessibility_announce">Enter <xliff:g id="windowing_mode" example="Desktop windowing">%1$s</xliff:g></string>
     <!-- Accessibility action replacement for maximize menu enter snap left button [CHAR LIMIT=NONE] -->
     <string name="maximize_menu_talkback_action_snap_left_text">Resize window to left</string>
     <!-- Accessibility action replacement for maximize menu enter snap right button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 637b47a..5f1db83 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -45,6 +45,7 @@
         <item name="android:layout_height">52dp</item>
         <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
         <item name="android:drawableTint">@androidprv:color/materialColorOnSurface</item>
+        <item name="android:importantForAccessibility">no</item>
     </style>
 
     <style name="DesktopModeHandleMenuActionButtonImage">
diff --git a/libs/WindowManager/Shell/shared/res/values/dimen.xml b/libs/WindowManager/Shell/shared/res/values/dimen.xml
index f6a176f..3b504cf 100644
--- a/libs/WindowManager/Shell/shared/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/shared/res/values/dimen.xml
@@ -31,6 +31,7 @@
     <dimen name="drag_zone_desktop_window_expanded_view_height">350dp</dimen>
     <dimen name="drag_zone_split_from_bubble_height">100dp</dimen>
     <dimen name="drag_zone_split_from_bubble_width">60dp</dimen>
+    <dimen name="drag_zone_h_split_from_app_width_fold">140dp</dimen>
     <dimen name="drag_zone_h_split_from_expanded_view_width">60dp</dimen>
     <dimen name="drag_zone_v_split_from_expanded_view_width">200dp</dimen>
     <dimen name="drag_zone_v_split_from_expanded_view_height_tablet">285dp</dimen>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellSharedConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellSharedConstants.java
index 01d2201..8bcbd2a 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellSharedConstants.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellSharedConstants.java
@@ -22,4 +22,11 @@
 public class ShellSharedConstants {
     public static final String KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION =
             "extra_shell_can_hand_off_animation";
+
+    /**
+     * Defines the max screen width or height in dp for a device to be considered a small tablet.
+     *
+     * @see android.view.WindowManager#LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
+     */
+    public static final int SMALL_TABLET_MAX_EDGE_DP = 960;
 }
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TypefaceUtils.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TypefaceUtils.kt
new file mode 100644
index 0000000..f4efcca
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TypefaceUtils.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.shared
+
+import android.graphics.Typeface
+import android.widget.TextView
+import com.android.wm.shell.Flags
+
+/**
+ * Utility class to apply a specified typeface to a [TextView].
+ *
+ * This class provides a method, [setTypeface],
+ * to easily set a pre-defined font family and style to a given [TextView].
+ */
+class TypefaceUtils {
+
+    enum class FontFamily(val value: String) {
+        GSF_DISPLAY_LARGE("variable-display-large"),
+        GSF_DISPLAY_MEDIUM("variable-display-medium"),
+        GSF_DISPLAY_SMALL("variable-display-small"),
+        GSF_HEADLINE_LARGE("variable-headline-large"),
+        GSF_HEADLINE_MEDIUM("variable-headline-medium"),
+        GSF_HEADLINE_SMALL("variable-headline-small"),
+        GSF_TITLE_LARGE("variable-title-large"),
+        GSF_TITLE_MEDIUM("variable-title-medium"),
+        GSF_TITLE_SMALL("variable-title-small"),
+        GSF_LABEL_LARGE("variable-label-large"),
+        GSF_LABEL_MEDIUM("variable-label-medium"),
+        GSF_LABEL_SMALL("variable-label-small"),
+        GSF_BODY_LARGE("variable-body-large"),
+        GSF_BODY_MEDIUM("variable-body-medium"),
+        GSF_BODY_SMALL("variable-body-small"),
+        GSF_DISPLAY_LARGE_EMPHASIZED("variable-display-large-emphasized"),
+        GSF_DISPLAY_MEDIUM_EMPHASIZED("variable-display-medium-emphasized"),
+        GSF_DISPLAY_SMALL_EMPHASIZED("variable-display-small-emphasized"),
+        GSF_HEADLINE_LARGE_EMPHASIZED("variable-headline-large-emphasized"),
+        GSF_HEADLINE_MEDIUM_EMPHASIZED("variable-headline-medium-emphasized"),
+        GSF_HEADLINE_SMALL_EMPHASIZED("variable-headline-small-emphasized"),
+        GSF_TITLE_LARGE_EMPHASIZED("variable-title-large-emphasized"),
+        GSF_TITLE_MEDIUM_EMPHASIZED("variable-title-medium-emphasized"),
+        GSF_TITLE_SMALL_EMPHASIZED("variable-title-small-emphasized"),
+        GSF_LABEL_LARGE_EMPHASIZED("variable-label-large-emphasized"),
+        GSF_LABEL_MEDIUM_EMPHASIZED("variable-label-medium-emphasized"),
+        GSF_LABEL_SMALL_EMPHASIZED("variable-label-small-emphasized"),
+        GSF_BODY_LARGE_EMPHASIZED("variable-body-large-emphasized"),
+        GSF_BODY_MEDIUM_EMPHASIZED("variable-body-medium-emphasized"),
+        GSF_BODY_SMALL_EMPHASIZED("variable-body-small-emphasized"),
+    }
+
+    companion object {
+        /**
+         * Sets the typeface of the provided [textView] to the specified [fontFamily] and [fontStyle].
+         *
+         * The typeface is only applied to the [TextView] when [Flags.enableGsf] is `true`.
+         * If [Flags.enableGsf] is `false`, this method has no effect.
+         *
+         * @param textView The [TextView] to which the typeface should be applied. If `null`, this method does nothing.
+         * @param fontFamily The desired [FontFamily] for the [TextView].
+         * @param fontStyle The desired font style (e.g., [Typeface.NORMAL], [Typeface.BOLD], [Typeface.ITALIC]). Defaults to [Typeface.NORMAL].
+         */
+        @JvmStatic
+        @JvmOverloads
+        fun setTypeface(
+            textView: TextView?,
+            fontFamily: FontFamily,
+            fontStyle: Int = Typeface.NORMAL,
+        ) {
+            if (!Flags.enableGsf()) return
+            textView?.typeface = Typeface.create(fontFamily.value, fontStyle)
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DeviceConfig.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DeviceConfig.kt
index 1b7c9c2..ad2671b 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DeviceConfig.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DeviceConfig.kt
@@ -26,6 +26,8 @@
 import android.view.WindowManager
 import kotlin.math.max
 
+import com.android.wm.shell.shared.ShellSharedConstants.SMALL_TABLET_MAX_EDGE_DP
+
 /** Contains device configuration used for positioning bubbles on the screen. */
 data class DeviceConfig(
         val isLargeScreen: Boolean,
@@ -38,7 +40,6 @@
     companion object {
 
         private const val LARGE_SCREEN_MIN_EDGE_DP = 600
-        private const val SMALL_TABLET_MAX_EDGE_DP = 960
 
         @JvmStatic
         fun create(context: Context, windowManager: WindowManager): DeviceConfig {
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZoneFactory.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZoneFactory.kt
index 362a5c5..afeaf70 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZoneFactory.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DragZoneFactory.kt
@@ -303,6 +303,7 @@
         val isVerticalSplit = deviceConfig.isSmallTablet == deviceConfig.isLandscape
         return if (isVerticalSplit) {
             when (splitScreenModeChecker.getSplitScreenMode()) {
+                SplitScreenMode.UNSUPPORTED -> emptyList()
                 SplitScreenMode.SPLIT_50_50,
                 SplitScreenMode.NONE ->
                     listOf(
@@ -360,6 +361,7 @@
             }
         } else {
             when (splitScreenModeChecker.getSplitScreenMode()) {
+                SplitScreenMode.UNSUPPORTED -> emptyList()
                 SplitScreenMode.SPLIT_50_50,
                 SplitScreenMode.NONE ->
                     listOf(
@@ -453,6 +455,7 @@
             // vertical split drag zones are aligned with the full screen drag zone width
             val splitZoneLeft = windowBounds.right / 2 - fullScreenDragZoneWidth / 2
             when (splitScreenModeChecker.getSplitScreenMode()) {
+                SplitScreenMode.UNSUPPORTED -> emptyList()
                 SplitScreenMode.SPLIT_50_50,
                 SplitScreenMode.NONE ->
                     listOf(
@@ -560,7 +563,8 @@
             NONE,
             SPLIT_50_50,
             SPLIT_10_90,
-            SPLIT_90_10
+            SPLIT_90_10,
+            UNSUPPORTED
         }
 
         fun getSplitScreenMode(): SplitScreenMode
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetManager.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetManager.kt
index 2dbbeae..651e776 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetManager.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetManager.kt
@@ -73,7 +73,11 @@
         val newDragZone = state.getMatchingDragZone(x = x, y = y)
         state.currentDragZone = newDragZone
         if (oldDragZone != newDragZone) {
-            dragZoneChangedListener.onDragZoneChanged(from = oldDragZone, to = newDragZone)
+            dragZoneChangedListener.onDragZoneChanged(
+                draggedObject = state.draggedObject,
+                from = oldDragZone,
+                to = newDragZone
+            )
             updateDropTarget()
         }
     }
@@ -136,7 +140,7 @@
     /** Stores the current drag state. */
     private inner class DragState(
         private val dragZones: List<DragZone>,
-        draggedObject: DraggedObject
+        val draggedObject: DraggedObject
     ) {
         val initialDragZone =
             if (draggedObject.initialLocation.isOnLeft(isLayoutRtl)) {
@@ -157,7 +161,7 @@
         fun onInitialDragZoneSet(dragZone: DragZone)
 
         /** Called when the object was dragged to a different drag zone. */
-        fun onDragZoneChanged(from: DragZone, to: DragZone)
+        fun onDragZoneChanged(draggedObject: DraggedObject, from: DragZone, to: DragZone)
 
         /** Called when the drag has ended with the zone it ended in. */
         fun onDragEnded(zone: DragZone)
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
index 2bb6cf4..7327731 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
@@ -20,6 +20,7 @@
 import android.graphics.Canvas
 import android.graphics.Paint
 import android.graphics.RectF
+import android.util.TypedValue
 import android.view.View
 import com.android.wm.shell.shared.R
 
@@ -37,14 +38,21 @@
     private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
         color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
         style = Paint.Style.STROKE
-        strokeWidth = context.resources.getDimensionPixelSize(R.dimen.drop_target_stroke).toFloat()
+        strokeWidth = 1.dpToPx()
     }
 
-    private val cornerRadius = context.resources.getDimensionPixelSize(
-        R.dimen.drop_target_radius).toFloat()
+    private val cornerRadius = 28.dpToPx()
 
     private val rect = RectF(0f, 0f, 0f, 0f)
 
+    // TODO b/396539130: Use shared xml resources once we can access them in launcher
+    private fun Int.dpToPx() =
+        TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP,
+                this.toFloat(),
+                context.resources.displayMetrics
+            )
+
     override fun onDraw(canvas: Canvas) {
         canvas.save()
         canvas.drawRoundRect(rect, cornerRadius, cornerRadius, rectPaint)
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
index 9ea0532..529203f 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
@@ -19,13 +19,11 @@
 import android.Manifest.permission.SYSTEM_ALERT_WINDOW
 import android.app.TaskInfo
 import android.content.Context
-import android.content.pm.ActivityInfo
-import android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED
-import android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION
-import android.content.pm.ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS
 import android.content.pm.PackageManager
 import android.window.DesktopModeFlags
 import com.android.internal.R
+import com.android.internal.policy.DesktopModeCompatUtils
+import java.util.function.Supplier
 
 /**
  * Class to decide whether to apply app compat policies in desktop mode.
@@ -37,9 +35,11 @@
     private val pkgManager: PackageManager
         get() = context.getPackageManager()
     private val defaultHomePackage: String?
-        get() = pkgManager.getHomeActivities(ArrayList())?.packageName
+        get() = defaultHomePackageSupplier?.get()
+            ?: pkgManager.getHomeActivities(ArrayList())?.packageName
     private val packageInfoCache = mutableMapOf<String, Boolean>()
 
+    var defaultHomePackageSupplier: Supplier<String?>? = null
 
     /**
      * If the top activity should be exempt from desktop windowing and forced back to fullscreen.
@@ -49,33 +49,28 @@
      */
     fun isTopActivityExemptFromDesktopWindowing(task: TaskInfo) =
         isTopActivityExemptFromDesktopWindowing(task.baseActivity?.packageName,
-            task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent)
+            task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent,
+            task.userId)
 
-    fun isTopActivityExemptFromDesktopWindowing(packageName: String?,
-        numActivities: Int, isTopActivityNoDisplay: Boolean, isActivityStackTransparent: Boolean) =
+    fun isTopActivityExemptFromDesktopWindowing(
+        packageName: String?,
+        numActivities: Int,
+        isTopActivityNoDisplay: Boolean,
+        isActivityStackTransparent: Boolean,
+        userId: Int
+    ) =
         DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue &&
                 ((isSystemUiTask(packageName) ||
                         isPartOfDefaultHomePackageOrNoHomeAvailable(packageName) ||
                         (isTransparentTask(isActivityStackTransparent, numActivities) &&
-                                hasFullscreenTransparentPermission(packageName))) &&
+                                hasFullscreenTransparentPermission(packageName, userId))) &&
                         !isTopActivityNoDisplay)
 
-    /**
-     * Whether the caption insets should be excluded from configuration for system to handle.
-     *
-     * The treatment is enabled when all the of the following is true:
-     * * Any flags to forcibly consume caption insets are enabled.
-     * * Top activity have configuration coupled with insets.
-     * * Task is not resizeable or [ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS]
-     * is enabled.
-     */
+    /** @see DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds */
     fun shouldExcludeCaptionFromAppBounds(taskInfo: TaskInfo): Boolean =
-        DesktopModeFlags.EXCLUDE_CAPTION_FROM_APP_BOUNDS.isTrue
-                && isAnyForceConsumptionFlagsEnabled()
-                && taskInfo.topActivityInfo?.let {
-            isInsetsCoupledWithConfiguration(it) && (!taskInfo.isResizeable || it.isChangeEnabled(
-                OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS
-            ))
+        taskInfo.topActivityInfo?.let {
+            DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds(it, taskInfo.isResizeable,
+                taskInfo.appCompatTaskInfo.hasOptOutEdgeToEdge())
         } ?: false
 
     /**
@@ -91,16 +86,17 @@
     private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage
 
     // Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission.
-    private fun hasFullscreenTransparentPermission(packageName: String?): Boolean {
+    private fun hasFullscreenTransparentPermission(packageName: String?, userId: Int): Boolean {
         if (DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue) {
             if (packageName == null) {
                 return false
             }
-            return packageInfoCache.getOrPut(packageName) {
+            return packageInfoCache.getOrPut("$userId@$packageName") {
                 try {
-                    val packageInfo = pkgManager.getPackageInfo(
+                    val packageInfo = pkgManager.getPackageInfoAsUser(
                         packageName,
-                        PackageManager.GET_PERMISSIONS
+                        PackageManager.GET_PERMISSIONS,
+                        userId
                     )
                     packageInfo?.requestedPermissions?.contains(SYSTEM_ALERT_WINDOW) == true
                 } catch (e: PackageManager.NameNotFoundException) {
@@ -118,12 +114,4 @@
      */
     private fun isPartOfDefaultHomePackageOrNoHomeAvailable(packageName: String?) =
         defaultHomePackage == null || (packageName != null && packageName == defaultHomePackage)
-
-    private fun isAnyForceConsumptionFlagsEnabled(): Boolean =
-        DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isTrue
-            || DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue
-
-    private fun isInsetsCoupledWithConfiguration(info: ActivityInfo): Boolean =
-        !(info.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION)
-                || info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED))
 }
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 2e33253..ed5e0c6 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
@@ -17,7 +17,9 @@
 package com.android.wm.shell.shared.desktopmode;
 
 import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
+import static android.window.DesktopExperienceFlags.ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE;
 
+import static com.android.server.display.feature.flags.Flags.enableDisplayContentModeManagement;
 import static com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper.enableBubbleToFullscreen;
 
 import android.annotation.NonNull;
@@ -224,7 +226,7 @@
     /**
      * Return {@code true} if the current device can host desktop sessions on its internal display.
      */
-    public static boolean canInternalDisplayHostDesktops(@NonNull Context context) {
+    private static boolean canInternalDisplayHostDesktops(@NonNull Context context) {
         return context.getResources().getBoolean(R.bool.config_canInternalDisplayHostDesktops);
     }
 
@@ -269,6 +271,29 @@
     }
 
     /**
+     * Check to see if a display should have desktop mode enabled or not. Internal
+     * and external displays have separate logic.
+     */
+    public static boolean isDesktopModeSupportedOnDisplay(Context context, Display display) {
+        if (!canEnterDesktopMode(context)) {
+            return false;
+        }
+        if (display.getType() == Display.TYPE_INTERNAL) {
+            return canInternalDisplayHostDesktops(context);
+        }
+
+        // TODO (b/395014779): Change this to use WM API
+        if ((display.getType() == Display.TYPE_EXTERNAL
+                || display.getType() == Display.TYPE_OVERLAY)
+                && enableDisplayContentModeManagement()) {
+            final WindowManager wm = context.getSystemService(WindowManager.class);
+            return wm != null && wm.shouldShowSystemDecors(display.getDisplayId());
+        }
+
+        return false;
+    }
+
+    /**
      * Returns whether the multiple desktops feature is enabled for this device (both backend and
      * frontend implementations).
      */
@@ -341,8 +366,11 @@
         if (!enforceDeviceRestrictions()) {
             return true;
         }
-        final boolean desktopModeSupported = isDesktopModeSupported(context)
-                && canInternalDisplayHostDesktops(context);
+        // If projected display is enabled, #canInternalDisplayHostDesktops is no longer a
+        // requirement.
+        final boolean desktopModeSupported = ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE.isTrue()
+                ? isDesktopModeSupported(context) : (isDesktopModeSupported(context)
+                && canInternalDisplayHostDesktops(context));
         final boolean desktopModeSupportedByDevOptions =
                 Flags.enableDesktopModeThroughDevOption()
                     && isDesktopModeDevOptionSupported(context);
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/multiinstance/ManageWindowsViewContainer.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/multiinstance/ManageWindowsViewContainer.kt
index 0954b52..ac54ac7 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/multiinstance/ManageWindowsViewContainer.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/multiinstance/ManageWindowsViewContainer.kt
@@ -48,12 +48,14 @@
     lateinit var menuView: ManageWindowsView
 
     /** Creates the base menu view and fills it with icon views. */
-    fun createMenu(snapshotList: List<Pair<Int, TaskSnapshot>>,
+    fun createMenu(snapshotList: List<Pair<Int, TaskSnapshot?>>,
              onIconClickListener: ((Int) -> Unit),
              onOutsideClickListener: (() -> Unit)): ManageWindowsView {
-        val bitmapList = snapshotList.map { (index, snapshot) ->
-            index to Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace)
-        }
+        val bitmapList = snapshotList
+            .filter { it.second != null }
+            .map { (index, snapshot) ->
+                index to Bitmap.wrapHardwareBuffer(snapshot!!.hardwareBuffer, snapshot.colorSpace)
+            }
         return createAndShowMenuView(
             bitmapList,
             onIconClickListener,
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 80b65d0..7f8cfae 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
@@ -28,7 +28,7 @@
 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME;
-import static com.android.systemui.Flags.predictiveBackDelayTransition;
+import static com.android.systemui.Flags.predictiveBackDelayWmTransition;
 import static com.android.window.flags.Flags.unifyBackNavigationTransition;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
 
@@ -433,7 +433,7 @@
     public void onThresholdCrossed() {
         mThresholdCrossed = true;
         BackTouchTracker activeTracker = getActiveTracker();
-        if (predictiveBackDelayTransition() && activeTracker != null && mActiveCallback == null
+        if (predictiveBackDelayWmTransition() && activeTracker != null && mActiveCallback == null
                 && mBackGestureStarted) {
             startBackNavigation(activeTracker);
         }
@@ -448,7 +448,7 @@
         final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
         if (!shouldDispatchToAnimator && mActiveCallback != null) {
             mCurrentTracker.updateStartLocation();
-            tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+            tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent());
             if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) {
                 tryPilferPointers();
             }
@@ -494,12 +494,12 @@
                 if (swipeEdge == EDGE_NONE) {
                     // start animation immediately for non-gestural sources (without ACTION_MOVE
                     // events)
-                    if (!predictiveBackDelayTransition()) {
+                    if (!predictiveBackDelayWmTransition()) {
                         mThresholdCrossed = true;
                     }
                     mPointersPilfered = true;
                     onGestureStarted(touchX, touchY, swipeEdge);
-                    if (predictiveBackDelayTransition()) {
+                    if (predictiveBackDelayWmTransition()) {
                         onThresholdCrossed();
                     }
                     mShouldStartOnNextMoveEvent = false;
@@ -555,7 +555,7 @@
             mPostCommitAnimationInProgress = false;
             mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
             startSystemAnimation();
-        } else if (!predictiveBackDelayTransition()) {
+        } else if (!predictiveBackDelayWmTransition()) {
             startBackNavigation(touchTracker);
         }
     }
@@ -604,7 +604,7 @@
             mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
             // App is handling back animation. Cancel system animation latency tracking.
             cancelLatencyTracking();
-            tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+            tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent());
             if (!isAppProgressGenerationAllowed()) {
                 tryPilferPointers();
             }
@@ -1041,7 +1041,7 @@
                 () -> mShellExecutor.execute(this::onBackAnimationFinished));
 
         if (mApps.length >= 1) {
-            BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
+            BackMotionEvent startEvent = mCurrentTracker.createStartEvent();
             dispatchOnBackStarted(mActiveCallback, startEvent);
             if (startEvent.getSwipeEdge() == EDGE_NONE) {
                 // TODO(b/373544911): onBackStarted is dispatched here so that
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index d948928..4f30a05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -375,7 +375,7 @@
                 user,
                 icon,
                 BubbleType.TYPE_APP,
-                getAppBubbleKeyForApp(intent.getPackage(), user),
+                getAppBubbleKeyForApp(ComponentUtils.getPackageName(intent), user),
                 mainExecutor, bgExecutor);
     }
 
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 50e2f4d..81eff6f 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
@@ -1306,7 +1306,11 @@
                 // TODO b/392893178: Merge the unfold and the task view transition so that we don't
                 //  have to post a delayed runnable to the looper to update the bounds
                 if (mStackView.isExpanded()) {
-                    mStackView.postDelayed(() -> mStackView.updateExpandedView(), 500);
+                    mStackView.postDelayed(() -> {
+                        if (mStackView != null) {
+                            mStackView.updateExpandedView();
+                        }
+                    } , 500);
                 }
             }
             if (newConfig.fontScale != mFontScale) {
@@ -1604,7 +1608,7 @@
             @Nullable BubbleTransitions.DragData dragData) {
         if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) return;
         Bubble b = mBubbleData.getOrCreateBubble(taskInfo); // Removes from overflow
-        ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", taskInfo.taskId);
+        ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - taskId=%s", taskInfo.taskId);
         BubbleBarLocation location = null;
         if (dragData != null) {
             location =
@@ -2643,13 +2647,9 @@
         mBubbleData.setSelectedBubbleAndExpandStack(bubbleToSelect);
     }
 
-    private void moveBubbleToFullscreen(String key) {
+    private void moveDraggedBubbleToFullscreen(String key, Point dropLocation) {
         Bubble b = mBubbleData.getBubbleInStackWithKey(key);
-        if (b == null) {
-            Log.w(TAG, "can't find bubble with key " + key + " to move to fullscreen");
-            return;
-        }
-        b.getTaskView().moveToFullscreen();
+        mBubbleTransitions.startDraggedBubbleIconToFullscreen(b, dropLocation);
     }
 
     private boolean isDeviceLocked() {
@@ -2940,8 +2940,9 @@
         }
 
         @Override
-        public void moveBubbleToFullscreen(String key) {
-            mMainExecutor.execute(() -> mController.moveBubbleToFullscreen(key));
+        public void moveDraggedBubbleToFullscreen(String key, Point dropLocation) {
+            mMainExecutor.execute(
+                    () -> mController.moveDraggedBubbleToFullscreen(key, dropLocation));
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 2c2451c..290ef16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -27,6 +27,7 @@
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
+import static com.android.wm.shell.shared.TypefaceUtils.setTypeface;
 
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
@@ -71,6 +72,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.AlphaOptimizedButton;
 import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.TypefaceUtils;
 import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.taskview.TaskView;
 
@@ -238,7 +240,6 @@
                                 mContext.createContextAsUser(
                                         mBubble.getUser(), Context.CONTEXT_RESTRICTED);
                         Intent fillInIntent = new Intent();
-                        fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                         PendingIntent pi = PendingIntent.getActivity(
                                 context,
                                 /* requestCode= */ 0,
@@ -467,6 +468,14 @@
                         new BubbleTaskViewListener.Callback() {
                             @Override
                             public void onTaskCreated() {
+                                // The taskId is saved to use for removeTask,
+                                // preventing appearance in recent tasks.
+                                BubbleTaskViewListener listener = mCurrentTaskViewListener != null
+                                        ? ((BubbleTaskViewListener) mCurrentTaskViewListener)
+                                        : null;
+                                mTaskId = listener != null
+                                        ? listener.getTaskId()
+                                        : bubbleTaskView.getTaskId();
                                 setContentVisibility(true);
                             }
 
@@ -544,6 +553,7 @@
         mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate(
                 R.layout.bubble_manage_button, this /* parent */, false /* attach */);
         addView(mManageButton);
+        setTypeface(mManageButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
         mManageButton.setVisibility(visibility);
         setManageClickListener();
         post(() -> {
@@ -602,6 +612,10 @@
         updateManageButtonIfExists();
     }
 
+    public float getCornerRadius() {
+        return mCornerRadius;
+    }
+
     /**
      * Updates the size and visuals of the pointer if {@link #mPointerView} is initialized.
      * Does nothing otherwise.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index da6948d..92007a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -50,6 +50,7 @@
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.TypefaceUtils;
 
 /**
  * Flyout view that appears as a 'chat bubble' alongside the bubble stack. The flyout can visually
@@ -165,8 +166,10 @@
         LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
         mFlyoutTextContainer = findViewById(R.id.bubble_flyout_text_container);
         mSenderText = findViewById(R.id.bubble_flyout_name);
+        TypefaceUtils.setTypeface(mSenderText, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
         mSenderAvatar = findViewById(R.id.bubble_flyout_avatar);
         mMessageText = mFlyoutTextContainer.findViewById(R.id.bubble_flyout_text);
+        TypefaceUtils.setTypeface(mMessageText, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM);
 
         final Resources res = getResources();
         mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 64f54b8..e901e0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -46,6 +46,7 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
+import com.android.wm.shell.shared.TypefaceUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -234,6 +235,10 @@
         setBackgroundColor(bgColor);
         mEmptyStateTitle.setTextColor(textColor);
         mEmptyStateSubtitle.setTextColor(textColor);
+        TypefaceUtils.setTypeface(mEmptyStateTitle,
+                TypefaceUtils.FontFamily.GSF_BODY_MEDIUM_EMPHASIZED);
+        TypefaceUtils.setTypeface(mEmptyStateSubtitle, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM);
+
     }
 
     public void updateFontSize() {
@@ -322,6 +327,7 @@
 
         TextView viewName = overflowView.findViewById(R.id.bubble_view_name);
         viewName.setTextColor(textColor);
+        TypefaceUtils.setTypeface(viewName, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
 
         return new ViewHolder(overflowView, mPositioner);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 70340d7..03d6b0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -103,7 +103,9 @@
     private int mManageButtonHeight;
     private int mOverflowHeight;
     private int mMinimumFlyoutWidthLargeScreen;
-    private int mBubbleBarExpandedViewDropTargetPadding;
+    private int mBarExpViewDropTargetPaddingTop;
+    private int mBarExpViewDropTargetPaddingBottom;
+    private int mBarExpViewDropTargetPaddingHorizontal;
 
     private PointF mRestingStackPosition;
 
@@ -173,8 +175,12 @@
                 res.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width),
                 mPositionRect.width() - 2 * mExpandedViewPadding
         );
-        mBubbleBarExpandedViewDropTargetPadding = res.getDimensionPixelSize(
-                R.dimen.bubble_bar_expanded_view_drop_target_padding);
+        mBarExpViewDropTargetPaddingTop = res.getDimensionPixelSize(
+                R.dimen.bubble_bar_expanded_view_drop_target_padding_top);
+        mBarExpViewDropTargetPaddingBottom = res.getDimensionPixelSize(
+                R.dimen.bubble_bar_expanded_view_drop_target_padding_bottom);
+        mBarExpViewDropTargetPaddingHorizontal = res.getDimensionPixelSize(
+                R.dimen.bubble_bar_expanded_view_drop_target_padding_horizontal);
 
         if (mShowingInBubbleBar) {
             mExpandedViewLargeScreenWidth = mExpandedViewBubbleBarWidth;
@@ -986,8 +992,15 @@
     public Rect getBubbleBarExpandedViewDropTargetBounds(boolean onLeft) {
         Rect bounds = new Rect();
         getBubbleBarExpandedViewBounds(onLeft, false, bounds);
-        bounds.inset(mBubbleBarExpandedViewDropTargetPadding,
-                mBubbleBarExpandedViewDropTargetPadding);
+        // Drop target bounds are based on expanded view bounds with some padding added
+        int leftPadding = onLeft ? 0 : mBarExpViewDropTargetPaddingHorizontal;
+        int rightPadding = onLeft ? mBarExpViewDropTargetPaddingHorizontal : 0;
+        bounds.inset(
+                leftPadding,
+                mBarExpViewDropTargetPaddingTop,
+                rightPadding,
+                mBarExpViewDropTargetPaddingBottom
+        );
         return bounds;
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 9272417..3dce456 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -89,8 +89,11 @@
 import com.android.wm.shell.bubbles.animation.StackAnimationController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
 import com.android.wm.shell.shared.animation.Interpolators;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.shared.bubbles.DeviceConfig;
 import com.android.wm.shell.shared.bubbles.DismissView;
 import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
@@ -1319,7 +1322,7 @@
         mBubbleContainer.bringToFront();
     }
 
-    // TODO: Create ManageMenuView and move setup / animations there
+    // TODO (b/402196554) : Create ManageMenuView and move setup / animations there
     private void setUpManageMenu() {
         if (mManageMenu != null) {
             removeView(mManageMenu);
@@ -1377,9 +1380,33 @@
         mManageSettingsIcon = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_icon);
         mManageSettingsText = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_name);
 
+        View fullscreenView = mManageMenu.findViewById(
+                R.id.bubble_manage_menu_fullscreen_container);
+        if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+            fullscreenView.setVisibility(VISIBLE);
+            fullscreenView.setOnClickListener(
+                    view -> {
+                        showManageMenu(false /* show */);
+                        BubbleExpandedView expandedView = getExpandedView();
+                        if (expandedView != null && expandedView.getTaskView() != null) {
+                            expandedView.getTaskView().moveToFullscreen();
+                        }
+                    });
+        } else {
+            fullscreenView.setVisibility(GONE);
+        }
+
         // The menu itself should respect locale direction so the icons are on the correct side.
         mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
         addView(mManageMenu);
+
+        // Doesn't seem to work unless view is added; so set font after.
+        TypefaceUtils.setTypeface(findViewById(R.id.manage_dismiss), FontFamily.GSF_LABEL_LARGE);
+        TypefaceUtils.setTypeface(findViewById(R.id.manage_dont_bubble),
+                FontFamily.GSF_LABEL_LARGE);
+        TypefaceUtils.setTypeface(mManageSettingsText, FontFamily.GSF_LABEL_LARGE);
+        TypefaceUtils.setTypeface(findViewById(R.id.bubble_manage_menu_fullscreen_title),
+                FontFamily.GSF_LABEL_LARGE);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
index 63d7134..9c20e3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
@@ -130,7 +130,6 @@
                             mContext.createContextAsUser(
                                     mBubble.getUser(), Context.CONTEXT_RESTRICTED);
                     Intent fillInIntent = new Intent();
-                    fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                     // First try get pending intent from the bubble
                     PendingIntent pi = mBubble.getPendingIntent();
                     if (pi == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index e7e7be9..728975e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -30,6 +30,7 @@
 import android.app.ActivityManager;
 import android.app.TaskInfo;
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -42,6 +43,11 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import androidx.core.animation.Animator;
+import androidx.core.animation.Animator.AnimatorUpdateListener;
+import androidx.core.animation.AnimatorListenerAdapter;
+import androidx.core.animation.ValueAnimator;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -113,6 +119,11 @@
         return convert;
     }
 
+    /** Starts a transition that converts a dragged bubble icon to a full screen task. */
+    public BubbleTransition startDraggedBubbleIconToFullscreen(Bubble bubble, Point dropLocation) {
+        return new DraggedBubbleIconToFullscreen(bubble, dropLocation);
+    }
+
     /**
      * Plucks the task-surface out of an ancestor view while making the view invisible. This helper
      * attempts to do this seamlessly (ie. view becomes invisible in sync with task reparent).
@@ -159,19 +170,22 @@
         private final WindowContainerTransaction mPendingWct;
         private final boolean mReleasedOnLeft;
         private final float mTaskScale;
+        private final float mCornerRadius;
         private final PointF mDragPosition;
 
         /**
          * @param releasedOnLeft true if the bubble was released in the left drop target
          * @param taskScale      the scale of the task when it was dragged to bubble
+         * @param cornerRadius   the corner radius of the task when it was dragged to bubble
          * @param dragPosition   the position of the task when it was dragged to bubble
          * @param wct            pending operations to be applied when finishing the drag
          */
-        public DragData(boolean releasedOnLeft, float taskScale, @Nullable PointF dragPosition,
-                @Nullable WindowContainerTransaction wct) {
+        public DragData(boolean releasedOnLeft, float taskScale, float cornerRadius,
+                @Nullable PointF dragPosition, @Nullable WindowContainerTransaction wct) {
             mPendingWct = wct;
             mReleasedOnLeft = releasedOnLeft;
             mTaskScale = taskScale;
+            mCornerRadius = cornerRadius;
             mDragPosition = dragPosition != null ? dragPosition : new PointF(0, 0);
         }
 
@@ -198,6 +212,13 @@
         }
 
         /**
+         * @return the corner radius of the task when it was dragged to bubble
+         */
+        public float getCornerRadius() {
+            return mCornerRadius;
+        }
+
+        /**
          * @return position of the task when it was dragged to bubble
          */
         public PointF getDragPosition() {
@@ -362,6 +383,7 @@
                         (int) mDragData.getDragPosition().y);
                 startTransaction.setScale(mSnapshot, mDragData.getTaskScale(),
                         mDragData.getTaskScale());
+                startTransaction.setCornerRadius(mSnapshot, mDragData.getCornerRadius());
             }
 
             // Now update state (and talk to launcher) in parallel with snapshot stuff
@@ -377,12 +399,6 @@
             startTransaction.setPosition(mSnapshot, left, top);
             startTransaction.setLayer(mSnapshot, Integer.MAX_VALUE);
 
-            BubbleBarExpandedView bbev = mBubble.getBubbleBarExpandedView();
-            if (bbev != null) {
-                // Corners get reset during the animation. Add them back
-                startTransaction.setCornerRadius(mSnapshot, bbev.getRestingCornerRadius());
-            }
-
             startTransaction.apply();
 
             mTaskViewTransitions.onExternalDone(transition);
@@ -602,8 +618,7 @@
             mTaskLeash = taskChg.getLeash();
             mRootLeash = info.getRoot(0).getLeash();
 
-            SurfaceControl dest =
-                    mBubble.getBubbleBarExpandedView().getViewRootImpl().getSurfaceControl();
+            SurfaceControl dest = getExpandedView(mBubble).getViewRootImpl().getSurfaceControl();
             final Runnable onPlucked = () -> {
                 // Need to remove the taskview AFTER applying the startTransaction because
                 // it isn't synchronized.
@@ -613,12 +628,12 @@
                 mBubbleData.setExpanded(false /* expanded */);
             };
             if (dest != null) {
-                pluck(mTaskLeash, mBubble.getBubbleBarExpandedView(), dest,
+                pluck(mTaskLeash, getExpandedView(mBubble), dest,
                         taskChg.getStartAbsBounds().left - info.getRoot(0).getOffset().x,
                         taskChg.getStartAbsBounds().top - info.getRoot(0).getOffset().y,
-                        mBubble.getBubbleBarExpandedView().getCornerRadius(), startTransaction,
+                        getCornerRadius(mBubble), startTransaction,
                         onPlucked);
-                mBubble.getBubbleBarExpandedView().post(() -> mTransitions.dispatchTransition(
+                getExpandedView(mBubble).post(() -> mTransitions.dispatchTransition(
                         mTransition, info, startTransaction, finishTransaction, finishCallback,
                         null));
             } else {
@@ -634,10 +649,175 @@
         @Override
         public void continueCollapse() {
             mBubble.cleanupTaskView();
-            if (mTaskLeash == null) return;
+            if (mTaskLeash == null || !mTaskLeash.isValid()) return;
             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
             t.reparent(mTaskLeash, mRootLeash);
             t.apply();
         }
+
+        private View getExpandedView(@NonNull Bubble bubble) {
+            if (bubble.getBubbleBarExpandedView() != null) {
+                return bubble.getBubbleBarExpandedView();
+            }
+            return bubble.getExpandedView();
+        }
+
+        private float getCornerRadius(@NonNull Bubble bubble) {
+            if (bubble.getBubbleBarExpandedView() != null) {
+                return bubble.getBubbleBarExpandedView().getCornerRadius();
+            }
+            return bubble.getExpandedView().getCornerRadius();
+        }
+    }
+
+    /**
+     * A transition that converts a dragged bubble icon to a full screen window.
+     *
+     * <p>This transition assumes that the bubble is invisible so it is simply sent to front.
+     */
+    class DraggedBubbleIconToFullscreen implements Transitions.TransitionHandler, BubbleTransition {
+
+        IBinder mTransition;
+        final Bubble mBubble;
+        final Point mDropLocation;
+        final TransactionProvider mTransactionProvider;
+
+        DraggedBubbleIconToFullscreen(Bubble bubble, Point dropLocation) {
+            this(bubble, dropLocation, SurfaceControl.Transaction::new);
+        }
+
+        @VisibleForTesting
+        DraggedBubbleIconToFullscreen(Bubble bubble, Point dropLocation,
+                TransactionProvider transactionProvider) {
+            mBubble = bubble;
+            mDropLocation = dropLocation;
+            mTransactionProvider = transactionProvider;
+            bubble.setPreparingTransition(this);
+            WindowContainerToken token = bubble.getTaskView().getTaskInfo().getToken();
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            wct.setAlwaysOnTop(token, false);
+            wct.setWindowingMode(token, WINDOWING_MODE_UNDEFINED);
+            wct.reorder(token, /* onTop= */ true);
+            wct.setHidden(token, false);
+            mTaskOrganizer.setInterceptBackPressedOnTaskRoot(token, false);
+            mTaskViewTransitions.enqueueExternal(bubble.getTaskView().getController(), () -> {
+                mTransition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct, this);
+                return mTransition;
+            });
+        }
+
+        @Override
+        public void skip() {
+            mBubble.setPreparingTransition(null);
+        }
+
+        @Override
+        public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            if (mTransition != transition) {
+                return false;
+            }
+
+            final TaskViewTaskController taskViewTaskController =
+                    mBubble.getTaskView().getController();
+            if (taskViewTaskController == null) {
+                mTaskViewTransitions.onExternalDone(transition);
+                finishCallback.onTransitionFinished(null);
+                return true;
+            }
+
+            TransitionInfo.Change change = findTransitionChange(info);
+            if (change == null) {
+                Slog.w(TAG, "Expected a TaskView transition to front but didn't find "
+                        + "one, cleaning up the task view");
+                taskViewTaskController.setTaskNotFound();
+                mTaskViewTransitions.onExternalDone(transition);
+                finishCallback.onTransitionFinished(null);
+                return true;
+            }
+            mRepository.remove(taskViewTaskController);
+
+            final SurfaceControl taskLeash = change.getLeash();
+            // set the initial position of the task with 0 scale
+            startTransaction.setPosition(taskLeash, mDropLocation.x, mDropLocation.y);
+            startTransaction.setScale(taskLeash, 0, 0);
+            startTransaction.apply();
+
+            final SurfaceControl.Transaction animT = mTransactionProvider.get();
+            ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+            animator.setDuration(250);
+            animator.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(@NonNull Animator animation) {
+                    float progress = animator.getAnimatedFraction();
+                    float x = mDropLocation.x * (1 - progress);
+                    float y = mDropLocation.y * (1 - progress);
+                    animT.setPosition(taskLeash, x, y);
+                    animT.setScale(taskLeash, progress, progress);
+                    animT.apply();
+                }
+            });
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(@NonNull Animator animation) {
+                    animT.close();
+                    finishCallback.onTransitionFinished(null);
+                }
+            });
+            animator.start();
+            taskViewTaskController.notifyTaskRemovalStarted(mBubble.getTaskView().getTaskInfo());
+            mTaskViewTransitions.onExternalDone(transition);
+            return true;
+        }
+
+        private TransitionInfo.Change findTransitionChange(TransitionInfo info) {
+            TransitionInfo.Change result = null;
+            WindowContainerToken token = mBubble.getTaskView().getTaskInfo().getToken();
+            for (int i = 0; i < info.getChanges().size(); ++i) {
+                final TransitionInfo.Change change = info.getChanges().get(i);
+                if (change.getTaskInfo() == null) {
+                    continue;
+                }
+                if (change.getMode() != TRANSIT_TO_FRONT) {
+                    continue;
+                }
+                if (!token.equals(change.getTaskInfo().token)) {
+                    continue;
+                }
+                result = change;
+                break;
+            }
+            return result;
+        }
+
+        @Override
+        public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+                @NonNull SurfaceControl.Transaction startTransaction,
+                @NonNull SurfaceControl.Transaction finishTransaction,
+                @NonNull IBinder mergeTarget,
+                @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        }
+
+        @Override
+        public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+                @NonNull TransitionRequestInfo request) {
+            return null;
+        }
+
+        @Override
+        public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+                @Nullable SurfaceControl.Transaction finishTransaction) {
+            if (!aborted) {
+                return;
+            }
+            mTransition = null;
+            mTaskViewTransitions.onExternalDone(transition);
+        }
+    }
+
+    interface TransactionProvider {
+        SurfaceControl.Transaction get();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 079edb3..e2d5d78 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.UserHandle;
 import com.android.wm.shell.bubbles.IBubblesListener;
@@ -59,5 +60,5 @@
 
     oneway void showDropTarget(in boolean show, in @nullable BubbleBarLocation location) = 15;
 
-    oneway void moveBubbleToFullscreen(in String key) = 16;
+    oneway void moveDraggedBubbleToFullscreen(in String key, in Point dropLocation) = 16;
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index 39a2a7b..d2ad708 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -27,6 +27,7 @@
 import android.widget.LinearLayout
 import com.android.internal.R.color.system_neutral1_900
 import com.android.wm.shell.R
+import com.android.wm.shell.shared.TypefaceUtils
 import com.android.wm.shell.shared.animation.Interpolators
 
 /**
@@ -53,6 +54,12 @@
 
     init {
         LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this)
+        TypefaceUtils.setTypeface(findViewById(R.id.user_education_title),
+            TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+        TypefaceUtils.setTypeface(findViewById(R.id.user_education_description),
+            TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
+        TypefaceUtils.setTypeface(manageButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE_EMPHASIZED)
+        TypefaceUtils.setTypeface(gotItButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE_EMPHASIZED)
         visibility = View.GONE
         elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 1660619..9ac05989 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -26,6 +26,7 @@
 import android.widget.TextView
 import com.android.internal.util.ContrastColorUtil
 import com.android.wm.shell.R
+import com.android.wm.shell.shared.TypefaceUtils
 import com.android.wm.shell.shared.animation.Interpolators
 
 /**
@@ -59,6 +60,9 @@
 
     init {
         LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this)
+        TypefaceUtils.setTypeface(titleTextView,
+            TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+        TypefaceUtils.setTypeface(descTextView, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
 
         visibility = View.GONE
         elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 6c840f0..3997412 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -55,6 +55,7 @@
 import com.android.wm.shell.shared.bubbles.DismissView;
 import com.android.wm.shell.shared.bubbles.DragZone;
 import com.android.wm.shell.shared.bubbles.DragZoneFactory;
+import com.android.wm.shell.shared.bubbles.DraggedObject;
 import com.android.wm.shell.shared.bubbles.DropTargetManager;
 
 import kotlin.Unit;
@@ -168,8 +169,8 @@
                         }
 
                         @Override
-                        public void onDragZoneChanged(@NonNull DragZone from,
-                                @NonNull DragZone to) {
+                        public void onDragZoneChanged(@NonNull DraggedObject draggedObject,
+                                @NonNull DragZone from, @NonNull DragZone to) {
                             final boolean isBubbleLeft = to instanceof DragZone.Bubble.Left;
                             final boolean isBubbleRight = to instanceof DragZone.Bubble.Right;
                             if ((isBubbleLeft || isBubbleRight)
@@ -447,9 +448,9 @@
             bubble.cleanupViews(!inTransition);
             endAction.run();
         };
-        if (mBubbleData.getBubbles().isEmpty()) {
-            // we're removing the last bubble. collapse the expanded view and cleanup bubble views
-            // at the end.
+        if (mBubbleData.getBubbles().isEmpty() || inTransition) {
+            // If we are removing the last bubble or removing the current bubble via transition,
+            // collapse the expanded view and clean up bubbles at the end.
             collapse(cleanUp);
         } else {
             cleanUp.run();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
index 6c14d83..bccc6dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
@@ -25,6 +25,7 @@
 import android.widget.TextView;
 
 import com.android.wm.shell.R;
+import com.android.wm.shell.shared.TypefaceUtils;
 
 /**
  * Bubble bar expanded view menu item view to display menu action details
@@ -55,6 +56,7 @@
         super.onFinishInflate();
         mImageView = findViewById(R.id.bubble_bar_menu_item_icon);
         mTextView = findViewById(R.id.bubble_bar_menu_item_title);
+        TypefaceUtils.setTypeface(mTextView, TypefaceUtils.FontFamily.GSF_TITLE_MEDIUM);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
index dfbf655..7c0f8e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -33,6 +33,7 @@
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.shared.TypefaceUtils;
 
 import java.util.ArrayList;
 
@@ -75,6 +76,7 @@
         mActionsSectionView = findViewById(R.id.bubble_bar_manage_menu_actions_section);
         mBubbleIconView = findViewById(R.id.bubble_bar_manage_menu_bubble_icon);
         mBubbleTitleView = findViewById(R.id.bubble_bar_manage_menu_bubble_title);
+        TypefaceUtils.setTypeface(mBubbleTitleView, TypefaceUtils.FontFamily.GSF_TITLE_MEDIUM);
         mBubbleDismissIconView = findViewById(R.id.bubble_bar_manage_menu_dismiss_icon);
         updateThemeColors();
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index b7761ec..69009fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -28,9 +28,9 @@
 import android.view.ViewGroup;
 
 import com.android.app.animation.Interpolators;
-import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 
 import java.util.ArrayList;
 
@@ -263,7 +263,7 @@
                 }
         ));
 
-        if (Flags.enableBubbleAnything() || Flags.enableBubbleToFullscreen()) {
+        if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
             menuActions.add(new BubbleBarMenuView.MenuAction(
                     Icon.createWithResource(resources,
                             R.drawable.desktop_mode_ic_handle_menu_fullscreen),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 7adec39..0bd3a54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -35,6 +35,7 @@
 import com.android.wm.shell.bubbles.BubbleEducationController
 import com.android.wm.shell.bubbles.BubbleViewProvider
 import com.android.wm.shell.bubbles.setup
+import com.android.wm.shell.shared.TypefaceUtils
 import com.android.wm.shell.shared.animation.PhysicsAnimator
 import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
 import com.android.wm.shell.shared.bubbles.BubblePopupView
@@ -108,6 +109,10 @@
         root.getBoundsOnScreen(rootBounds)
         educationView =
             createEducationView(R.layout.bubble_bar_stack_education, root).apply {
+                TypefaceUtils.setTypeface(findViewById(R.id.education_title),
+                    TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+                TypefaceUtils.setTypeface(findViewById(R.id.education_text),
+                    TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
                 setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
                 updateEducationPosition(view = this, position, rootBounds)
                 val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
@@ -153,6 +158,10 @@
 
         educationView =
             createEducationView(R.layout.bubble_bar_manage_education, root).apply {
+                TypefaceUtils.setTypeface(findViewById(R.id.education_manage_title),
+                    TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+                TypefaceUtils.setTypeface(findViewById(R.id.education_manage_text),
+                    TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
                 pivotY = 0f
                 doOnLayout { it.pivotX = it.width / 2f }
                 setOnClickListener { hideEducation(animated = true) }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
index 0d0bc9b..9fefb51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ComponentUtils.kt
@@ -25,7 +25,8 @@
 object ComponentUtils {
     /** Retrieves the package name from an [Intent].  */
     @JvmStatic
-    fun getPackageName(intent: Intent?): String? = intent?.component?.packageName
+    fun getPackageName(intent: Intent?): String? =
+        intent?.component?.packageName ?: intent?.`package`
 
     /** Retrieves the package name from a [PendingIntent].  */
     @JvmStatic
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index dd2050a..bc76a87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -440,11 +440,18 @@
                             statsToken);
                 }
 
-                // In case of a hide, the statsToken should not been send yet (as the animation
-                // is still ongoing). It will be sent at the end of the animation
-                boolean hideAnimOngoing = !mImeRequestedVisible && mAnimation != null;
-                setVisibleDirectly(mImeRequestedVisible || mAnimation != null,
-                        hideAnimOngoing ? null : statsToken);
+                boolean hideAnimOngoing;
+                boolean reportVisible;
+                if (android.view.inputmethod.Flags.reportAnimatingInsetsTypes()) {
+                    hideAnimOngoing = false;
+                    reportVisible = mImeRequestedVisible;
+                } else {
+                    // In case of a hide, the statsToken should not been send yet (as the animation
+                    // is still ongoing). It will be sent at the end of the animation.
+                    hideAnimOngoing = !mImeRequestedVisible && mAnimation != null;
+                    reportVisible = mImeRequestedVisible || mAnimation != null;
+                }
+                setVisibleDirectly(reportVisible, hideAnimOngoing ? null : statsToken);
             }
         }
 
@@ -461,10 +468,12 @@
             }
         }
 
-        private void setAnimating(boolean imeAnimationOngoing) {
+        private void setAnimating(boolean imeAnimationOngoing,
+                @Nullable ImeTracker.Token statsToken) {
             int animatingTypes = imeAnimationOngoing ? WindowInsets.Type.ime() : 0;
             try {
-                mWmService.updateDisplayWindowAnimatingTypes(mDisplayId, animatingTypes);
+                mWmService.updateDisplayWindowAnimatingTypes(mDisplayId, animatingTypes,
+                        statsToken);
             } catch (RemoteException e) {
             }
         }
@@ -628,7 +637,9 @@
                                 + " showing:" + (mAnimationDirection == DIRECTION_SHOW));
                     }
                     if (android.view.inputmethod.Flags.reportAnimatingInsetsTypes()) {
-                        setAnimating(true);
+                        // Updating the animatingTypes when starting the animation is not the
+                        // trigger to show the IME. Thus, not sending the statsToken here.
+                        setAnimating(true /* imeAnimationOngoing */, null /* statsToken */);
                     }
                     int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY, defaultY),
                             imeTop(shownY, defaultY), mAnimationDirection == DIRECTION_SHOW,
@@ -678,7 +689,8 @@
                     if (!android.view.inputmethod.Flags.refactorInsetsController()) {
                         dispatchEndPositioning(mDisplayId, mCancelled, t);
                     } else if (android.view.inputmethod.Flags.reportAnimatingInsetsTypes()) {
-                        setAnimating(false);
+                        setAnimating(false /* imeAnimationOngoing */,
+                                mAnimationDirection == DIRECTION_HIDE ? statsToken : null);
                     }
                     if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
                         ImeTracker.forLogging().onProgress(mStatsToken,
@@ -688,7 +700,12 @@
                             removeImeSurface(mDisplayId);
                         }
                         if (android.view.inputmethod.Flags.refactorInsetsController()) {
-                            setVisibleDirectly(false /* visible */, statsToken);
+                            // Updating the client visibility will not hide the IME, unless it is
+                            // not animating anymore. Thus, not sending a statsToken here, but
+                            // only later when we're updating the animatingTypes.
+                            setVisibleDirectly(false /* visible */,
+                                    !android.view.inputmethod.Flags.reportAnimatingInsetsTypes()
+                                            ? statsToken : null);
                         }
                         if (!android.view.inputmethod.Flags.refactorInsetsController()) {
                             ImeTracker.forLogging().onHidden(mStatsToken);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HomeIntentProvider.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HomeIntentProvider.kt
new file mode 100644
index 0000000..8751b65
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HomeIntentProvider.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common
+
+import android.app.ActivityManager
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+import android.view.Display.DEFAULT_DISPLAY
+import android.window.WindowContainerTransaction
+import com.android.window.flags.Flags
+
+/** Creates home intent **/
+class HomeIntentProvider(
+    private val context: Context,
+) {
+    fun addLaunchHomePendingIntent(
+        wct: WindowContainerTransaction, displayId: Int, userId: Int? = null
+    ) {
+        val userHandle =
+            if (userId != null) UserHandle.of(userId) else UserHandle.of(ActivityManager.getCurrentUser())
+
+        val launchHomeIntent = Intent(Intent.ACTION_MAIN).apply {
+            if (displayId != DEFAULT_DISPLAY) {
+                addCategory(Intent.CATEGORY_SECONDARY_HOME)
+            } else {
+                addCategory(Intent.CATEGORY_HOME)
+            }
+        }
+        val options = ActivityOptions.makeBasic().apply {
+            launchWindowingMode = WINDOWING_MODE_FULLSCREEN
+            pendingIntentBackgroundActivityStartMode =
+                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
+            if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+                launchDisplayId = displayId
+            }
+        }
+        val pendingIntent = PendingIntent.getActivityAsUser(
+            context,
+            /* requestCode= */ 0,
+            launchHomeIntent,
+            PendingIntent.FLAG_IMMUTABLE,
+            /* options= */ null,
+            userHandle,
+        )
+        wct.sendPendingIntent(pendingIntent, launchHomeIntent, options.toBundle())
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index c4696d5..a8e6b59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -20,17 +20,14 @@
 
 import android.annotation.BinderThread;
 import android.annotation.NonNull;
-import android.os.RemoteException;
 import android.util.Slog;
 import android.view.SurfaceControl;
-import android.view.WindowManager;
 import android.window.WindowContainerTransaction;
 import android.window.WindowContainerTransactionCallback;
 import android.window.WindowOrganizer;
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.shared.TransactionPool;
-import com.android.wm.shell.transition.LegacyTransitions;
 
 import java.util.ArrayList;
 
@@ -87,25 +84,6 @@
     }
 
     /**
-     * Queues a legacy transition to be sent serially to WM
-     */
-    public void queue(LegacyTransitions.ILegacyTransition transition,
-            @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
-        if (wct.isEmpty()) {
-            if (DEBUG) Slog.d(TAG, "Skip queue due to transaction change is empty");
-            return;
-        }
-        SyncCallback cb = new SyncCallback(transition, type, wct);
-        synchronized (mQueue) {
-            if (DEBUG) Slog.d(TAG, "Queueing up legacy transition " + wct);
-            mQueue.add(cb);
-            if (mQueue.size() == 1) {
-                cb.send();
-            }
-        }
-    }
-
-    /**
      * Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
      * Otherwise just returns without queueing.
      * @return {@code true} if queued, {@code false} if not.
@@ -168,17 +146,9 @@
     private class SyncCallback extends WindowContainerTransactionCallback {
         int mId = -1;
         final WindowContainerTransaction mWCT;
-        final LegacyTransitions.LegacyTransition mLegacyTransition;
 
         SyncCallback(WindowContainerTransaction wct) {
             mWCT = wct;
-            mLegacyTransition = null;
-        }
-
-        SyncCallback(LegacyTransitions.ILegacyTransition legacyTransition,
-                @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
-            mWCT = wct;
-            mLegacyTransition = new LegacyTransitions.LegacyTransition(type, legacyTransition);
         }
 
         // Must be sychronized on mQueue
@@ -194,12 +164,7 @@
             }
             if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
             try {
-                if (mLegacyTransition != null) {
-                    mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
-                            mLegacyTransition.getAdapter(), this, mWCT);
-                } else {
-                    mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
-                }
+                mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
             } catch (RuntimeException e) {
                 Slog.e(TAG, "Send failed", e);
                 // Finish current sync callback immediately.
@@ -228,18 +193,10 @@
                     if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
                     mQueue.remove(this);
                     onTransactionReceived(t);
-                    if (mLegacyTransition != null) {
-                        try {
-                            mLegacyTransition.getSyncCallback().onTransactionReady(mId, t);
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e);
-                        }
-                    } else {
-                        ProtoLog.v(WM_SHELL,
-                                "SyncTransactionQueue.onTransactionReady(): syncId=%d apply", id);
-                        t.apply();
-                        t.close();
-                    }
+                    ProtoLog.v(WM_SHELL,
+                            "SyncTransactionQueue.onTransactionReady(): syncId=%d apply", id);
+                    t.apply();
+                    t.close();
                     if (!mQueue.isEmpty()) {
                         mQueue.get(0).send();
                     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index 214e6ad..aeef211 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -143,6 +143,9 @@
      */
     public final Rect mCachedLauncherShelfHeightKeepClearArea = new Rect();
 
+    private final List<OnPipComponentChangedListener> mOnPipComponentChangedListeners =
+            new ArrayList<>();
+
     // the size of the current bounds relative to the max size spec
     private float mBoundsScale;
 
@@ -156,9 +159,7 @@
         // Update the relative proportion of the bounds compared to max possible size. Max size
         // spec takes the aspect ratio of the bounds into account, so both width and height
         // scale by the same factor.
-        addPipExclusionBoundsChangeCallback((bounds) -> {
-            updateBoundsScale();
-        });
+        addPipExclusionBoundsChangeCallback((bounds) -> updateBoundsScale());
     }
 
     /** Reloads the resources. */
@@ -341,11 +342,14 @@
     /** Set the last {@link ComponentName} to enter PIP mode. */
     public void setLastPipComponentName(@Nullable ComponentName lastPipComponentName) {
         final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName);
+        if (!changed) return;
+        clearReentryState();
+        setHasUserResizedPip(false);
+        setHasUserMovedPip(false);
+        final ComponentName oldComponentName = mLastPipComponentName;
         mLastPipComponentName = lastPipComponentName;
-        if (changed) {
-            clearReentryState();
-            setHasUserResizedPip(false);
-            setHasUserMovedPip(false);
+        for (OnPipComponentChangedListener listener : mOnPipComponentChangedListeners) {
+            listener.onPipComponentChanged(oldComponentName, mLastPipComponentName);
         }
     }
 
@@ -616,6 +620,21 @@
         }
     }
 
+    /** Adds callback to listen on component change. */
+    public void addOnPipComponentChangedListener(@NonNull OnPipComponentChangedListener listener) {
+        if (!mOnPipComponentChangedListeners.contains(listener)) {
+            mOnPipComponentChangedListeners.add(listener);
+        }
+    }
+
+    /** Removes callback to listen on component change. */
+    public void removeOnPipComponentChangedListener(
+            @NonNull OnPipComponentChangedListener listener) {
+        if (mOnPipComponentChangedListeners.contains(listener)) {
+            mOnPipComponentChangedListeners.remove(listener);
+        }
+    }
+
     public LauncherState getLauncherState() {
         return mLauncherState;
     }
@@ -695,7 +714,7 @@
      * Represents the state of pip to potentially restore upon reentry.
      */
     @VisibleForTesting
-    public static final class PipReentryState {
+    static final class PipReentryState {
         private static final String TAG = PipReentryState.class.getSimpleName();
 
         private final float mSnapFraction;
@@ -722,6 +741,22 @@
         }
     }
 
+    /**
+     * Listener interface for PiP component change, i.e. the app in pip mode changes
+     * TODO: Move this out of PipBoundsState once pip1 is deprecated.
+     */
+    public interface OnPipComponentChangedListener {
+        /**
+         * Callback when the component in pip mode changes.
+         * @param oldPipComponent previous component in pip mode,
+         *                        {@code null} if this is the very first time PiP appears.
+         * @param newPipComponent new component that enters pip mode.
+         */
+        void onPipComponentChanged(
+                @Nullable ComponentName oldPipComponent,
+                @NonNull ComponentName newPipComponent);
+    }
+
     /** Dumps internal state. */
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
index 01fd344..1128fb2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
@@ -20,9 +20,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import android.window.DesktopExperienceFlags;
+import android.window.DesktopModeFlags;
 import android.window.DisplayAreaInfo;
 
-import com.android.window.flags.Flags;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
 import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
@@ -48,18 +49,23 @@
 
     /**
      * Returns whether PiP in Desktop Windowing is enabled by checking the following:
-     * - Desktop Windowing in PiP flag is enabled
+     * - PiP in Desktop Windowing flag is enabled
      * - DesktopUserRepositories is injected
      * - DragToDesktopTransitionHandler is injected
      */
     public boolean isDesktopWindowingPipEnabled() {
-        return Flags.enableDesktopWindowingPip() && mDesktopUserRepositoriesOptional.isPresent()
+        return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()
+                && mDesktopUserRepositoriesOptional.isPresent()
                 && mDragToDesktopTransitionHandlerOptional.isPresent();
     }
 
-    /** Returns whether PiP in Connected Displays is enabled by checking the flag. */
+    /**
+     * Returns whether PiP in Connected Displays is enabled by checking the following:
+     * - PiP in Connected Displays flag is enabled
+     * - PiP2 flag is enabled
+     */
     public boolean isConnectedDisplaysPipEnabled() {
-        return DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue();
+        return DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue() && Flags.enablePip2();
     }
 
     /** Returns whether the display with the PiP task is in freeform windowing mode. */
@@ -80,8 +86,7 @@
             return false;
         }
         final int displayId = mPipDisplayLayoutState.getDisplayId();
-        return mDesktopUserRepositoriesOptional.get().getCurrent().isAnyDeskActive(displayId)
-                || isDisplayInFreeform();
+        return mDesktopUserRepositoriesOptional.get().getCurrent().isAnyDeskActive(displayId);
     }
 
     /**
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 e20a3d8..2b0885e 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
@@ -559,7 +559,12 @@
                 true /* setEffectBounds */);
     }
 
-    /** Updates recording bounds of divider window and both of the splits. */
+    /**
+     * Updates the bounds of the divider window and both split apps.
+     * @param position The left/top edge of the visual divider, where the edge of app A meets the
+     *                 divider. Not to be confused with the actual divider surface, which is larger
+     *                 and overlaps the apps a bit.
+     */
     private void updateBounds(int position, Rect bounds1, Rect bounds2, Rect dividerBounds,
             boolean setEffectBounds) {
         dividerBounds.set(mRootBounds);
@@ -574,10 +579,11 @@
 
             // For flexible split, expand app offscreen as well
             if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) {
-                if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) {
-                    bounds1.left = bounds1.right - bounds2.width();
+                int distanceToCenter = position - mDividerSnapAlgorithm.getMiddleTarget().position;
+                if (position < mDividerSnapAlgorithm.getMiddleTarget().position) {
+                    bounds1.left += distanceToCenter * 2;
                 } else {
-                    bounds2.right = bounds2.left + bounds1.width();
+                    bounds2.right += distanceToCenter * 2;
                 }
             }
 
@@ -590,10 +596,11 @@
 
             // For flexible split, expand app offscreen as well
             if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) {
-                if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) {
-                    bounds1.top = bounds1.bottom - bounds2.height();
+                int distanceToCenter = position - mDividerSnapAlgorithm.getMiddleTarget().position;
+                if (position < mDividerSnapAlgorithm.getMiddleTarget().position) {
+                    bounds1.top += distanceToCenter * 2;
                 } else {
-                    bounds2.bottom = bounds2.top + bounds1.height();
+                    bounds2.bottom += distanceToCenter * 2;
                 }
             }
         }
@@ -1466,11 +1473,7 @@
                 // Freeze the configuration size with offset to prevent app get a configuration
                 // changed or relaunch. This is required to make sure client apps will calculate
                 // insets properly after layout shifted.
-                if (mTargetYOffset == 0) {
-                    mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this);
-                } else {
-                    mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset, SplitLayout.this);
-                }
+                mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset, SplitLayout.this);
             }
 
             // Make {@link DividerView} non-interactive while IME showing in split mode. Listen to
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 9b11e4a..4413c87 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
@@ -18,6 +18,8 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
+import static com.android.wm.shell.compatui.impl.CompatUIRequestsKt.DISPLAY_COMPAT_SHOW_RESTART_DIALOG;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.TaskInfo;
@@ -53,7 +55,9 @@
 import com.android.wm.shell.compatui.api.CompatUIEvent;
 import com.android.wm.shell.compatui.api.CompatUIHandler;
 import com.android.wm.shell.compatui.api.CompatUIInfo;
+import com.android.wm.shell.compatui.api.CompatUIRequest;
 import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonClicked;
+import com.android.wm.shell.compatui.impl.CompatUIRequests;
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
@@ -245,6 +249,21 @@
         mCallback = callback;
     }
 
+    @Override
+    public void sendCompatUIRequest(CompatUIRequest compatUIRequest) {
+        switch(compatUIRequest.getRequestId()) {
+            case DISPLAY_COMPAT_SHOW_RESTART_DIALOG:
+                handleDisplayCompatShowRestartDialog(compatUIRequest.asType());
+                break;
+            default:
+        }
+    }
+
+    private void handleDisplayCompatShowRestartDialog(
+            CompatUIRequests.DisplayCompatShowRestartDialog request) {
+        onRestartButtonClicked(new Pair<>(request.getTaskInfo(), request.getTaskListener()));
+    }
+
     /**
      * Called when the Task info changed. Creates and updates the compat UI if there is an
      * activity in size compat, or removes the UI if there is no size compat activity.
@@ -254,13 +273,17 @@
     public void onCompatInfoChanged(@NonNull CompatUIInfo compatUIInfo) {
         final TaskInfo taskInfo = compatUIInfo.getTaskInfo();
         final ShellTaskOrganizer.TaskListener taskListener = compatUIInfo.getListener();
-        if (taskInfo != null && !taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat()) {
+        final boolean isInDisplayCompatMode =
+                taskInfo.appCompatTaskInfo.isRestartMenuEnabledForDisplayMove();
+        if (taskInfo != null && !taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat()
+                && !isInDisplayCompatMode) {
             mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
         }
         mIsInDesktopMode = isInDesktopMode(taskInfo);
         // We close all the Compat UI educations in case TaskInfo has no configuration or
         // TaskListener or in desktop mode.
-        if (taskInfo.configuration == null || taskListener == null || mIsInDesktopMode) {
+        if (taskInfo.configuration == null || taskListener == null
+                || (mIsInDesktopMode && !isInDisplayCompatMode)) {
             // Null token means the current foreground activity is not in compatibility mode.
             removeLayouts(taskInfo.taskId);
             return;
@@ -552,8 +575,11 @@
             @Nullable ShellTaskOrganizer.TaskListener taskListener) {
         RestartDialogWindowManager layout =
                 mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId);
+        final boolean isInNonDisplayCompatDesktopMode = mIsInDesktopMode
+                && !taskInfo.appCompatTaskInfo.isRestartMenuEnabledForDisplayMove();
         if (layout != null) {
-            if (layout.needsToBeRecreated(taskInfo, taskListener) || mIsInDesktopMode) {
+            if (layout.needsToBeRecreated(taskInfo, taskListener)
+                    || isInNonDisplayCompatDesktopMode) {
                 mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId);
                 layout.release();
             } else {
@@ -568,8 +594,9 @@
                 return;
             }
         }
-        if (mIsInDesktopMode) {
-            // Return if in desktop mode.
+        if (isInNonDisplayCompatDesktopMode) {
+            // No restart dialog can be shown in desktop mode unless the task is in display compat
+            // mode.
             return;
         }
         // Create a new UI layout.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIHandler.kt
index 817e554..f71f809 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIHandler.kt
@@ -28,6 +28,11 @@
     fun onCompatInfoChanged(compatUIInfo: CompatUIInfo)
 
     /**
+     * Invoked when another component in Shell requests a CompatUI state change.
+     */
+    fun sendCompatUIRequest(compatUIRequest: CompatUIRequest)
+
+    /**
      * Optional reference to the object responsible to send {@link CompatUIEvent}
      */
     fun setCallback(compatUIEventSender: Consumer<CompatUIEvent>?)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIRequest.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIRequest.kt
new file mode 100644
index 0000000..069fd9b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIRequest.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.api
+
+/**
+ * Abstraction for all the possible Compat UI Component requests.
+ */
+interface CompatUIRequest {
+    /**
+     * Unique request identifier
+     */
+    val requestId: Int
+
+    @Suppress("UNCHECKED_CAST")
+    fun <T : CompatUIRequest> asType(): T? = this as? T
+
+    fun <T : CompatUIRequest> asType(clazz: Class<T>): T? {
+        return if (clazz.isInstance(this)) clazz.cast(this) else null
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt
new file mode 100644
index 0000000..da4fc99
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.impl
+
+import android.app.TaskInfo
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.compatui.api.CompatUIRequest
+
+internal const val DISPLAY_COMPAT_SHOW_RESTART_DIALOG = 0
+
+/**
+ * All the {@link CompatUIRequest} the Compat UI Framework can handle
+ */
+sealed class CompatUIRequests(override val requestId: Int) : CompatUIRequest {
+    /** Sent when the restart handle menu is clicked, and a restart dialog is requested. */
+    data class DisplayCompatShowRestartDialog(val taskInfo: TaskInfo,
+        val taskListener: ShellTaskOrganizer.TaskListener) :
+        CompatUIRequests(DISPLAY_COMPAT_SHOW_RESTART_DIALOG)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandler.kt
index 02db85a..7dcb16c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandler.kt
@@ -23,6 +23,7 @@
 import com.android.wm.shell.compatui.api.CompatUIHandler
 import com.android.wm.shell.compatui.api.CompatUIInfo
 import com.android.wm.shell.compatui.api.CompatUIRepository
+import com.android.wm.shell.compatui.api.CompatUIRequest
 import com.android.wm.shell.compatui.api.CompatUIState
 import java.util.function.Consumer
 
@@ -102,4 +103,6 @@
     override fun setCallback(compatUIEventSender: Consumer<CompatUIEvent>?) {
         this.compatUIEventSender = compatUIEventSender
     }
+
+    override fun sendCompatUIRequest(compatUIRequest: CompatUIRequest) {}
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxInputDetector.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxInputDetector.kt
index 812cc01..bcbe123 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxInputDetector.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxInputDetector.kt
@@ -167,6 +167,7 @@
 
                 windowSession.updateInputChannel(
                     inputChannel.token,
+                    null /* hostInputTransferToken */,
                     displayId,
                     inputSurface,
                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/crashhandling/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/crashhandling/OWNERS
new file mode 100644
index 0000000..007528e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/crashhandling/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-module crash handling owners
+uysalorhan@google.com
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/crashhandling/ShellCrashHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/crashhandling/ShellCrashHandler.kt
new file mode 100644
index 0000000..2e34d043
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/crashhandling/ShellCrashHandler.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.crashhandling
+
+import android.app.WindowConfiguration
+import android.content.Context
+import android.view.Display.DEFAULT_DISPLAY
+import android.window.DesktopExperienceFlags
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.HomeIntentProvider
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellInit
+
+/** [ShellCrashHandler] for shell to use when it's being initialized. Currently it only restores
+ *  the home task to top.
+ **/
+class ShellCrashHandler(
+    private val context: Context,
+    private val shellTaskOrganizer: ShellTaskOrganizer,
+    private val homeIntentProvider: HomeIntentProvider,
+    shellInit: ShellInit,
+) {
+    init {
+        shellInit.addInitCallback(::onInit, this)
+    }
+
+    private fun onInit() {
+        handleCrashIfNeeded()
+    }
+
+    private fun handleCrashIfNeeded() {
+        // For now only handle crashes when desktop mode is enabled on the device.
+        if (DesktopModeStatus.canEnterDesktopMode(context) &&
+            !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+            var freeformTaskExists = false
+            // If there are running tasks at init, WMShell has crashed but WMCore is still alive.
+            for (task in shellTaskOrganizer.getRunningTasks()) {
+                if (task.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) {
+                    freeformTaskExists = true
+                }
+
+                if (freeformTaskExists) {
+                    shellTaskOrganizer.applyTransaction(
+                        addLaunchHomePendingIntent(WindowContainerTransaction(), DEFAULT_DISPLAY)
+                    )
+                    break
+                }
+            }
+        }
+    }
+
+    private fun addLaunchHomePendingIntent(
+        wct: WindowContainerTransaction, displayId: Int
+    ): WindowContainerTransaction {
+        // TODO: b/400462917 - Check that crashes are also handled correctly on HSUM devices. We
+        // might need to pass the [userId] here to launch the correct home.
+        homeIntentProvider.addLaunchHomePendingIntent(wct, displayId)
+        return wct
+    }
+}
\ No newline at end of file
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 318cdee..f62fd819 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
@@ -95,6 +95,7 @@
 import com.android.wm.shell.desktopmode.DesktopMode;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.desktopmode.common.DefaultHomePackageSupplier;
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
@@ -260,8 +261,14 @@
 
     @WMSingleton
     @Provides
-    static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(Context context) {
-        return new DesktopModeCompatPolicy(context);
+    static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(
+            Context context,
+            ShellInit shellInit,
+            @ShellMainThread Handler mainHandler) {
+        final DesktopModeCompatPolicy policy = new DesktopModeCompatPolicy(context);
+        policy.setDefaultHomePackageSupplier(new DefaultHomePackageSupplier(
+                context, shellInit, mainHandler));
+        return policy;
     }
 
     @WMSingleton
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 43a19ef..2cf671b 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
@@ -69,6 +69,7 @@
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.HomeIntentProvider;
 import com.android.wm.shell.common.LaunchAdjacentController;
 import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController;
 import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorSurface;
@@ -80,6 +81,7 @@
 import com.android.wm.shell.common.split.SplitState;
 import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
 import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
+import com.android.wm.shell.crashhandling.ShellCrashHandler;
 import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
 import com.android.wm.shell.dagger.pip.PipModule;
 import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler;
@@ -96,6 +98,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
 import com.android.wm.shell.desktopmode.DesktopModeMoveToDisplayTransitionHandler;
 import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
+import com.android.wm.shell.desktopmode.DesktopPipTransitionObserver;
 import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
@@ -169,6 +172,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.AppHandleAndHeaderVisibilityHelper;
 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;
@@ -450,7 +454,8 @@
             Optional<DesktopImmersiveController> desktopImmersiveController,
             WindowDecorViewModel windowDecorViewModel,
             Optional<TaskChangeListener> taskChangeListener,
-            FocusTransitionObserver focusTransitionObserver) {
+            FocusTransitionObserver focusTransitionObserver,
+            Optional<DesksTransitionObserver> desksTransitionObserver) {
         return new FreeformTaskTransitionObserver(
                 context,
                 shellInit,
@@ -458,7 +463,8 @@
                 desktopImmersiveController,
                 windowDecorViewModel,
                 taskChangeListener,
-                focusTransitionObserver);
+                focusTransitionObserver,
+                desksTransitionObserver);
     }
 
     @WMSingleton
@@ -724,9 +730,11 @@
     static DesksOrganizer provideDesksOrganizer(
             @NonNull ShellInit shellInit,
             @NonNull ShellCommandHandler shellCommandHandler,
-            @NonNull ShellTaskOrganizer shellTaskOrganizer
+            @NonNull ShellTaskOrganizer shellTaskOrganizer,
+            @NonNull LaunchAdjacentController launchAdjacentController
     ) {
-        return new RootTaskDesksOrganizer(shellInit, shellCommandHandler, shellTaskOrganizer);
+        return new RootTaskDesksOrganizer(shellInit, shellCommandHandler, shellTaskOrganizer,
+                launchAdjacentController);
     }
 
     @WMSingleton
@@ -752,6 +760,7 @@
             ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
             DragToDesktopTransitionHandler dragToDesktopTransitionHandler,
             @DynamicOverride DesktopUserRepositories desktopUserRepositories,
+            DesktopRepositoryInitializer desktopRepositoryInitializer,
             Optional<DesktopImmersiveController> desktopImmersiveController,
             DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver,
             LaunchAdjacentController launchAdjacentController,
@@ -771,11 +780,13 @@
             Optional<BubbleController> bubbleController,
             OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver,
             DesksOrganizer desksOrganizer,
-            DesksTransitionObserver desksTransitionObserver,
+            Optional<DesksTransitionObserver> desksTransitionObserver,
+            Optional<DesktopPipTransitionObserver> desktopPipTransitionObserver,
             UserProfileContexts userProfileContexts,
             DesktopModeCompatPolicy desktopModeCompatPolicy,
             DragToDisplayTransitionHandler dragToDisplayTransitionHandler,
-            DesktopModeMoveToDisplayTransitionHandler moveToDisplayTransitionHandler) {
+            DesktopModeMoveToDisplayTransitionHandler moveToDisplayTransitionHandler,
+            HomeIntentProvider homeIntentProvider) {
         return new DesktopTasksController(
                 context,
                 shellInit,
@@ -797,6 +808,7 @@
                 dragToDesktopTransitionHandler,
                 desktopImmersiveController.get(),
                 desktopUserRepositories,
+                desktopRepositoryInitializer,
                 recentsTransitionHandler,
                 multiInstanceHelper,
                 mainExecutor,
@@ -812,11 +824,13 @@
                 bubbleController,
                 overviewToDesktopTransitionObserver,
                 desksOrganizer,
-                desksTransitionObserver,
+                desksTransitionObserver.get(),
+                desktopPipTransitionObserver,
                 userProfileContexts,
                 desktopModeCompatPolicy,
                 dragToDisplayTransitionHandler,
-                moveToDisplayTransitionHandler);
+                moveToDisplayTransitionHandler,
+                homeIntentProvider);
     }
 
     @WMSingleton
@@ -873,6 +887,7 @@
             Transitions transitions,
             @DynamicOverride DesktopUserRepositories desktopUserRepositories,
             ShellTaskOrganizer shellTaskOrganizer,
+            DesksOrganizer desksOrganizer,
             InteractionJankMonitor interactionJankMonitor,
             @ShellMainThread Handler handler) {
         int maxTaskLimit = DesktopModeStatus.getMaxTaskLimit(context);
@@ -885,6 +900,7 @@
                         transitions,
                         desktopUserRepositories,
                         shellTaskOrganizer,
+                        desksOrganizer,
                         maxTaskLimit <= 0 ? null : maxTaskLimit,
                         interactionJankMonitor,
                         context,
@@ -953,8 +969,15 @@
 
     @WMSingleton
     @Provides
-    static DesktopModeMoveToDisplayTransitionHandler provideMoveToDisplayTransitionHandler() {
-        return new DesktopModeMoveToDisplayTransitionHandler(new SurfaceControl.Transaction());
+    static DesktopModeMoveToDisplayTransitionHandler provideMoveToDisplayTransitionHandler(
+            InteractionJankMonitor interactionJankMonitor,
+            @ShellMainThread Handler shellMainHandler,
+            DisplayController displayController) {
+        return new DesktopModeMoveToDisplayTransitionHandler(
+                new SurfaceControl.Transaction(),
+                interactionJankMonitor,
+                shellMainHandler,
+                displayController);
     }
 
     @WMSingleton
@@ -1011,6 +1034,7 @@
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             AppHandleEducationController appHandleEducationController,
             AppToWebEducationController appToWebEducationController,
+            AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
             Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
             FocusTransitionObserver focusTransitionObserver,
@@ -1034,10 +1058,10 @@
                 rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser,
                 assistContentRequester, windowDecorViewHostSupplier, multiInstanceHelper,
                 desktopTasksLimiter, appHandleEducationController, appToWebEducationController,
-                windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
-                focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger,
-                taskResourceLoader, recentsTransitionHandler, desktopModeCompatPolicy,
-                desktopTilingDecorViewModel,
+                appHandleAndHeaderVisibilityHelper, windowDecorCaptionHandleRepository,
+                activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger,
+                desktopModeUiEventLogger, taskResourceLoader, recentsTransitionHandler,
+                desktopModeCompatPolicy, desktopTilingDecorViewModel,
                 multiDisplayDragMoveIndicatorController));
     }
 
@@ -1065,6 +1089,16 @@
 
     @WMSingleton
     @Provides
+    static AppHandleAndHeaderVisibilityHelper provideAppHandleAndHeaderVisibilityHelper(
+            @NonNull Context context,
+            @NonNull DisplayController displayController,
+            @NonNull DesktopModeCompatPolicy desktopModeCompatPolicy) {
+        return new AppHandleAndHeaderVisibilityHelper(context, displayController,
+                desktopModeCompatPolicy);
+    }
+
+    @WMSingleton
+    @Provides
     static WindowDecorTaskResourceLoader provideWindowDecorTaskResourceLoader(
             @NonNull Context context, @NonNull ShellInit shellInit,
             @NonNull ShellController shellController,
@@ -1201,9 +1235,9 @@
             Transitions transitions,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
+            Optional<DesktopPipTransitionObserver> desktopPipTransitionObserver,
             Optional<BackAnimationController> backAnimationController,
             DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
-            @NonNull DesksTransitionObserver desksTransitionObserver,
             ShellInit shellInit) {
         return desktopUserRepositories.flatMap(
                 repository ->
@@ -1214,19 +1248,37 @@
                                         transitions,
                                         shellTaskOrganizer,
                                         desktopMixedTransitionHandler.get(),
+                                        desktopPipTransitionObserver,
                                         backAnimationController.get(),
                                         desktopWallpaperActivityTokenProvider,
-                                        desksTransitionObserver,
                                         shellInit)));
     }
 
     @WMSingleton
     @Provides
-    static DesksTransitionObserver provideDesksTransitionObserver(
-            @NonNull @DynamicOverride DesktopUserRepositories desktopUserRepositories,
+    static Optional<DesksTransitionObserver> provideDesksTransitionObserver(
+            Context context,
+            @DynamicOverride DesktopUserRepositories desktopUserRepositories,
             @NonNull DesksOrganizer desksOrganizer
     ) {
-        return new DesksTransitionObserver(desktopUserRepositories, desksOrganizer);
+        if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
+            return Optional.of(
+                    new DesksTransitionObserver(desktopUserRepositories, desksOrganizer));
+        }
+        return Optional.empty();
+    }
+
+    @WMSingleton
+    @Provides
+    static Optional<DesktopPipTransitionObserver> provideDesktopPipTransitionObserver(
+            Context context
+    ) {
+        if (DesktopModeStatus.canEnterDesktopMode(context)
+                && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
+            return Optional.of(
+                    new DesktopPipTransitionObserver());
+        }
+        return Optional.empty();
     }
 
     @WMSingleton
@@ -1288,10 +1340,12 @@
     static Optional<DesktopDisplayEventHandler> provideDesktopDisplayEventHandler(
             Context context,
             ShellInit shellInit,
+            @ShellMainThread CoroutineScope mainScope,
             DisplayController displayController,
             Optional<DesktopUserRepositories> desktopUserRepositories,
             Optional<DesktopTasksController> desktopTasksController,
-            Optional<DesktopDisplayModeController> desktopDisplayModeController
+            Optional<DesktopDisplayModeController> desktopDisplayModeController,
+            DesktopRepositoryInitializer desktopRepositoryInitializer
     ) {
         if (!DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.empty();
@@ -1300,7 +1354,9 @@
                 new DesktopDisplayEventHandler(
                         context,
                         shellInit,
+                        mainScope,
                         displayController,
+                        desktopRepositoryInitializer,
                         desktopUserRepositories.get(),
                         desktopTasksController.get(),
                         desktopDisplayModeController.get()));
@@ -1441,7 +1497,10 @@
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             IWindowManager windowManager,
             ShellTaskOrganizer shellTaskOrganizer,
-            DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider
+            DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
+            InputManager inputManager,
+            DisplayController displayController,
+            @ShellMainThread Handler mainHandler
     ) {
         if (!DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.empty();
@@ -1453,7 +1512,10 @@
                         rootTaskDisplayAreaOrganizer,
                         windowManager,
                         shellTaskOrganizer,
-                        desktopWallpaperActivityTokenProvider));
+                        desktopWallpaperActivityTokenProvider,
+                        inputManager,
+                        displayController,
+                        mainHandler));
     }
 
     //
@@ -1535,7 +1597,8 @@
             Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional,
             Optional<DesktopDisplayEventHandler> desktopDisplayEventHandler,
             Optional<DesktopModeKeyGestureHandler> desktopModeKeyGestureHandler,
-            Optional<SystemModalsTransitionHandler> systemModalsTransitionHandler) {
+            Optional<SystemModalsTransitionHandler> systemModalsTransitionHandler,
+            ShellCrashHandler shellCrashHandler) {
         return new Object();
     }
 
@@ -1555,4 +1618,20 @@
         return new UserProfileContexts(context, shellController, shellInit);
     }
 
+    @WMSingleton
+    @Provides
+    static ShellCrashHandler provideShellCrashHandler(
+            Context context,
+            ShellTaskOrganizer shellTaskOrganizer,
+            HomeIntentProvider homeIntentProvider,
+            ShellInit shellInit) {
+        return new ShellCrashHandler(context, shellTaskOrganizer, homeIntentProvider, shellInit);
+    }
+
+    @WMSingleton
+    @Provides
+    static HomeIntentProvider provideHomeIntentProvider(Context context) {
+        return new HomeIntentProvider(context);
+    }
+
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index afc48ac..683b743 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -23,15 +23,21 @@
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
 import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
 
 /** Handles display events in desktop mode */
 class DesktopDisplayEventHandler(
     private val context: Context,
     shellInit: ShellInit,
+    private val mainScope: CoroutineScope,
     private val displayController: DisplayController,
+    private val desktopRepositoryInitializer: DesktopRepositoryInitializer,
     private val desktopUserRepositories: DesktopUserRepositories,
     private val desktopTasksController: DesktopTasksController,
     private val desktopDisplayModeController: DesktopDisplayModeController,
@@ -61,15 +67,19 @@
             logV("Display #$displayId does not support desks")
             return
         }
-        logV("Creating new desk in new display#$displayId")
-        // TODO: b/362720497 - when SystemUI crashes with a freeform task open for any reason, the
-        //  task is recreated and received in [FreeformTaskListener] before this display callback
-        //  is invoked, which results in the repository trying to add the task to a desk before the
-        //  desk has been recreated here, which may result in a crash-loop if the repository is
-        //  checking that the desk exists before adding a task to it. See b/391984373.
-        desktopTasksController.createDesk(displayId)
-        // TODO: b/393978539 - consider activating the desk on creation when applicable, such as
-        //  for connected displays.
+
+        mainScope.launch {
+            desktopRepositoryInitializer.isInitialized.collect { initialized ->
+                if (!initialized) return@collect
+                if (desktopRepository.getNumberOfDesks(displayId) == 0) {
+                    logV("Creating new desk in new display#$displayId")
+                    // TODO: b/393978539 - consider activating the desk on creation when
+                    //  applicable, such as for connected displays.
+                    desktopTasksController.createDesk(displayId)
+                }
+                cancel()
+            }
+        }
     }
 
     override fun onDisplayRemoved(displayId: Int) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
index e89aafe..ea2fdc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -22,18 +22,25 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
 import android.app.WindowConfiguration.windowingModeToString
 import android.content.Context
+import android.hardware.input.InputManager
+import android.os.Handler
 import android.provider.Settings
 import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.IWindowManager
+import android.view.InputDevice
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.window.DesktopExperienceFlags
 import android.window.WindowContainerTransaction
+import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
 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.transition.Transitions
 
 /** Controls the display windowing mode in desktop mode */
@@ -44,8 +51,43 @@
     private val windowManager: IWindowManager,
     private val shellTaskOrganizer: ShellTaskOrganizer,
     private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
+    private val inputManager: InputManager,
+    private val displayController: DisplayController,
+    @ShellMainThread private val mainHandler: Handler,
 ) {
 
+    private val onTabletModeChangedListener =
+        object : InputManager.OnTabletModeChangedListener {
+            override fun onTabletModeChanged(whenNanos: Long, inTabletMode: Boolean) {
+                refreshDisplayWindowingMode()
+            }
+        }
+
+    private val inputDeviceListener =
+        object : InputManager.InputDeviceListener {
+            override fun onInputDeviceAdded(deviceId: Int) {
+                refreshDisplayWindowingMode()
+            }
+
+            override fun onInputDeviceChanged(deviceId: Int) {
+                refreshDisplayWindowingMode()
+            }
+
+            override fun onInputDeviceRemoved(deviceId: Int) {
+                refreshDisplayWindowingMode()
+            }
+        }
+
+    init {
+        if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+            inputManager.registerOnTabletModeChangedListener(
+                onTabletModeChangedListener,
+                mainHandler,
+            )
+            inputManager.registerInputDeviceListener(inputDeviceListener, mainHandler)
+        }
+    }
+
     fun refreshDisplayWindowingMode() {
         if (!DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue) return
 
@@ -89,25 +131,75 @@
         transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
     }
 
-    private fun getTargetWindowingModeForDefaultDisplay(): Int {
-        if (isExtendedDisplayEnabled() && hasExternalDisplay()) {
-            return WINDOWING_MODE_FREEFORM
+    // Do not directly use this method to check the state of desktop-first mode. Check the display
+    // windowing mode instead.
+    private fun canDesktopFirstModeBeEnabledOnDefaultDisplay(): Boolean {
+        if (isDefaultDisplayDesktopEligible()) {
+            if (isExtendedDisplayEnabled() && hasExternalDisplay()) {
+                return true
+            }
+            if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+                if (isInClamshellMode() || hasAnyMouseDevice()) {
+                    return true
+                }
+            }
         }
-        return windowManager.getWindowingMode(DEFAULT_DISPLAY)
+        return false
     }
 
-    // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
-    private fun isExtendedDisplayEnabled() =
-        0 !=
+    @VisibleForTesting
+    fun getTargetWindowingModeForDefaultDisplay(): Int {
+        if (canDesktopFirstModeBeEnabledOnDefaultDisplay()) {
+            return WINDOWING_MODE_FREEFORM
+        }
+
+        return if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
+            WINDOWING_MODE_FULLSCREEN
+        } else {
+            // If form factor-based desktop first switch is disabled, use the default display
+            // windowing mode here to keep the freeform mode for some form factors (e.g.,
+            // FEATURE_PC).
+            windowManager.getWindowingMode(DEFAULT_DISPLAY)
+        }
+    }
+
+    private fun isExtendedDisplayEnabled(): Boolean {
+        if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue) {
+            return rootTaskDisplayAreaOrganizer
+                .getDisplayIds()
+                .filter { it != DEFAULT_DISPLAY }
+                .any { displayId ->
+                    displayController.getDisplay(displayId)?.let { display ->
+                        DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+                    } ?: false
+                }
+        }
+
+        return 0 !=
             Settings.Global.getInt(
                 context.contentResolver,
                 DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
                 0,
             )
+    }
 
     private fun hasExternalDisplay() =
         rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY }
 
+    private fun hasAnyMouseDevice() =
+        inputManager.inputDeviceIds.any {
+            inputManager.getInputDevice(it)?.supportsSource(InputDevice.SOURCE_MOUSE) == true
+        }
+
+    private fun isInClamshellMode() = inputManager.isInTabletMode() == InputManager.SWITCH_STATE_OFF
+
+    private fun isDefaultDisplayDesktopEligible(): Boolean {
+        val display = requireNotNull(displayController.getDisplay(DEFAULT_DISPLAY)) {
+            "Display object of DEFAULT_DISPLAY must be non-null."
+        }
+        return DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+    }
+
     private fun logV(msg: String, vararg arguments: Any?) {
         ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
     }
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 1f7edb4..4646662 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
@@ -452,6 +452,11 @@
     private fun findTaskChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? =
         info.changes.firstOrNull { change -> change.taskInfo?.taskId == taskId }
 
+    private fun findLaunchChange(info: TransitionInfo): TransitionInfo.Change? =
+        info.changes.firstOrNull { change ->
+            change.mode == TRANSIT_OPEN && change.taskInfo != null && change.taskInfo!!.isFreeform
+        }
+
     private fun findDesktopTaskLaunchChange(
         info: TransitionInfo,
         launchTaskId: Int?,
@@ -459,14 +464,18 @@
         return if (launchTaskId != null) {
             // Launching a known task (probably from background or moving to front), so
             // specifically look for it.
-            findTaskChange(info, launchTaskId)
+            val launchChange = findTaskChange(info, launchTaskId)
+            if (
+                DesktopModeFlags.ENABLE_DESKTOP_OPENING_DEEPLINK_MINIMIZE_ANIMATION_BUGFIX.isTrue &&
+                    launchChange == null
+            ) {
+                findLaunchChange(info)
+            } else {
+                launchChange
+            }
         } else {
             // Launching a new task, so the first opening freeform task.
-            info.changes.firstOrNull { change ->
-                change.mode == TRANSIT_OPEN &&
-                    change.taskInfo != null &&
-                    change.taskInfo!!.isFreeform
-            }
+            findLaunchChange(info)
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
index 5269318..1ea545f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
@@ -56,7 +56,6 @@
 
     override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?): Boolean {
         if (
-            !isKeyGestureSupported(event.keyGestureType) ||
                 !desktopTasksController.isPresent ||
                 !desktopModeWindowDecorViewModel.isPresent
         ) {
@@ -136,19 +135,6 @@
         }
     }
 
-    override fun isKeyGestureSupported(gestureType: Int): Boolean =
-        when (gestureType) {
-            KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY ->
-                enableMoveToNextDisplayShortcut()
-            KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
-            KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
-            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
-            KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW ->
-                DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue &&
-                    manageKeyGestures()
-            else -> false
-        }
-
     //  TODO: b/364154795 - wait for the completion of moveToNextDisplay transition, otherwise it
     //  will pick a wrong task when a user quickly perform other actions with keyboard shortcuts
     //  after moveToNextDisplay, and move this to FocusTransitionObserver class.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandler.kt
index fbf170f13..844a1f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandler.kt
@@ -17,22 +17,28 @@
 package com.android.wm.shell.desktopmode
 
 import android.animation.Animator
+import android.animation.AnimatorSet
 import android.animation.ValueAnimator
+import android.os.Handler
 import android.os.IBinder
 import android.view.Choreographer
 import android.view.SurfaceControl
 import android.window.TransitionInfo
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.shared.animation.Interpolators
 import com.android.wm.shell.transition.Transitions
 import kotlin.time.Duration.Companion.milliseconds
 
-/**
- * Transition handler for moving a window to a different display.
- */
+/** Transition handler for moving a window to a different display. */
 class DesktopModeMoveToDisplayTransitionHandler(
-    private val animationTransaction: SurfaceControl.Transaction
+    private val animationTransaction: SurfaceControl.Transaction,
+    private val interactionJankMonitor: InteractionJankMonitor,
+    private val shellMainHandler: Handler,
+    private val displayController: DisplayController,
 ) : Transitions.TransitionHandler {
 
     override fun handleRequest(
@@ -47,46 +53,65 @@
         finishTransaction: SurfaceControl.Transaction,
         finishCallback: Transitions.TransitionFinishCallback,
     ): Boolean {
-        val change = info.changes.find { it.startDisplayId != it.endDisplayId } ?: return false
-        ValueAnimator.ofFloat(0f, 1f)
-            .apply {
-                duration = ANIM_DURATION.inWholeMilliseconds
-                interpolator = Interpolators.LINEAR
-                addUpdateListener { animation ->
-                    animationTransaction
-                        .setAlpha(change.leash, animation.animatedValue as Float)
-                        .setFrameTimeline(Choreographer.getInstance().vsyncId)
-                        .apply()
-                }
-                addListener(
-                    object : Animator.AnimatorListener {
-                        override fun onAnimationStart(animation: Animator) {
-                            val endBounds = change.endAbsBounds
-                            startTransaction
-                                .setPosition(
-                                    change.leash,
-                                    endBounds.left.toFloat(),
-                                    endBounds.top.toFloat(),
-                                )
-                                .setWindowCrop(change.leash, endBounds.width(), endBounds.height())
-                                .apply()
-                        }
+        val changes = info.changes.filter { it.startDisplayId != it.endDisplayId }
+        if (changes.isEmpty()) return false
+        for (change in changes) {
+            val endBounds = change.endAbsBounds
+            // The position should be relative to the parent. For example, in ActivityEmbedding, the
+            // leash surface for the embedded Activity is parented to the container.
+            val endPosition = change.endRelOffset
+            startTransaction
+                .setPosition(change.leash, endPosition.x.toFloat(), endPosition.y.toFloat())
+                .setWindowCrop(change.leash, endBounds.width(), endBounds.height())
+        }
+        startTransaction.apply()
 
-                        override fun onAnimationEnd(animation: Animator) {
-                            finishTransaction.apply()
-                            finishCallback.onTransitionFinished(null)
-                        }
-
-                        override fun onAnimationCancel(animation: Animator) {
-                            finishTransaction.apply()
-                            finishCallback.onTransitionFinished(null)
-                        }
-
-                        override fun onAnimationRepeat(animation: Animator) = Unit
+        val animator = AnimatorSet()
+        animator.playTogether(
+            changes.map {
+                ValueAnimator.ofFloat(0f, 1f).apply {
+                    duration = ANIM_DURATION.inWholeMilliseconds
+                    interpolator = Interpolators.LINEAR
+                    addUpdateListener { animation ->
+                        animationTransaction
+                            .setAlpha(it.leash, animation.animatedValue as Float)
+                            .setFrameTimeline(Choreographer.getInstance().vsyncId)
+                            .apply()
                     }
-                )
+                }
             }
-            .start()
+        )
+
+        animator.addListener(
+            object : Animator.AnimatorListener {
+                override fun onAnimationStart(animation: Animator) {
+                    val displayContext =
+                        displayController.getDisplayContext(changes[0].endDisplayId)
+                    if (displayContext == null) return
+                    interactionJankMonitor.begin(
+                        changes[0].leash,
+                        displayContext,
+                        shellMainHandler,
+                        CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY,
+                    )
+                }
+
+                override fun onAnimationEnd(animation: Animator) {
+                    finishTransaction.apply()
+                    finishCallback.onTransitionFinished(null)
+                    interactionJankMonitor.end(CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY)
+                }
+
+                override fun onAnimationCancel(animation: Animator) {
+                    finishTransaction.apply()
+                    finishCallback.onTransitionFinished(null)
+                    interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY)
+                }
+
+                override fun onAnimationRepeat(animation: Animator) = Unit
+            }
+        )
+        animator.start()
         return true
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index c5ee313..a8b0baf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -18,10 +18,15 @@
 
 package com.android.wm.shell.desktopmode
 
-import android.annotation.DimenRes
 import android.app.ActivityManager.RunningTaskInfo
 import android.app.TaskInfo
-import android.content.Context
+import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
+import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.content.pm.ActivityInfo.LAUNCH_MULTIPLE
+import android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE
+import android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK
+import android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK
 import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
 import android.content.pm.ActivityInfo.isFixedOrientationLandscape
 import android.content.pm.ActivityInfo.isFixedOrientationPortrait
@@ -30,7 +35,8 @@
 import android.graphics.Rect
 import android.os.SystemProperties
 import android.util.Size
-import com.android.wm.shell.R
+import android.window.DesktopModeFlags
+import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
 import kotlin.math.ceil
@@ -256,12 +262,57 @@
     return taskBounds == stableBounds
 }
 
-/** Returns the app header height in desktop mode in pixels. */
-fun getAppHeaderHeight(context: Context): Int =
-    context.resources.getDimensionPixelSize(getAppHeaderHeightId())
+/**
+ * Returns the task bounds a launching task should inherit from an existing running instance.
+ * Returns null if there are no bounds to inherit.
+ */
+fun getInheritedExistingTaskBounds(
+    taskRepository: DesktopRepository,
+    shellTaskOrganizer: ShellTaskOrganizer,
+    task: RunningTaskInfo,
+    deskId: Int,
+): Rect? {
+    if (!DesktopModeFlags.INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES.isTrue) return null
+    val activeTask = taskRepository.getExpandedTasksIdsInDeskOrdered(deskId).firstOrNull()
+    if (activeTask == null) return null
+    val lastTask = shellTaskOrganizer.getRunningTaskInfo(activeTask)
+    val lastTaskTopActivity = lastTask?.topActivity
+    val currentTaskTopActivity = task.topActivity
+    val intentFlags = task.baseIntent.flags
+    val launchMode = task.topActivityInfo?.launchMode ?: LAUNCH_MULTIPLE
+    return when {
+        // No running task activity to inherit bounds from.
+        lastTaskTopActivity == null -> null
+        // No current top activity to set bounds for.
+        currentTaskTopActivity == null -> null
+        // Top task is not an instance of the launching activity, do not inherit its bounds.
+        lastTaskTopActivity.packageName != currentTaskTopActivity.packageName -> null
+        // Top task is an instance of launching activity. Activity will be launching in a new
+        // task with the existing task also being closed. Inherit existing task bounds to
+        // prevent new task jumping.
+        (isLaunchingNewTask(launchMode, intentFlags) && isClosingExitingInstance(intentFlags)) ->
+            lastTask.configuration.windowConfiguration.bounds
+        else -> null
+    }
+}
 
-/** Returns the resource id of the app header height in desktop mode. */
-@DimenRes fun getAppHeaderHeightId(): Int = R.dimen.desktop_mode_freeform_decor_caption_height
+/**
+ * Returns true if the launch mode or intent will result in a new task being created for the
+ * activity.
+ */
+private fun isLaunchingNewTask(launchMode: Int, intentFlags: Int) =
+    launchMode == LAUNCH_SINGLE_TASK ||
+        launchMode == LAUNCH_SINGLE_INSTANCE ||
+        launchMode == LAUNCH_SINGLE_INSTANCE_PER_TASK ||
+        (intentFlags and FLAG_ACTIVITY_NEW_TASK) != 0
+
+/**
+ * Returns true if the intent will result in an existing task instance being closed if a new one
+ * appears.
+ */
+private fun isClosingExitingInstance(intentFlags: Int) =
+    (intentFlags and FLAG_ACTIVITY_CLEAR_TASK) != 0 ||
+        (intentFlags and FLAG_ACTIVITY_MULTIPLE_TASK) == 0
 
 /**
  * Calculates the desired initial bounds for applications in desktop windowing. This is done as a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 7053990..1c5138f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -19,11 +19,15 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
+import static com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightPx;
 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_LEFT_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR;
 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR;
 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR;
 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR;
+import static com.android.wm.shell.shared.ShellSharedConstants.SMALL_TABLET_MAX_EDGE_DP;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,6 +36,8 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.util.Pair;
+import android.view.Display;
 import android.view.SurfaceControl;
 import android.window.DesktopModeFlags;
 
@@ -44,13 +50,17 @@
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.shared.annotations.ShellDesktopThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider;
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.windowdecor.tiling.SnapEventHandler;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * Animated visual indicator for Desktop Mode windowing transitions.
  */
@@ -119,6 +129,15 @@
     private final DragStartState mDragStartState;
     private final SnapEventHandler mSnapEventHandler;
 
+    private final boolean mUseSmallTabletRegions;
+    /**
+     * Ordered list of {@link Rect} zones that we will match an input coordinate against.
+     * List is traversed from first to last element. The first rect that contains the input event
+     * will be used and the matching {@link IndicatorType} is returned.
+     * Empty rect matches all.
+     */
+    private final List<Pair<Rect, IndicatorType>> mSortedRegions;
+
     public DesktopModeVisualIndicator(@ShellDesktopThread ShellExecutor desktopExecutor,
             @ShellMainThread ShellExecutor mainExecutor,
             SyncTransactionQueue syncQueue,
@@ -128,6 +147,24 @@
             DragStartState dragStartState,
             @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider,
             SnapEventHandler snapEventHandler) {
+        this(desktopExecutor, mainExecutor, syncQueue, taskInfo, displayController, context,
+                taskSurface, taskDisplayAreaOrganizer, dragStartState, bubbleBoundsProvider,
+                snapEventHandler, useSmallTabletRegions(displayController, taskInfo),
+                isLeftRightSplit(context, displayController, taskInfo));
+    }
+
+    @VisibleForTesting
+    DesktopModeVisualIndicator(@ShellDesktopThread ShellExecutor desktopExecutor,
+            @ShellMainThread ShellExecutor mainExecutor,
+            SyncTransactionQueue syncQueue,
+            ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController,
+            Context context, SurfaceControl taskSurface,
+            RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer,
+            DragStartState dragStartState,
+            @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider,
+            SnapEventHandler snapEventHandler,
+            boolean useSmallTabletRegions,
+            boolean isLeftRightSplit) {
         SurfaceControl.Builder builder = new SurfaceControl.Builder();
         if (!DragStartState.isDragToDesktopStartState(dragStartState)
                 || !DesktopModeFlags.ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX.isTrue()) {
@@ -145,13 +182,46 @@
         mCurrentType = NO_INDICATOR;
         mDragStartState = dragStartState;
         mSnapEventHandler = snapEventHandler;
+        Display display = mDisplayController.getDisplay(mTaskInfo.displayId);
+        DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
         mVisualIndicatorViewContainer.createView(
                 mContext,
-                mDisplayController.getDisplay(mTaskInfo.displayId),
-                mDisplayController.getDisplayLayout(mTaskInfo.displayId),
+                display,
+                displayLayout,
                 mTaskInfo,
                 taskSurface
         );
+
+        mUseSmallTabletRegions = useSmallTabletRegions;
+
+        if (useSmallTabletRegions) {
+            mSortedRegions = initSmallTabletRegions(displayLayout, isLeftRightSplit);
+        } else {
+            // TODO(b/401596837): add support for initializing regions for large tablets
+            mSortedRegions = Collections.emptyList();
+        }
+    }
+
+    private static boolean useSmallTabletRegions(DisplayController displayController,
+            ActivityManager.RunningTaskInfo taskInfo) {
+        if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+            // Small tablet regions get enabled with bubbles feature
+            return false;
+        }
+        Display display = displayController.getDisplay(taskInfo.displayId);
+        DisplayLayout displayLayout = displayController.getDisplayLayout(taskInfo.displayId);
+        if (displayLayout == null) return false;
+        return displayLayout.pxToDp(display.getMaximumSizeDimension()) < SMALL_TABLET_MAX_EDGE_DP;
+    }
+
+    private static boolean isLeftRightSplit(Context context, DisplayController displayController,
+            ActivityManager.RunningTaskInfo taskInfo) {
+        DisplayLayout layout = displayController.getDisplayLayout(taskInfo.displayId);
+        boolean landscape = layout != null && layout.isLandscape();
+        boolean leftRightSplitInPortrait = SplitScreenUtils.allowLeftRightSplitInPortrait(
+                context.getResources());
+        return SplitScreenUtils.isLeftRightSplit(leftRightSplitInPortrait,
+                /* isLargeScreen= */ true, landscape);
     }
 
     /** Start the fade out animation, running the callback on the main thread once it is done. */
@@ -175,6 +245,7 @@
 
     /** Start the fade-in animation. */
     void fadeInIndicator() {
+        if (mCurrentType == NO_INDICATOR) return;
         mVisualIndicatorViewContainer.fadeInIndicator(
                 mDisplayController.getDisplayLayout(mTaskInfo.displayId), mCurrentType,
                 mTaskInfo.displayId);
@@ -186,34 +257,44 @@
      */
     @NonNull
     IndicatorType updateIndicatorType(PointF inputCoordinates) {
+        final IndicatorType result;
+        if (mUseSmallTabletRegions) {
+            result = getIndicatorSmallTablet(inputCoordinates);
+        } else {
+            result = getIndicatorLargeTablet(inputCoordinates);
+        }
+        if (mDragStartState != DragStartState.DRAGGED_INTENT) {
+            mVisualIndicatorViewContainer.transitionIndicator(
+                    mTaskInfo, mDisplayController, mCurrentType, result
+            );
+            mCurrentType = result;
+        }
+        return result;
+    }
+
+    @NonNull
+    private IndicatorType getIndicatorLargeTablet(PointF inputCoordinates) {
+        // TODO(b/401596837): cache the regions to avoid recalculating on each motion event
         final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
         // Perform a quick check first: any input off the left edge of the display should be split
         // left, and split right for the right edge. This is universal across all drag event types.
         if (inputCoordinates.x < 0) return TO_SPLIT_LEFT_INDICATOR;
         if (inputCoordinates.x > layout.width()) return TO_SPLIT_RIGHT_INDICATOR;
-        IndicatorType result;
-        if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()
-                && !DesktopModeStatus.canEnterDesktopMode(mContext)) {
-            // If desktop is not available, default to "no indicator"
-            result = NO_INDICATOR;
-        } else {
-            // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
-            // In drags not originating on a freeform caption, we should default to a TO_DESKTOP
-            // indicator.
-            result = mDragStartState == DragStartState.FROM_FREEFORM
+        // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
+        // In drags not originating on a freeform caption, we should default to a TO_DESKTOP
+        // indicator.
+        IndicatorType result = mDragStartState == DragStartState.FROM_FREEFORM
                     ? NO_INDICATOR
                     : TO_DESKTOP_INDICATOR;
-        }
         final int transitionAreaWidth = mContext.getResources().getDimensionPixelSize(
                 com.android.wm.shell.R.dimen.desktop_mode_transition_region_thickness);
         // Because drags in freeform use task position for indicator calculation, we need to
         // account for the possibility of the task going off the top of the screen by captionHeight
-        final int captionHeight = mContext.getResources().getDimensionPixelSize(
-                com.android.wm.shell.R.dimen.desktop_mode_freeform_decor_caption_height);
+        final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext);
         final Region fullscreenRegion = calculateFullscreenRegion(layout, captionHeight);
-        final Region splitLeftRegion = calculateSplitLeftRegion(layout, transitionAreaWidth,
+        final Rect splitLeftRegion = calculateSplitLeftRegion(layout, transitionAreaWidth,
                 captionHeight);
-        final Region splitRightRegion = calculateSplitRightRegion(layout, transitionAreaWidth,
+        final Rect splitRightRegion = calculateSplitRightRegion(layout, transitionAreaWidth,
                 captionHeight);
         final int x = (int) inputCoordinates.x;
         final int y = (int) inputCoordinates.y;
@@ -230,18 +311,23 @@
             if (calculateBubbleLeftRegion(layout).contains(x, y)) {
                 result = IndicatorType.TO_BUBBLE_LEFT_INDICATOR;
             } else if (calculateBubbleRightRegion(layout).contains(x, y)) {
-                result = IndicatorType.TO_BUBBLE_RIGHT_INDICATOR;
+                result = TO_BUBBLE_RIGHT_INDICATOR;
             }
         }
-        if (mDragStartState != DragStartState.DRAGGED_INTENT) {
-            mVisualIndicatorViewContainer.transitionIndicator(
-                    mTaskInfo, mDisplayController, mCurrentType, result
-            );
-            mCurrentType = result;
-        }
         return result;
     }
 
+    @NonNull
+    private IndicatorType getIndicatorSmallTablet(PointF inputCoordinates) {
+        for (Pair<Rect, IndicatorType> region : mSortedRegions) {
+            if (region.first.isEmpty()) return region.second; // empty rect matches all
+            if (region.first.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
+                return region.second;
+            }
+        }
+        return NO_INDICATOR;
+    }
+
     /**
      * Returns the [DragStartState] of the visual indicator.
      */
@@ -280,53 +366,79 @@
     }
 
     @VisibleForTesting
-    Region calculateSplitLeftRegion(DisplayLayout layout,
+    Rect calculateSplitLeftRegion(DisplayLayout layout,
             int transitionEdgeWidth, int captionHeight) {
-        final Region region = new Region();
         // In freeform, keep the top corners clear.
         int transitionHeight = mDragStartState == DragStartState.FROM_FREEFORM
                 ? mContext.getResources().getDimensionPixelSize(
                 com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) :
                 -captionHeight;
-        region.union(new Rect(0, transitionHeight, transitionEdgeWidth, layout.height()));
-        return region;
+        return new Rect(0, transitionHeight, transitionEdgeWidth, layout.height());
     }
 
     @VisibleForTesting
-    Region calculateSplitRightRegion(DisplayLayout layout,
+    Rect calculateSplitRightRegion(DisplayLayout layout,
             int transitionEdgeWidth, int captionHeight) {
-        final Region region = new Region();
         // In freeform, keep the top corners clear.
         int transitionHeight = mDragStartState == DragStartState.FROM_FREEFORM
                 ? mContext.getResources().getDimensionPixelSize(
                 com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) :
                 -captionHeight;
-        region.union(new Rect(layout.width() - transitionEdgeWidth, transitionHeight,
-                layout.width(), layout.height()));
-        return region;
-    }
-
-    @VisibleForTesting
-    Region calculateBubbleLeftRegion(DisplayLayout layout) {
-        int regionWidth = mContext.getResources().getDimensionPixelSize(
-                com.android.wm.shell.R.dimen.bubble_transform_area_width);
-        int regionHeight = mContext.getResources().getDimensionPixelSize(
-                com.android.wm.shell.R.dimen.bubble_transform_area_height);
-        return new Region(0, layout.height() - regionHeight, regionWidth, layout.height());
-    }
-
-    @VisibleForTesting
-    Region calculateBubbleRightRegion(DisplayLayout layout) {
-        int regionWidth = mContext.getResources().getDimensionPixelSize(
-                com.android.wm.shell.R.dimen.bubble_transform_area_width);
-        int regionHeight = mContext.getResources().getDimensionPixelSize(
-                com.android.wm.shell.R.dimen.bubble_transform_area_height);
-        return new Region(layout.width() - regionWidth, layout.height() - regionHeight,
+        return new Rect(layout.width() - transitionEdgeWidth, transitionHeight,
                 layout.width(), layout.height());
     }
 
     @VisibleForTesting
+    Rect calculateBubbleLeftRegion(DisplayLayout layout) {
+        int regionSize = getBubbleRegionSize();
+        return new Rect(0, layout.height() - regionSize, regionSize, layout.height());
+    }
+
+    @VisibleForTesting
+    Rect calculateBubbleRightRegion(DisplayLayout layout) {
+        int regionSize = getBubbleRegionSize();
+        return new Rect(layout.width() - regionSize, layout.height() - regionSize,
+                layout.width(), layout.height());
+    }
+
+    private int getBubbleRegionSize() {
+        int resId = mUseSmallTabletRegions
+                ? com.android.wm.shell.shared.R.dimen.drag_zone_bubble_fold
+                : com.android.wm.shell.shared.R.dimen.drag_zone_bubble_tablet;
+        return mContext.getResources().getDimensionPixelSize(resId);
+    }
+
+    @VisibleForTesting
     Rect getIndicatorBounds() {
         return mVisualIndicatorViewContainer.getIndicatorBounds();
     }
+
+    private List<Pair<Rect, IndicatorType>> initSmallTabletRegions(DisplayLayout layout,
+            boolean isLeftRightSplit) {
+        boolean dragFromFullscreen = mDragStartState == DragStartState.FROM_FULLSCREEN;
+        boolean dragFromSplit = mDragStartState == DragStartState.FROM_SPLIT;
+        if (isLeftRightSplit && (dragFromFullscreen || dragFromSplit)) {
+            int splitRegionWidth = mContext.getResources().getDimensionPixelSize(
+                    com.android.wm.shell.shared.R.dimen.drag_zone_h_split_from_app_width_fold);
+            return Arrays.asList(
+                    new Pair<>(calculateBubbleLeftRegion(layout), TO_BUBBLE_LEFT_INDICATOR),
+                    new Pair<>(calculateBubbleRightRegion(layout), TO_BUBBLE_RIGHT_INDICATOR),
+                    new Pair<>(calculateSplitLeftRegion(layout, splitRegionWidth,
+                            /* captionHeight= */ 0), TO_SPLIT_LEFT_INDICATOR),
+                    new Pair<>(calculateSplitRightRegion(layout, splitRegionWidth,
+                            /* captionHeight= */ 0), TO_SPLIT_RIGHT_INDICATOR),
+                    new Pair<>(new Rect(), TO_FULLSCREEN_INDICATOR) // default to fullscreen
+            );
+        }
+        if (dragFromFullscreen) {
+            // If left/right split is not available, we can only drag fullscreen tasks
+            // TODO(b/401352409): add support for top/bottom split zones
+            return Arrays.asList(
+                    new Pair<>(calculateBubbleLeftRegion(layout), TO_BUBBLE_LEFT_INDICATOR),
+                    new Pair<>(calculateBubbleRightRegion(layout), TO_BUBBLE_RIGHT_INDICATOR),
+                    new Pair<>(new Rect(), TO_FULLSCREEN_INDICATOR) // default to fullscreen
+            );
+        }
+        return Collections.emptyList();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt
new file mode 100644
index 0000000..efd3866
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.desktopmode
+
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.os.IBinder
+import android.window.DesktopModeFlags
+import android.window.TransitionInfo
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+
+/**
+ * Observer of PiP in Desktop Mode transitions. At the moment, this is specifically tracking a PiP
+ * transition for a task that is entering PiP via the minimize button on the caption bar.
+ */
+class DesktopPipTransitionObserver {
+    private val pendingPipTransitions = mutableMapOf<IBinder, PendingPipTransition>()
+
+    /** Adds a pending PiP transition to be tracked. */
+    fun addPendingPipTransition(transition: PendingPipTransition) {
+        if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue) return
+        pendingPipTransitions[transition.token] = transition
+    }
+
+    /**
+     * Called when any transition is ready, which may include transitions not tracked by this
+     * observer.
+     */
+    fun onTransitionReady(transition: IBinder, info: TransitionInfo) {
+        if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue) return
+        val pipTransition = pendingPipTransitions.remove(transition) ?: return
+
+        logD("Desktop PiP transition ready: %s", transition)
+        for (change in info.changes) {
+            val taskInfo = change.taskInfo
+            if (taskInfo == null || taskInfo.taskId == -1) {
+                continue
+            }
+
+            if (
+                taskInfo.taskId == pipTransition.taskId &&
+                    taskInfo.windowingMode == WINDOWING_MODE_PINNED
+            ) {
+                logD("Desktop PiP transition was successful")
+                pipTransition.onSuccess()
+                return
+            }
+        }
+        logD("Change with PiP task not found in Desktop PiP transition; likely failed")
+    }
+
+    /**
+     * Data tracked for a pending PiP transition.
+     *
+     * @property token the PiP transition that is started.
+     * @property taskId task id of the task entering PiP.
+     * @property onSuccess callback to be invoked if the PiP transition is successful.
+     */
+    data class PendingPipTransition(val token: IBinder, val taskId: Int, val onSuccess: () -> Unit)
+
+    private fun logD(msg: String, vararg arguments: Any?) {
+        ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+
+    private companion object {
+        private const val TAG = "DesktopPipTransitionObserver"
+    }
+}
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 bbb300e..e77acfb 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
@@ -68,7 +68,8 @@
      * @property topTransparentFullscreenTaskId the task id of any current top transparent
      *   fullscreen task launched on top of the desk. Cleared when the transparent task is closed or
      *   sent to back. (top is at index 0).
-     * @property pipTaskId the task id of PiP task entered while in Desktop Mode.
+     * @property leftTiledTaskId task id of the task tiled on the left.
+     * @property rightTiledTaskId task id of the task tiled on the right.
      */
     private data class Desk(
         val deskId: Int,
@@ -81,7 +82,8 @@
         val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
         var fullImmersiveTaskId: Int? = null,
         var topTransparentFullscreenTaskId: Int? = null,
-        var pipTaskId: Int? = null,
+        var leftTiledTaskId: Int? = null,
+        var rightTiledTaskId: Int? = null,
     ) {
         fun deepCopy(): Desk =
             Desk(
@@ -94,7 +96,8 @@
                 freeformTasksInZOrder = ArrayList(freeformTasksInZOrder),
                 fullImmersiveTaskId = fullImmersiveTaskId,
                 topTransparentFullscreenTaskId = topTransparentFullscreenTaskId,
-                pipTaskId = pipTaskId,
+                leftTiledTaskId = leftTiledTaskId,
+                rightTiledTaskId = rightTiledTaskId,
             )
 
         // TODO: b/362720497 - remove when multi-desktops is enabled where instances aren't
@@ -107,7 +110,8 @@
             freeformTasksInZOrder.clear()
             fullImmersiveTaskId = null
             topTransparentFullscreenTaskId = null
-            pipTaskId = null
+            leftTiledTaskId = null
+            rightTiledTaskId = null
         }
     }
 
@@ -127,9 +131,6 @@
     /* Tracks last bounds of task before toggled to immersive state. */
     private val boundsBeforeFullImmersiveByTaskId = SparseArray<Rect>()
 
-    /* Callback for when a pending PiP transition has been aborted. */
-    private var onPipAbortedCallback: ((Int, Int) -> Unit)? = null
-
     private var desktopGestureExclusionListener: Consumer<Region>? = null
     private var desktopGestureExclusionExecutor: Executor? = null
 
@@ -235,6 +236,10 @@
     /** Returns the default desk in the given display. */
     private fun getDefaultDesk(displayId: Int): Desk? = desktopData.getDefaultDesk(displayId)
 
+    /** Returns whether the given desk is active in its display. */
+    fun isDeskActive(deskId: Int): Boolean =
+        desktopData.getAllActiveDesks().any { desk -> desk.deskId == deskId }
+
     /** Sets the given desk as the active one in the given display. */
     fun setActiveDesk(displayId: Int, deskId: Int) {
         logD("setActiveDesk for displayId=%d and deskId=%d", displayId, deskId)
@@ -271,6 +276,106 @@
         }
     }
 
+    /** Register a left tiled task to desktop state. */
+    fun addLeftTiledTask(displayId: Int, taskId: Int) {
+        logD("addLeftTiledTask for displayId=%d, taskId=%d", displayId, taskId)
+        val activeDesk =
+            checkNotNull(desktopData.getDefaultDesk(displayId)) {
+                "Expected desk in display: $displayId"
+            }
+        addLeftTiledTaskToDesk(displayId, taskId, activeDesk.deskId)
+    }
+
+    private fun addLeftTiledTaskToDesk(displayId: Int, taskId: Int, deskId: Int) {
+        logD("addLeftTiledTaskToDesk for displayId=%d, taskId=%d", displayId, taskId)
+        val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+        desk.leftTiledTaskId = taskId
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
+            updatePersistentRepository(displayId)
+        }
+    }
+
+    /** Register a right tiled task to desktop state. */
+    fun addRightTiledTask(displayId: Int, taskId: Int) {
+        logD("addRightTiledTask for displayId=%d, taskId=%d", displayId, taskId)
+        val activeDesk =
+            checkNotNull(desktopData.getDefaultDesk(displayId)) {
+                "Expected desk in display: $displayId"
+            }
+        addRightTiledTaskToDesk(displayId, taskId, activeDesk.deskId)
+    }
+
+    private fun addRightTiledTaskToDesk(displayId: Int, taskId: Int, deskId: Int) {
+        logD("addRightTiledTaskToDesk for displayId=%d, taskId=%d", displayId, taskId)
+        val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+        desk.rightTiledTaskId = taskId
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
+            updatePersistentRepository(displayId)
+        }
+    }
+
+    /** Gets a registered left tiled task to desktop state or returns null. */
+    fun getLeftTiledTask(displayId: Int): Int? {
+        logD("getLeftTiledTask for displayId=%d", displayId)
+        val activeDesk =
+            checkNotNull(desktopData.getDefaultDesk(displayId)) {
+                "Expected desk in display: $displayId"
+            }
+        val deskId = activeDesk.deskId
+        val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+        return desk.leftTiledTaskId
+    }
+
+    /** gets a registered right tiled task to desktop state or returns null. */
+    fun getRightTiledTask(displayId: Int): Int? {
+        logD("getRightTiledTask for displayId=%d", displayId)
+        val activeDesk =
+            checkNotNull(desktopData.getDefaultDesk(displayId)) {
+                "Expected desk in display: $displayId"
+            }
+        val deskId = activeDesk.deskId
+        val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+        return desk.rightTiledTaskId
+    }
+
+    /* Unregisters a left tiled task from desktop state. */
+    fun removeLeftTiledTask(displayId: Int) {
+        logD("removeLeftTiledTask for displayId=%d", displayId)
+        val activeDesk =
+            checkNotNull(desktopData.getDefaultDesk(displayId)) {
+                "Expected desk in display: $displayId"
+            }
+        removeLeftTiledTaskFromDesk(displayId, activeDesk.deskId)
+    }
+
+    private fun removeLeftTiledTaskFromDesk(displayId: Int, deskId: Int) {
+        logD("removeLeftTiledTaskToDesk for displayId=%d", displayId)
+        val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+        desk.leftTiledTaskId = null
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
+            updatePersistentRepository(displayId)
+        }
+    }
+
+    /* Unregisters a right tiled task from desktop state. */
+    fun removeRightTiledTask(displayId: Int) {
+        logD("removeRightTiledTask for displayId=%d", displayId)
+        val activeDesk =
+            checkNotNull(desktopData.getDefaultDesk(displayId)) {
+                "Expected desk in display: $displayId"
+            }
+        removeRightTiledTaskFromDesk(displayId, activeDesk.deskId)
+    }
+
+    private fun removeRightTiledTaskFromDesk(displayId: Int, deskId: Int) {
+        logD("removeRightTiledTaskFromDesk for displayId=%d", displayId)
+        val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+        desk.rightTiledTaskId = null
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
+            updatePersistentRepository(displayId)
+        }
+    }
+
     /** Returns the id of the active desk in the given display, if any. */
     fun getActiveDeskId(displayId: Int): Int? = desktopData.getActiveDesk(displayId)?.deskId
 
@@ -335,7 +440,7 @@
         val affectedDisplays = mutableSetOf<Int>()
         desktopData
             .desksSequence()
-            .filter { desk -> desk.displayId != excludedDeskId }
+            .filter { desk -> desk.deskId != excludedDeskId }
             .forEach { desk ->
                 val removed = removeActiveTaskFromDesk(desk.deskId, taskId, notifyListeners = false)
                 if (removed) {
@@ -485,7 +590,7 @@
     fun getExpandedTasksOrdered(displayId: Int): List<Int> =
         getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) }
 
-    @VisibleForTesting
+    /** Returns all active non-minimized tasks for [deskId] ordered from top to bottom. */
     fun getExpandedTasksIdsInDeskOrdered(deskId: Int): List<Int> =
         getFreeformTasksIdsInDeskInZOrder(deskId).filter { !isMinimizedTask(it) }
 
@@ -607,57 +712,6 @@
     }
 
     /**
-     * Set whether the given task is the Desktop-entered PiP task in this display's active desk.
-     *
-     * TODO: b/389960283 - add explicit [deskId] argument.
-     */
-    fun setTaskInPip(displayId: Int, taskId: Int, enterPip: Boolean) {
-        val activeDesk =
-            desktopData.getActiveDesk(displayId)
-                ?: error("Expected active desk in display: $displayId")
-        if (enterPip) {
-            activeDesk.pipTaskId = taskId
-        } else {
-            activeDesk.pipTaskId =
-                if (activeDesk.pipTaskId == taskId) null
-                else {
-                    logW(
-                        "setTaskInPip: taskId=%d did not match saved taskId=%d",
-                        taskId,
-                        activeDesk.pipTaskId,
-                    )
-                    activeDesk.pipTaskId
-                }
-        }
-    }
-
-    /**
-     * Returns whether the given task is the Desktop-entered PiP task in this display's active desk.
-     *
-     * TODO: b/389960283 - add explicit [deskId] argument.
-     */
-    fun isTaskMinimizedPipInDisplay(displayId: Int, taskId: Int): Boolean =
-        desktopData.getActiveDesk(displayId)?.pipTaskId == taskId
-
-    /**
-     * Saves callback to handle a pending PiP transition being aborted.
-     *
-     * TODO: b/389960283 - add explicit [deskId] argument.
-     */
-    fun setOnPipAbortedCallback(callbackIfPipAborted: ((displayId: Int, pipTaskId: Int) -> Unit)?) {
-        onPipAbortedCallback = callbackIfPipAborted
-    }
-
-    /**
-     * Invokes callback to handle a pending PiP transition with the given task id being aborted.
-     *
-     * TODO: b/389960283 - add explicit [deskId] argument.
-     */
-    fun onPipAborted(displayId: Int, pipTaskId: Int) {
-        onPipAbortedCallback?.invoke(displayId, pipTaskId)
-    }
-
-    /**
      * Set whether the given task is the full-immersive task in this display's active desk.
      *
      * TODO: b/389960283 - consider forcing callers to use [setTaskInFullImmersiveStateInDesk] with
@@ -712,12 +766,15 @@
     }
 
     /**
-     * Returns the top transparent fullscreen task id for a given display's active desk, or null.
+     * Returns the top transparent fullscreen task id for a given display, or null.
      *
      * TODO: b/389960283 - add explicit [deskId] argument.
      */
     fun getTopTransparentFullscreenTaskId(displayId: Int): Int? =
-        desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId
+        desktopData
+            .desksSequence(displayId)
+            .mapNotNull { it.topTransparentFullscreenTaskId }
+            .firstOrNull()
 
     /**
      * Clears the top transparent fullscreen task id info for a given display's active desk.
@@ -814,7 +871,6 @@
     }
 
     /** Minimizes the task in its desk. */
-    @VisibleForTesting
     fun minimizeTaskInDesk(displayId: Int, deskId: Int, taskId: Int) {
         logD("MinimizeTaskInDesk: displayId=%d deskId=%d, task=%d", displayId, deskId, taskId)
         desktopData.getDesk(deskId)?.minimizedTasks?.add(taskId)
@@ -929,6 +985,12 @@
                 listener.onDeskRemoved(displayId = desk.displayId, deskId = desk.deskId)
             }
         }
+        if (
+            DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue &&
+                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+        ) {
+            removeDeskFromPersistentRepository(desk)
+        }
         return activeTasks
     }
 
@@ -1018,6 +1080,8 @@
                 visibleTasks = desk.visibleTasks,
                 minimizedTasks = desk.minimizedTasks,
                 freeformTasksInZOrder = desk.freeformTasksInZOrder,
+                leftTiledTask = desk.leftTiledTaskId,
+                rightTiledTask = desk.rightTiledTaskId,
             )
         } catch (exception: Exception) {
             logE(
@@ -1027,6 +1091,24 @@
         }
     }
 
+    private fun removeDeskFromPersistentRepository(desk: Desk) {
+        mainCoroutineScope.launch {
+            try {
+                logD(
+                    "updatePersistentRepositoryForRemovedDesk user=%d desk=%d",
+                    userId,
+                    desk.deskId,
+                )
+                persistentRepository.removeDesktop(userId = userId, desktopId = desk.deskId)
+            } catch (throwable: Throwable) {
+                logE(
+                    "An exception occurred while updating the persistent repository \n%s",
+                    throwable.stackTrace,
+                )
+            }
+        }
+    }
+
     internal fun dump(pw: PrintWriter, prefix: String) {
         val innerPrefix = "$prefix  "
         pw.println("${prefix}DesktopRepository")
@@ -1045,6 +1127,7 @@
             }
             .forEach { (displayId, activeDeskId, desks) ->
                 pw.println("${prefix}Display #$displayId:")
+                pw.println("${innerPrefix}numOfDesks=${desks.size}")
                 pw.println("${innerPrefix}activeDesk=$activeDeskId")
                 pw.println("${innerPrefix}desks:")
                 val desksPrefix = "$innerPrefix  "
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index fcdf4af..e04d14459 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -53,24 +53,12 @@
         // Case 1: When the task change is from a task in the desktop repository which is now
         // fullscreen,
         // remove the task from the desktop repository since it is no longer a freeform task.
-        if (!isFreeformTask(taskInfo)) {
-            if (desktopRepository.isActiveTask(taskInfo.taskId)) {
-                desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
-            }
-        } else { // Task change is a freeform task
-            if (!desktopRepository.isActiveTask(taskInfo.taskId)) {
-                // Case 2: When the task change is a freeform visible task, but the task is not
-                // yet active in the desktop repository, adds task to desktop repository.
-                desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
-            } else {
-                // Case 3: When the task change is a freeform task which already exists as an active
-                // task in the desktop repository, updates the task state.
-                desktopRepository.updateTask(
-                    taskInfo.displayId,
-                    taskInfo.taskId,
-                    taskInfo.isVisible,
-                )
-            }
+        if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
+            desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
+        } else if (isFreeformTask(taskInfo)) {
+            // If the task is already active in the repository, then moves task to the front,
+            // else adds the task.
+            desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
         }
     }
 
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 301b79a..0920f27 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
@@ -43,11 +43,13 @@
 import android.os.SystemProperties
 import android.os.UserHandle
 import android.util.Slog
+import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.DragEvent
 import android.view.MotionEvent
 import android.view.SurfaceControl
 import android.view.SurfaceControl.Transaction
+import android.view.WindowManager
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.view.WindowManager.TRANSIT_CLOSE
 import android.view.WindowManager.TRANSIT_NONE
@@ -73,6 +75,7 @@
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE
 import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightPx
 import com.android.internal.protolog.ProtoLog
 import com.android.internal.util.LatencyTracker
 import com.android.window.flags.Flags
@@ -84,6 +87,7 @@
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
 import com.android.wm.shell.common.ExternalInterfaceBinder
+import com.android.wm.shell.common.HomeIntentProvider
 import com.android.wm.shell.common.MultiInstanceHelper
 import com.android.wm.shell.common.MultiInstanceHelper.Companion.getComponent
 import com.android.wm.shell.common.RemoteCallable
@@ -111,6 +115,9 @@
 import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
 import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
 import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
+import com.android.wm.shell.desktopmode.multidesks.createDesk
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer.DeskRecreationFactory
 import com.android.wm.shell.draganddrop.DragAndDropController
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -190,6 +197,7 @@
     private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
     private val desktopImmersiveController: DesktopImmersiveController,
     private val userRepositories: DesktopUserRepositories,
+    desktopRepositoryInitializer: DesktopRepositoryInitializer,
     private val recentsTransitionHandler: RecentsTransitionHandler,
     private val multiInstanceHelper: MultiInstanceHelper,
     @ShellMainThread private val mainExecutor: ShellExecutor,
@@ -206,10 +214,12 @@
     private val overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver,
     private val desksOrganizer: DesksOrganizer,
     private val desksTransitionObserver: DesksTransitionObserver,
+    private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
     private val userProfileContexts: UserProfileContexts,
     private val desktopModeCompatPolicy: DesktopModeCompatPolicy,
     private val dragToDisplayTransitionHandler: DragToDisplayTransitionHandler,
     private val moveToDisplayTransitionHandler: DesktopModeMoveToDisplayTransitionHandler,
+    private val homeIntentProvider: HomeIntentProvider,
 ) :
     RemoteCallable<DesktopTasksController>,
     Transitions.TransitionHandler,
@@ -235,6 +245,10 @@
                 removeVisualIndicator()
             }
 
+            override fun onTransitionInterrupted() {
+                removeVisualIndicator()
+            }
+
             private fun removeVisualIndicator() {
                 visualIndicator?.fadeOutIndicator { releaseVisualIndicator() }
             }
@@ -267,6 +281,19 @@
         }
         userId = ActivityManager.getCurrentUser()
         taskRepository = userRepositories.getProfile(userId)
+
+        if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+            desktopRepositoryInitializer.deskRecreationFactory =
+                DeskRecreationFactory { deskUserId, destinationDisplayId, deskId ->
+                    if (deskUserId != userId) {
+                        // TODO: b/400984250 - add multi-user support for multi-desk restoration.
+                        logW("Tried to recreated desk of another user.")
+                        deskId
+                    } else {
+                        desksOrganizer.createDesk(destinationDisplayId)
+                    }
+                }
+        }
     }
 
     private fun onInit() {
@@ -279,6 +306,8 @@
             this,
         )
         shellController.addUserChangeListener(this)
+        // Update the current user id again because it might be updated between init and onInit().
+        updateCurrentUser(ActivityManager.getCurrentUser())
         transitions.addHandler(this)
         dragToDesktopTransitionHandler.dragToDesktopStateListener = dragToDesktopStateListener
         recentsTransitionHandler.addTransitionStateListener(
@@ -438,6 +467,10 @@
 
     /** Creates a new desk in the given display. */
     fun createDesk(displayId: Int) {
+        if (displayId == Display.INVALID_DISPLAY) {
+            logW("createDesk attempt with invalid displayId", displayId)
+            return
+        }
         if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
             desksOrganizer.createDesk(displayId) { deskId ->
                 taskRepository.addDesk(displayId = displayId, deskId = deskId)
@@ -626,6 +659,7 @@
         taskInfo: RunningTaskInfo,
         dragToDesktopValueAnimator: MoveToDesktopAnimator,
         taskSurface: SurfaceControl,
+        dragInterruptedCallback: Runnable,
     ) {
         logV("startDragToDesktop taskId=%d", taskInfo.taskId)
         val jankConfigBuilder =
@@ -641,6 +675,7 @@
             taskInfo,
             dragToDesktopValueAnimator,
             visualIndicator,
+            dragInterruptedCallback,
         )
     }
 
@@ -761,8 +796,31 @@
 
     fun minimizeTask(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
         val wct = WindowContainerTransaction()
+        val taskId = taskInfo.taskId
+        val displayId = taskInfo.displayId
+        val deskId =
+            taskRepository.getDeskIdForTask(taskInfo.taskId)
+                ?: if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+                    logW("minimizeTask: desk not found for task: ${taskInfo.taskId}")
+                    return
+                } else {
+                    getDefaultDeskId(taskInfo.displayId)
+                }
+        val isLastTask =
+            if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+                taskRepository.isOnlyVisibleNonClosingTaskInDesk(
+                    taskId = taskId,
+                    deskId = checkNotNull(deskId) { "Expected non-null deskId" },
+                    displayId = displayId,
+                )
+            } else {
+                taskRepository.isOnlyVisibleNonClosingTask(taskId = taskId, displayId = displayId)
+            }
+        val isMinimizingToPip =
+            DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
+                desktopPipTransitionObserver.isPresent &&
+                (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false)
 
-        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 =
@@ -776,75 +834,63 @@
                 )
             val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
             wct.merge(requestRes.second, true)
-            freeformTaskTransitionStarter.startPipTransition(wct)
-            taskRepository.setTaskInPip(taskInfo.displayId, taskInfo.taskId, enterPip = true)
-            taskRepository.setOnPipAbortedCallback { displayId, taskId ->
-                minimizeTaskInner(shellTaskOrganizer.getRunningTaskInfo(taskId)!!, minimizeReason)
-                taskRepository.setTaskInPip(displayId, taskId, enterPip = false)
-            }
-            return
-        }
 
-        minimizeTaskInner(taskInfo, minimizeReason)
-    }
-
-    private fun minimizeTaskInner(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
-        val taskId = taskInfo.taskId
-        val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
-        if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
-            logW("minimizeTaskInner: desk not found for task: ${taskInfo.taskId}")
-            return
-        }
-        val displayId = taskInfo.displayId
-        val wct = WindowContainerTransaction()
-
-        snapEventHandler.removeTaskIfTiled(displayId, taskId)
-        val willExitDesktop = willExitDesktop(taskId, displayId, forceExitDesktop = false)
-        val desktopExitRunnable =
-            performDesktopExitCleanUp(
-                wct = wct,
-                deskId = deskId,
-                displayId = displayId,
-                willExitDesktop = willExitDesktop,
-            )
-        // Notify immersive handler as it might need to exit immersive state.
-        val exitResult =
-            desktopImmersiveController.exitImmersiveIfApplicable(
-                wct = wct,
-                taskInfo = taskInfo,
-                reason = DesktopImmersiveController.ExitReason.MINIMIZED,
-            )
-        if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
-            desksOrganizer.minimizeTask(
-                wct = wct,
-                deskId = checkNotNull(deskId) { "Expected non-null deskId" },
-                task = taskInfo,
+            desktopPipTransitionObserver.get().addPendingPipTransition(
+                DesktopPipTransitionObserver.PendingPipTransition(
+                    token = freeformTaskTransitionStarter.startPipTransition(wct),
+                    taskId = taskInfo.taskId,
+                    onSuccess = {
+                        onDesktopTaskEnteredPip(
+                            taskId = taskId,
+                            deskId = deskId,
+                            displayId = taskInfo.displayId,
+                            taskIsLastVisibleTaskBeforePip = isLastTask,
+                        )
+                    },
+                )
             )
         } else {
-            wct.reorder(taskInfo.token, /* onTop= */ false)
-        }
-        val isLastTask =
-            if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
-                taskRepository.isOnlyVisibleNonClosingTaskInDesk(
-                    taskId = taskId,
-                    deskId = checkNotNull(deskId) { "Expected non-null deskId" },
+            snapEventHandler.removeTaskIfTiled(displayId, taskId)
+            val willExitDesktop = willExitDesktop(taskId, displayId, forceExitDesktop = false)
+            val desktopExitRunnable =
+                performDesktopExitCleanUp(
+                    wct = wct,
+                    deskId = deskId,
                     displayId = displayId,
+                    willExitDesktop = willExitDesktop,
+                )
+            // Notify immersive handler as it might need to exit immersive state.
+            val exitResult =
+                desktopImmersiveController.exitImmersiveIfApplicable(
+                    wct = wct,
+                    taskInfo = taskInfo,
+                    reason = DesktopImmersiveController.ExitReason.MINIMIZED,
+                )
+            if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+                desksOrganizer.minimizeTask(
+                    wct = wct,
+                    deskId = checkNotNull(deskId) { "Expected non-null deskId" },
+                    task = taskInfo,
                 )
             } else {
-                taskRepository.isOnlyVisibleNonClosingTask(taskId = taskId, displayId = displayId)
+                wct.reorder(taskInfo.token, /* onTop= */ false)
             }
-        val transition =
-            freeformTaskTransitionStarter.startMinimizedModeTransition(wct, taskId, isLastTask)
-        desktopTasksLimiter.ifPresent {
-            it.addPendingMinimizeChange(
-                transition = transition,
-                displayId = displayId,
-                taskId = taskId,
-                minimizeReason = minimizeReason,
-            )
+            val transition =
+                freeformTaskTransitionStarter.startMinimizedModeTransition(wct, taskId, isLastTask)
+            desktopTasksLimiter.ifPresent {
+                it.addPendingMinimizeChange(
+                    transition = transition,
+                    displayId = displayId,
+                    taskId = taskId,
+                    minimizeReason = minimizeReason,
+                )
+            }
+            exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
+            desktopExitRunnable?.invoke(transition)
         }
-        exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
-        desktopExitRunnable?.invoke(transition)
+        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
+            doesAnyTaskRequireTaskbarRounding(displayId, taskId)
+        )
     }
 
     /** Move a task with given `taskId` to fullscreen */
@@ -961,10 +1007,13 @@
                 .apply { launchWindowingMode = WINDOWING_MODE_FREEFORM }
                 .toBundle(),
         )
+        val deskId = taskRepository.getDeskIdForTask(taskId) ?: getDefaultDeskId(DEFAULT_DISPLAY)
         startLaunchTransition(
             TRANSIT_OPEN,
             wct,
             taskId,
+            deskId = deskId,
+            displayId = DEFAULT_DISPLAY,
             remoteTransition = remoteTransition,
             unminimizeReason = unminimizeReason,
         )
@@ -982,19 +1031,26 @@
         remoteTransition: RemoteTransition? = null,
         unminimizeReason: UnminimizeReason = UnminimizeReason.UNKNOWN,
     ) {
-        logV("moveTaskToFront taskId=%s", taskInfo.taskId)
+        val deskId =
+            taskRepository.getDeskIdForTask(taskInfo.taskId) ?: getDefaultDeskId(taskInfo.displayId)
+        logV("moveTaskToFront taskId=%s deskId=%s", taskInfo.taskId, deskId)
         // If a task is tiled, another task should be brought to foreground with it so let
         // tiling controller handle the request.
         if (snapEventHandler.moveTaskToFrontIfTiled(taskInfo)) {
             return
         }
         val wct = WindowContainerTransaction()
-        wct.reorder(taskInfo.token, /* onTop= */ true, /* includingParents= */ true)
+        if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+            desksOrganizer.reorderTaskToFront(wct, deskId, taskInfo)
+        } else {
+            wct.reorder(taskInfo.token, /* onTop= */ true, /* includingParents= */ true)
+        }
         startLaunchTransition(
             transitionType = TRANSIT_TO_FRONT,
             wct = wct,
             launchingTaskId = taskInfo.taskId,
             remoteTransition = remoteTransition,
+            deskId = deskId,
             displayId = taskInfo.displayId,
             unminimizeReason = unminimizeReason,
         )
@@ -1006,14 +1062,22 @@
         wct: WindowContainerTransaction,
         launchingTaskId: Int?,
         remoteTransition: RemoteTransition? = null,
-        displayId: Int = DEFAULT_DISPLAY,
+        deskId: Int,
+        displayId: Int,
         unminimizeReason: UnminimizeReason = UnminimizeReason.UNKNOWN,
     ): IBinder {
+        logV(
+            "startLaunchTransition type=%s launchingTaskId=%d deskId=%d displayId=%d",
+            WindowManager.transitTypeToString(transitionType),
+            launchingTaskId,
+            deskId,
+            displayId,
+        )
         // TODO: b/397619806 - Consolidate sharable logic with [handleFreeformTaskLaunch].
         var launchTransaction = wct
         val taskIdToMinimize =
             addAndGetMinimizeChanges(
-                displayId,
+                deskId,
                 launchTransaction,
                 newTaskId = launchingTaskId,
                 launchingNewIntent = launchingTaskId == null,
@@ -1027,21 +1091,20 @@
             )
         var activationRunOnTransitStart: RunOnTransitStart? = null
         val shouldActivateDesk =
-            (DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue ||
-                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) &&
-                !isDesktopModeShowing(displayId)
+            when {
+                DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ->
+                    !taskRepository.isDeskActive(deskId)
+                DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue -> {
+                    !isDesktopModeShowing(displayId)
+                }
+                else -> false
+            }
         if (shouldActivateDesk) {
-            val deskIdToActivate =
-                checkNotNull(
-                    launchingTaskId?.let { taskRepository.getDeskIdForTask(it) }
-                        ?: getDefaultDeskId(displayId)
-                )
             val activateDeskWct = WindowContainerTransaction()
             // TODO: b/391485148 - pass in the launching task here to apply task-limit policy,
             //  but make sure to not do it twice since it is also done at the start of this
             //  function.
-            activationRunOnTransitStart =
-                addDeskActivationChanges(deskIdToActivate, activateDeskWct)
+            activationRunOnTransitStart = addDeskActivationChanges(deskId, activateDeskWct)
             // Desk activation must be handled before app launch-related transactions.
             activateDeskWct.merge(launchTransaction, /* transfer= */ true)
             launchTransaction = activateDeskWct
@@ -1152,7 +1215,14 @@
             }
 
         wct.sendPendingIntent(pendingIntent, intent, ops.toBundle())
-        startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+        val deskId = getDefaultDeskId(displayId)
+        startLaunchTransition(
+            TRANSIT_OPEN,
+            wct,
+            launchingTaskId = null,
+            deskId = deskId,
+            displayId = displayId,
+        )
     }
 
     /**
@@ -1169,6 +1239,11 @@
             return
         }
 
+        if (splitScreenController.isTaskInSplitScreen(task.taskId)) {
+            moveSplitPairToDisplay(task, displayId)
+            return
+        }
+
         val wct = WindowContainerTransaction()
         val displayAreaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
         if (displayAreaInfo == null) {
@@ -1176,39 +1251,6 @@
             return
         }
 
-        // check if the task is part of splitscreen
-        if (
-            Flags.enableNonDefaultDisplaySplit() &&
-                Flags.enableMoveToNextDisplayShortcut() &&
-                splitScreenController.isTaskInSplitScreen(task.taskId)
-        ) {
-            val activeDeskId = taskRepository.getActiveDeskId(displayId)
-            logV("moveToDisplay: moving split root to displayId=%d", displayId)
-            val stageCoordinatorRootTaskToken =
-                splitScreenController.multiDisplayProvider.getDisplayRootForDisplayId(
-                    DEFAULT_DISPLAY
-                )
-            wct.reparent(stageCoordinatorRootTaskToken, displayAreaInfo.token, true /* onTop */)
-            val deactivationRunnable =
-                if (activeDeskId != null) {
-                    // Split is being placed on top of an existing desk in the target display. Make
-                    // sure it is cleaned up.
-                    performDesktopExitCleanUp(
-                        wct = wct,
-                        deskId = activeDeskId,
-                        displayId = displayId,
-                        willExitDesktop = true,
-                        shouldEndUpAtHome = false,
-                    )
-                } else {
-                    null
-                }
-            val transition =
-                transitions.startTransition(TRANSIT_CHANGE, wct, moveToDisplayTransitionHandler)
-            deactivationRunnable?.invoke(transition)
-            return
-        }
-
         val destinationDeskId = taskRepository.getDefaultDeskId(displayId)
         if (destinationDeskId == null) {
             logW("moveToDisplay: desk not found for display: $displayId")
@@ -1231,8 +1273,7 @@
             wct.reparent(task.token, displayAreaInfo.token, /* onTop= */ true)
         }
 
-        // TODO: b/391485148 - pass in the moving-to-desk |task| here to apply task-limit policy.
-        val activationRunnable = addDeskActivationChanges(destinationDeskId, wct)
+        val activationRunnable = addDeskActivationChanges(destinationDeskId, wct, task)
 
         if (Flags.enableDisplayFocusInShellTransitions()) {
             // Bring the destination display to top with includingParents=true, so that the
@@ -1270,6 +1311,56 @@
     }
 
     /**
+     * Move split pair associated with the [task] to display with [displayId].
+     *
+     * No-op if task is already on that display per [RunningTaskInfo.displayId].
+     */
+    private fun moveSplitPairToDisplay(task: RunningTaskInfo, displayId: Int) {
+        if (!splitScreenController.isTaskInSplitScreen(task.taskId)) {
+            return
+        }
+
+        if (!Flags.enableNonDefaultDisplaySplit() || !Flags.enableMoveToNextDisplayShortcut()) {
+            return
+        }
+
+        val wct = WindowContainerTransaction()
+        val displayAreaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
+        if (displayAreaInfo == null) {
+            logW("moveSplitPairToDisplay: display not found")
+            return
+        }
+
+        val activeDeskId = taskRepository.getActiveDeskId(displayId)
+        logV("moveSplitPairToDisplay: moving split root to displayId=%d", displayId)
+
+        val stageCoordinatorRootTaskToken =
+            splitScreenController.multiDisplayProvider.getDisplayRootForDisplayId(DEFAULT_DISPLAY)
+        if (stageCoordinatorRootTaskToken == null) {
+            return
+        }
+        wct.reparent(stageCoordinatorRootTaskToken, displayAreaInfo.token, true /* onTop */)
+
+        val deactivationRunnable =
+            if (activeDeskId != null) {
+                // Split is being placed on top of an existing desk in the target display. Make
+                // sure it is cleaned up.
+                performDesktopExitCleanUp(
+                    wct = wct,
+                    deskId = activeDeskId,
+                    displayId = displayId,
+                    willExitDesktop = true,
+                    shouldEndUpAtHome = false,
+                )
+            } else {
+                null
+            }
+        val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
+        deactivationRunnable?.invoke(transition)
+        return
+    }
+
+    /**
      * Quick-resizes a desktop task, toggling between a fullscreen state (represented by the stable
      * bounds) and a free floating state (either the last saved bounds if available or the default
      * bounds otherwise).
@@ -1664,15 +1755,7 @@
                     wct.reorder(runningTaskInfo.token, /* onTop= */ true)
                 } else if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
                     // Task is not running, start it
-                    wct.startTask(
-                        taskId,
-                        ActivityOptions.makeBasic()
-                            .apply {
-                                launchWindowingMode = WINDOWING_MODE_FREEFORM
-                                splashScreenStyle = SPLASH_SCREEN_STYLE_ICON
-                            }
-                            .toBundle(),
-                    )
+                    wct.startTask(taskId, createActivityOptionsForStartTask().toBundle())
                 }
             }
 
@@ -1691,34 +1774,7 @@
     }
 
     private fun addLaunchHomePendingIntent(wct: WindowContainerTransaction, displayId: Int) {
-        val userHandle = UserHandle.of(userId)
-        val launchHomeIntent =
-            Intent(Intent.ACTION_MAIN).apply {
-                if (displayId != DEFAULT_DISPLAY) {
-                    addCategory(Intent.CATEGORY_SECONDARY_HOME)
-                } else {
-                    addCategory(Intent.CATEGORY_HOME)
-                }
-            }
-        val options =
-            ActivityOptions.makeBasic().apply {
-                launchWindowingMode = WINDOWING_MODE_FULLSCREEN
-                pendingIntentBackgroundActivityStartMode =
-                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
-                if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
-                    launchDisplayId = displayId
-                }
-            }
-        val pendingIntent =
-            PendingIntent.getActivityAsUser(
-                context,
-                /* requestCode= */ 0,
-                launchHomeIntent,
-                PendingIntent.FLAG_IMMUTABLE,
-                /* options= */ null,
-                userHandle,
-            )
-        wct.sendPendingIntent(pendingIntent, launchHomeIntent, options.toBundle())
+        homeIntentProvider.addLaunchHomePendingIntent(wct, displayId, userId)
     }
 
     private fun addWallpaperActivity(displayId: Int, wct: WindowContainerTransaction) {
@@ -1804,7 +1860,11 @@
         displayId: Int,
         forceExitDesktop: Boolean,
     ): Boolean {
-        if (forceExitDesktop && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+        if (
+            forceExitDesktop &&
+                (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ||
+                    DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue)
+        ) {
             // |forceExitDesktop| is true when the callers knows we'll exit desktop, such as when
             // explicitly going fullscreen, so there's no point in checking the desktop state.
             return true
@@ -1821,6 +1881,33 @@
         return true
     }
 
+    /** Potentially perform Desktop cleanup after a task successfully enters PiP. */
+    @VisibleForTesting
+    fun onDesktopTaskEnteredPip(
+        taskId: Int,
+        deskId: Int,
+        displayId: Int,
+        taskIsLastVisibleTaskBeforePip: Boolean,
+    ) {
+        if (
+            !willExitDesktop(taskId, displayId, forceExitDesktop = taskIsLastVisibleTaskBeforePip)
+        ) {
+            return
+        }
+
+        val wct = WindowContainerTransaction()
+        val desktopExitRunnable =
+            performDesktopExitCleanUp(
+                wct = wct,
+                deskId = deskId,
+                displayId = displayId,
+                willExitDesktop = true,
+            )
+
+        val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
+        desktopExitRunnable?.invoke(transition)
+    }
+
     private fun performDesktopExitCleanupIfNeeded(
         taskId: Int,
         deskId: Int? = null,
@@ -2103,6 +2190,7 @@
         // TODO(b/337915660): Add a transition handler for these; animations
         //  need updates in some cases.
         val baseActivity = callingTaskInfo.baseActivity ?: return
+        val userHandle = UserHandle.of(callingTaskInfo.userId)
         val fillIn: Intent =
             userProfileContexts
                 .getOrCreate(callingTaskInfo.userId)
@@ -2110,11 +2198,13 @@
                 .getLaunchIntentForPackage(baseActivity.packageName) ?: return
         fillIn.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
         val launchIntent =
-            PendingIntent.getActivity(
+            PendingIntent.getActivityAsUser(
                 context,
                 /* requestCode= */ 0,
                 fillIn,
                 PendingIntent.FLAG_IMMUTABLE,
+                /* options= */ null,
+                userHandle,
             )
         val options = createNewWindowOptions(callingTaskInfo)
         when (options.launchWindowingMode) {
@@ -2141,10 +2231,14 @@
             WINDOWING_MODE_FREEFORM -> {
                 val wct = WindowContainerTransaction()
                 wct.sendPendingIntent(launchIntent, fillIn, options.toBundle())
+                val deskId =
+                    taskRepository.getDeskIdForTask(callingTaskInfo.taskId)
+                        ?: getDefaultDeskId(callingTaskInfo.displayId)
                 startLaunchTransition(
                     transitionType = TRANSIT_OPEN,
                     wct = wct,
                     launchingTaskId = null,
+                    deskId = deskId,
                     displayId = callingTaskInfo.displayId,
                 )
             }
@@ -2224,6 +2318,7 @@
             logV("skip keyguard is locked")
             return null
         }
+        val deskId = getDefaultDeskId(task.displayId)
         val wct = WindowContainerTransaction()
         if (shouldFreeformTaskLaunchSwitchToFullscreen(task)) {
             logD("Bring desktop tasks to front on transition=taskId=%d", task.taskId)
@@ -2246,17 +2341,24 @@
                 runOnTransitStart?.invoke(transition)
                 return wct
             }
-            val deskId = getDefaultDeskId(task.displayId)
             val runOnTransitStart = addDeskActivationChanges(deskId, wct, task)
             runOnTransitStart?.invoke(transition)
             wct.reorder(task.token, true)
             return wct
         }
+        val inheritedTaskBounds =
+            getInheritedExistingTaskBounds(taskRepository, shellTaskOrganizer, task, deskId)
+        if (!taskRepository.isActiveTask(task.taskId) && inheritedTaskBounds != null) {
+            // Inherit bounds from closing task instance to prevent application jumping different
+            // cascading positions.
+            wct.setBounds(task.token, inheritedTaskBounds)
+        }
         // TODO(b/365723620): Handle non running tasks that were launched after reboot.
         // If task is already visible, it must have been handled already and added to desktop mode.
-        // Cascade task only if it's not visible yet.
+        // Cascade task only if it's not visible yet and has no inherited bounds.
         if (
-            DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue() &&
+            inheritedTaskBounds == null &&
+                DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue() &&
                 !taskRepository.isVisibleTask(task.taskId)
         ) {
             val displayLayout = displayController.getDisplayLayout(task.displayId)
@@ -2288,7 +2390,7 @@
             reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
         )
         // 2) minimize a Task if needed.
-        val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+        val taskIdToMinimize = addAndGetMinimizeChanges(deskId, wct, task.taskId)
         addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
         if (taskIdToMinimize != null) {
             addPendingMinimizeTransition(transition, taskIdToMinimize, MinimizeReason.TASK_LIMIT)
@@ -2334,7 +2436,7 @@
                             // The desk was already showing and we're launching a new Task - we
                             // might need to minimize another Task.
                             val taskIdToMinimize =
-                                addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+                                addAndGetMinimizeChanges(deskId, wct, task.taskId)
                             taskIdToMinimize?.let { minimizingTaskId ->
                                 addPendingMinimizeTransition(
                                     transition,
@@ -2492,9 +2594,17 @@
     ) {
         val targetDisplayId = taskRepository.getDisplayForDesk(deskId)
         val displayLayout = displayController.getDisplayLayout(targetDisplayId) ?: return
-        val initialBounds = getInitialBounds(displayLayout, task, targetDisplayId)
-        if (canChangeTaskPosition(task)) {
-            wct.setBounds(task.token, initialBounds)
+        val inheritedTaskBounds =
+            getInheritedExistingTaskBounds(taskRepository, shellTaskOrganizer, task, deskId)
+        if (inheritedTaskBounds != null) {
+            // Inherit bounds from closing task instance to prevent application jumping different
+            // cascading positions.
+            wct.setBounds(task.token, inheritedTaskBounds)
+        } else {
+            val initialBounds = getInitialBounds(displayLayout, task, targetDisplayId)
+            if (canChangeTaskPosition(task)) {
+                wct.setBounds(task.token, initialBounds)
+            }
         }
         if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
             desksOrganizer.moveTaskToDesk(wct = wct, deskId = deskId, task = task)
@@ -2572,7 +2682,7 @@
                 // Caption insets stay fixed and don't scale with bounds.
                 val captionInsets =
                     if (desktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo)) {
-                        getAppHeaderHeight(context)
+                        getDesktopViewAppHeaderHeightPx(context)
                     } else {
                         0
                     }
@@ -2670,7 +2780,7 @@
 
     /** Returns the ID of the Task that will be minimized, or null if no task will be minimized. */
     private fun addAndGetMinimizeChanges(
-        displayId: Int,
+        deskId: Int,
         wct: WindowContainerTransaction,
         newTaskId: Int?,
         launchingNewIntent: Boolean = false,
@@ -2679,7 +2789,7 @@
         require(newTaskId == null || !launchingNewIntent)
         return desktopTasksLimiter
             .get()
-            .addAndGetMinimizeTaskChanges(displayId, wct, newTaskId, launchingNewIntent)
+            .addAndGetMinimizeTaskChanges(deskId, wct, newTaskId, launchingNewIntent)
     }
 
     private fun addPendingMinimizeTransition(
@@ -2776,15 +2886,36 @@
         }
         prepareForDeskActivation(displayId, wct)
         desksOrganizer.activateDesk(wct, deskId)
-        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
-            // TODO: 362720497 - do non-running tasks need to be restarted with |wct#startTask|?
-        }
         taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
             doesAnyTaskRequireTaskbarRounding(displayId)
         )
-        // TODO: b/362720497 - activating a desk with the intention to move a new task to
-        //  it means we may need to minimize something in the activating desk. Do so here
-        //  similar to how it's done in #bringDesktopAppsToFront.
+        val expandedTasksOrderedFrontToBack =
+            taskRepository.getExpandedTasksIdsInDeskOrdered(deskId = deskId)
+        // If we're adding a new Task we might need to minimize an old one
+        val taskIdToMinimize =
+            desktopTasksLimiter
+                .getOrNull()
+                ?.getTaskIdToMinimize(expandedTasksOrderedFrontToBack, newTaskIdInFront)
+        if (taskIdToMinimize != null) {
+            val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
+            // TODO(b/365725441): Handle non running task minimization
+            if (taskToMinimize != null) {
+                desksOrganizer.minimizeTask(wct, deskId, taskToMinimize)
+            }
+        }
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
+            expandedTasksOrderedFrontToBack
+                .filter { taskId -> taskId != taskIdToMinimize }
+                .reversed()
+                .forEach { taskId ->
+                    val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)
+                    if (runningTaskInfo == null) {
+                        wct.startTask(taskId, createActivityOptionsForStartTask().toBundle())
+                    } else {
+                        desksOrganizer.reorderTaskToFront(wct, deskId, runningTaskInfo)
+                    }
+                }
+        }
         return { transition ->
             val activateDeskTransition =
                 if (newTaskIdInFront != null) {
@@ -2802,6 +2933,9 @@
                     )
                 }
             desksTransitionObserver.addPendingTransition(activateDeskTransition)
+            taskIdToMinimize?.let { minimizingTask ->
+                addPendingMinimizeTransition(transition, minimizingTask, MinimizeReason.TASK_LIMIT)
+            }
         }
     }
 
@@ -2860,6 +2994,11 @@
         removeDesk(displayId = displayId, deskId = deskId)
     }
 
+    /** Removes all the available desks on all displays. */
+    fun removeAllDesks() {
+        taskRepository.getAllDeskIds().forEach { deskId -> removeDesk(deskId) }
+    }
+
     private fun removeDesk(displayId: Int, deskId: Int) {
         if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
         logV("removeDesk deskId=%d from displayId=%d", deskId, displayId)
@@ -3381,7 +3520,14 @@
         if (windowingMode == WINDOWING_MODE_FREEFORM) {
             if (DesktopModeFlags.ENABLE_DESKTOP_TAB_TEARING_MINIMIZE_ANIMATION_BUGFIX.isTrue()) {
                 // TODO b/376389593: Use a custom tab tearing transition/animation
-                startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+                val deskId = getDefaultDeskId(DEFAULT_DISPLAY)
+                startLaunchTransition(
+                    TRANSIT_OPEN,
+                    wct,
+                    launchingTaskId = null,
+                    deskId = deskId,
+                    displayId = DEFAULT_DISPLAY,
+                )
             } else {
                 desktopModeDragAndDropTransitionHandler.handleDropEvent(wct)
             }
@@ -3403,9 +3549,15 @@
     // TODO(b/366397912): Support full multi-user mode in Windowing.
     override fun onUserChanged(newUserId: Int, userContext: Context) {
         logV("onUserChanged previousUserId=%d, newUserId=%d", userId, newUserId)
+        updateCurrentUser(newUserId)
+    }
+
+    private fun updateCurrentUser(newUserId: Int) {
         userId = newUserId
         taskRepository = userRepositories.getProfile(userId)
-        snapEventHandler.onUserChange()
+        if (this::snapEventHandler.isInitialized) {
+            snapEventHandler.onUserChange()
+        }
     }
 
     /** Called when a task's info changes. */
@@ -3426,6 +3578,13 @@
         }
     }
 
+    private fun createActivityOptionsForStartTask(): ActivityOptions {
+        return ActivityOptions.makeBasic().apply {
+            launchWindowingMode = WINDOWING_MODE_FREEFORM
+            splashScreenStyle = SPLASH_SCREEN_STYLE_ICON
+        }
+    }
+
     private fun dump(pw: PrintWriter, prefix: String) {
         val innerPrefix = "$prefix  "
         pw.println("${prefix}DesktopTasksController")
@@ -3612,6 +3771,18 @@
             }
         }
 
+        override fun removeDesk(deskId: Int) {
+            executeRemoteCallWithTaskPermission(controller, "removeDesk") { c ->
+                c.removeDesk(deskId)
+            }
+        }
+
+        override fun removeAllDesks() {
+            executeRemoteCallWithTaskPermission(controller, "removeAllDesks") { c ->
+                c.removeAllDesks()
+            }
+        }
+
         override fun activateDesk(deskId: Int, remoteTransition: RemoteTransition?) {
             executeRemoteCallWithTaskPermission(controller, "activateDesk") { c ->
                 c.activateDesk(deskId, remoteTransition)
@@ -3675,8 +3846,8 @@
             }
         }
 
-        override fun removeDesktop(displayId: Int) {
-            executeRemoteCallWithTaskPermission(controller, "removeDesktop") { c ->
+        override fun removeDefaultDeskInDisplay(displayId: Int) {
+            executeRemoteCallWithTaskPermission(controller, "removeDefaultDeskInDisplay") { c ->
                 c.removeDefaultDeskInDisplay(displayId)
             }
         }
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 da369f0..4ca5882 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
@@ -22,6 +22,7 @@
 import android.os.IBinder
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_TO_BACK
+import android.window.DesktopExperienceFlags
 import android.window.DesktopModeFlags
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
@@ -31,6 +32,7 @@
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.sysui.UserChangeListener
@@ -48,6 +50,7 @@
     transitions: Transitions,
     private val desktopUserRepositories: DesktopUserRepositories,
     private val shellTaskOrganizer: ShellTaskOrganizer,
+    private val desksOrganizer: DesksOrganizer,
     private val maxTasksLimit: Int?,
     private val interactionJankMonitor: InteractionJankMonitor,
     private val context: Context,
@@ -258,7 +261,7 @@
      * returning the task to minimize.
      */
     fun addAndGetMinimizeTaskChanges(
-        displayId: Int,
+        deskId: Int,
         wct: WindowContainerTransaction,
         newFrontTaskId: Int?,
         launchingNewIntent: Boolean = false,
@@ -267,15 +270,19 @@
         val taskRepository = desktopUserRepositories.current
         val taskIdToMinimize =
             getTaskIdToMinimize(
-                taskRepository.getExpandedTasksOrdered(displayId),
+                taskRepository.getExpandedTasksIdsInDeskOrdered(deskId),
                 newFrontTaskId,
                 launchingNewIntent,
             )
-        // If it's a running task, reorder it to back.
         taskIdToMinimize
             ?.let { shellTaskOrganizer.getRunningTaskInfo(it) }
-            // TODO: b/391485148 - this won't really work with multi-desks enabled.
-            ?.let { wct.reorder(it.token, /* onTop= */ false) }
+            ?.let { task ->
+                if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+                    wct.reorder(task.token, /* onTop= */ false)
+                } else {
+                    desksOrganizer.minimizeTask(wct, deskId, task)
+                }
+            }
         return taskIdToMinimize
     }
 
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 26a5d5b..df4d18f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -23,7 +23,6 @@
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CLOSE
 import android.view.WindowManager.TRANSIT_OPEN
-import android.view.WindowManager.TRANSIT_PIP
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.window.DesktopExperienceFlags
 import android.window.DesktopModeFlags
@@ -32,19 +31,18 @@
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
 import com.android.internal.protolog.ProtoLog
-import com.android.window.flags.Flags
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.back.BackAnimationController
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
-import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.TransitionUtil.isClosingMode
+import com.android.wm.shell.shared.TransitionUtil.isOpeningMode
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
-import com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP
-import com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP
+import java.util.Optional
 
 /**
  * A [Transitions.TransitionObserver] that observes shell transitions and updates the
@@ -57,17 +55,15 @@
     private val transitions: Transitions,
     private val shellTaskOrganizer: ShellTaskOrganizer,
     private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
+    private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
     private val backAnimationController: BackAnimationController,
     private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
-    private val desksTransitionObserver: DesksTransitionObserver,
     shellInit: ShellInit,
 ) : Transitions.TransitionObserver {
 
     data class CloseWallpaperTransition(val transition: IBinder, val displayId: Int)
 
     private var transitionToCloseWallpaper: CloseWallpaperTransition? = null
-    /* Pending PiP transition and its associated display id and task id. */
-    private var pendingPipTransitionAndPipTask: Triple<IBinder, Int, Int>? = null
     private var currentProfileId: Int
 
     init {
@@ -89,7 +85,6 @@
         finishTransaction: SurfaceControl.Transaction,
     ) {
         // TODO: b/332682201 Update repository state
-        desksTransitionObserver.onTransitionReady(transition, info)
         if (
             DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
                 .isTrue() && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
@@ -102,33 +97,7 @@
             removeTaskIfNeeded(info)
         }
         removeWallpaperOnLastTaskClosingIfNeeded(transition, info)
-
-        val desktopRepository = desktopUserRepositories.getProfile(currentProfileId)
-        info.changes.forEach { change ->
-            change.taskInfo?.let { taskInfo ->
-                if (
-                    Flags.enableDesktopWindowingPip() &&
-                        desktopRepository.isTaskMinimizedPipInDisplay(
-                            taskInfo.displayId,
-                            taskInfo.taskId,
-                        )
-                ) {
-                    when (info.type) {
-                        TRANSIT_PIP ->
-                            pendingPipTransitionAndPipTask =
-                                Triple(transition, taskInfo.displayId, taskInfo.taskId)
-
-                        TRANSIT_EXIT_PIP,
-                        TRANSIT_REMOVE_PIP ->
-                            desktopRepository.setTaskInPip(
-                                taskInfo.displayId,
-                                taskInfo.taskId,
-                                enterPip = false,
-                            )
-                    }
-                }
-            }
-        }
+        desktopPipTransitionObserver.ifPresent { it.onTransitionReady(transition, info) }
     }
 
     private fun removeTaskIfNeeded(info: TransitionInfo) {
@@ -303,18 +272,6 @@
                     }
                 }
             transitionToCloseWallpaper = null
-        } else if (pendingPipTransitionAndPipTask?.first == transition) {
-            val desktopRepository = desktopUserRepositories.getProfile(currentProfileId)
-            if (aborted) {
-                pendingPipTransitionAndPipTask?.let {
-                    desktopRepository.onPipAborted(
-                        /*displayId=*/ it.second,
-                        /* taskId=*/ it.third,
-                    )
-                }
-            }
-            desktopRepository.setOnPipAbortedCallback(null)
-            pendingPipTransitionAndPipTask = null
         }
     }
 
@@ -349,18 +306,29 @@
     }
 
     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)
+        run forEachLoop@{
+            info.changes.forEach { change ->
+                change.taskInfo?.let { task ->
+                    val desktopRepository = desktopUserRepositories.getProfile(task.userId)
+                    val displayId = task.displayId
+                    val transparentTaskId =
+                        desktopRepository.getTopTransparentFullscreenTaskId(displayId)
+                    if (transparentTaskId == null) return@forEachLoop
+                    val changeMode = change.mode
+                    val taskId = task.taskId
+                    val isTopTransparentFullscreenTaskClosing =
+                        taskId == transparentTaskId && isClosingMode(changeMode)
+                    val isNonTopTransparentFullscreenTaskOpening =
+                        taskId != transparentTaskId && isOpeningMode(changeMode)
+                    // Clear `topTransparentFullscreenTask` information from repository if task
+                    // is closed, sent to back or if a different task is opened, brought to front.
+                    if (
+                        isTopTransparentFullscreenTaskClosing ||
+                            isNonTopTransparentFullscreenTaskOpening
+                    ) {
+                        desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+                        return@forEachLoop
+                    }
                 }
             }
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index e943c42..c6f74728 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -2,6 +2,7 @@
 
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
 import android.animation.RectEvaluator
 import android.animation.ValueAnimator
 import android.app.ActivityManager.RunningTaskInfo
@@ -23,9 +24,12 @@
 import android.os.SystemClock
 import android.os.SystemProperties
 import android.os.UserHandle
+import android.view.Choreographer
 import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
 import android.view.WindowManager.TRANSIT_CLOSE
 import android.window.DesktopModeFlags
+import android.window.DesktopModeFlags.ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX
 import android.window.TransitionInfo
 import android.window.TransitionInfo.Change
 import android.window.TransitionRequestInfo
@@ -46,6 +50,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.animation.Interpolators
 import com.android.wm.shell.shared.animation.PhysicsAnimator
 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
@@ -120,6 +125,7 @@
         taskInfo: RunningTaskInfo,
         dragToDesktopAnimator: MoveToDesktopAnimator,
         visualIndicator: DesktopModeVisualIndicator?,
+        dragCancelCallback: Runnable,
     ) {
         if (inProgress) {
             logV("Drag to desktop transition already in progress.")
@@ -166,6 +172,7 @@
                     startTransitionToken = startTransitionToken,
                     otherSplitTask = otherTask,
                     visualIndicator = visualIndicator,
+                    dragCancelCallback = dragCancelCallback,
                 )
             } else {
                 TransitionState.FromFullscreen(
@@ -173,6 +180,7 @@
                     dragAnimator = dragToDesktopAnimator,
                     startTransitionToken = startTransitionToken,
                     visualIndicator = visualIndicator,
+                    dragCancelCallback = dragCancelCallback,
                 )
             }
     }
@@ -185,18 +193,30 @@
      */
     fun finishDragToDesktopTransition(wct: WindowContainerTransaction): IBinder? {
         if (!inProgress) {
+            logV("finishDragToDesktop: not in progress, returning")
             // Don't attempt to finish a drag to desktop transition since there is no transition in
             // progress which means that the drag to desktop transition was never successfully
             // started.
             return null
         }
-        if (requireTransitionState().startAborted) {
+        val state = requireTransitionState()
+        if (state.startAborted) {
+            logV("finishDragToDesktop: start was aborted, clearing state")
             // Don't attempt to complete the drag-to-desktop since the start transition didn't
             // succeed as expected. Just reset the state as if nothing happened.
             clearState()
             return null
         }
-        return transitions.startTransition(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, wct, this)
+        if (state.startInterrupted) {
+            logV("finishDragToDesktop: start was interrupted, returning")
+            // If start was interrupted we've either already requested a cancel/end transition - so
+            // we should let that request play out, or we're cancelling the drag-to-desktop
+            // transition altogether, so just return here.
+            return null
+        }
+        state.endTransitionToken =
+            transitions.startTransition(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, wct, this)
+        return state.endTransitionToken
     }
 
     /**
@@ -208,6 +228,7 @@
      */
     fun cancelDragToDesktopTransition(cancelState: CancelState) {
         if (!inProgress) {
+            logV("cancelDragToDesktop: not in progress, returning")
             // Don't attempt to cancel a drag to desktop transition since there is no transition in
             // progress which means that the drag to desktop transition was never successfully
             // started.
@@ -215,11 +236,19 @@
         }
         val state = requireTransitionState()
         if (state.startAborted) {
+            logV("cancelDragToDesktop: start was aborted, clearing state")
             // Don't attempt to cancel the drag-to-desktop since the start transition didn't
             // succeed as expected. Just reset the state as if nothing happened.
             clearState()
             return
         }
+        if (state.startInterrupted) {
+            logV("cancelDragToDesktop: start was interrupted, returning")
+            // If start was interrupted we've either already requested a cancel/end transition - so
+            // we should let that request play out, or we're cancelling the drag-to-desktop
+            // transition altogether, so just return here.
+            return
+        }
         state.cancelState = cancelState
 
         if (state.draggedTaskChange != null && cancelState == CancelState.STANDARD_CANCEL) {
@@ -227,7 +256,7 @@
             // transient to start and merge. Animate the cancellation (scale back to original
             // bounds) first before actually starting the cancel transition so that the wallpaper
             // is visible behind the animating task.
-            startCancelAnimation()
+            state.activeCancelAnimation = startCancelAnimation()
         } else if (
             state.draggedTaskChange != null &&
                 (cancelState == CancelState.CANCEL_SPLIT_LEFT ||
@@ -255,7 +284,7 @@
         ) {
             if (bubbleController.isEmpty || state !is TransitionState.FromFullscreen) {
                 // TODO(b/388853233): add support for dragging split task to bubble
-                startCancelAnimation()
+                state.activeCancelAnimation = startCancelAnimation()
             } else {
                 // Animation is handled by BubbleController
                 val wct = WindowContainerTransaction()
@@ -327,8 +356,9 @@
         val taskInfo = state.draggedTaskChange?.taskInfo ?: error("Expected non-null taskInfo")
         val dragPosition = PointF(state.dragAnimator.position)
         val scale = state.dragAnimator.scale
+        val cornerRadius = state.dragAnimator.cornerRadius
         state.dragAnimator.cancelAnimator()
-        requestBubble(wct, taskInfo, onLeft, scale, dragPosition)
+        requestBubble(wct, taskInfo, onLeft, scale, cornerRadius, dragPosition)
     }
 
     private fun requestBubble(
@@ -336,13 +366,14 @@
         taskInfo: RunningTaskInfo,
         onLeft: Boolean,
         taskScale: Float = 1f,
+        cornerRadius: Float = 0f,
         dragPosition: PointF = PointF(0f, 0f),
     ) {
         val controller =
             bubbleController.orElseThrow { IllegalStateException("BubbleController not set") }
         controller.expandStackAndSelectBubble(
             taskInfo,
-            BubbleTransitions.DragData(onLeft, taskScale, dragPosition, wct),
+            BubbleTransitions.DragData(onLeft, taskScale, cornerRadius, dragPosition, wct),
         )
     }
 
@@ -355,6 +386,19 @@
     ): Boolean {
         val state = requireTransitionState()
 
+        if (
+            handleCancelOrExitAfterInterrupt(
+                transition,
+                info,
+                startTransaction,
+                finishTransaction,
+                finishCallback,
+                state,
+            )
+        ) {
+            return true
+        }
+
         val isStartDragToDesktop =
             info.type == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP &&
                 transition == state.startTransitionToken
@@ -537,6 +581,58 @@
         }
     }
 
+    private fun handleCancelOrExitAfterInterrupt(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: Transaction,
+        finishTransaction: Transaction,
+        finishCallback: Transitions.TransitionFinishCallback,
+        state: TransitionState,
+    ): Boolean {
+        if (!ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue) {
+            return false
+        }
+        val isCancelDragToDesktop =
+            info.type == TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP &&
+                transition == state.cancelTransitionToken
+        val isEndDragToDesktop =
+            info.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP &&
+                transition == state.endTransitionToken
+        // We should only receive cancel or end transitions through startAnimation() if the
+        // start transition was interrupted while a cancel- or end-transition had already
+        // been requested. Finish the cancel/end transition to avoid having to deal with more
+        // incoming transitions, and clear the state for the next start-drag transition.
+        if (!isCancelDragToDesktop && !isEndDragToDesktop) {
+            return false
+        }
+        if (!state.startInterrupted) {
+            logW(
+                "Not interrupted, but received startAnimation for cancel/end drag." +
+                    "isCancel=$isCancelDragToDesktop, isEnd=$isEndDragToDesktop"
+            )
+            return false
+        }
+        logV(
+            "startAnimation: interrupted -> " +
+                "isCancel=$isCancelDragToDesktop, isEnd=$isEndDragToDesktop"
+        )
+        if (isEndDragToDesktop) {
+            setupEndDragToDesktop(info, startTransaction, finishTransaction)
+            animateEndDragToDesktop(startTransaction = startTransaction, finishCallback)
+        } else { // isCancelDragToDesktop
+            // Similar to when we merge the cancel transition: ensure all tasks involved in the
+            // cancel transition are shown, and finish the transition immediately.
+            info.changes.forEach { change ->
+                startTransaction.show(change.leash)
+                finishTransaction.show(change.leash)
+            }
+        }
+        startTransaction.apply()
+        finishCallback.onTransitionFinished(/* wct= */ null)
+        clearState()
+        return true
+    }
+
     /**
      * Calculates start drag to desktop layers for transition [info]. The leash layer is calculated
      * based on its change position in the transition, e.g. `appLayer = appLayers - i`, where i is
@@ -588,6 +684,7 @@
                 ?: error("Start transition expected to be waiting for merge but wasn't")
         if (isEndTransition) {
             logV("mergeAnimation: end-transition, target=$mergeTarget")
+            state.mergedEndTransition = true
             setupEndDragToDesktop(
                 info,
                 startTransaction = startT,
@@ -615,8 +712,93 @@
             return
         }
         logW("unhandled merge transition: transitionInfo=$info")
+        // Handle unknown incoming transitions by finishing the start transition. For now, only do
+        // this if we've already requested a cancel- or end transition. If we've already merged the
+        // end-transition, or if the end-transition is running on its own, then just wait until that
+        // finishes instead. If we've merged the cancel-transition we've finished the
+        // start-transition and won't reach this code.
+        if (mergeTarget == state.startTransitionToken && !state.mergedEndTransition) {
+            interruptStartTransition(state)
+        }
     }
 
+    private fun isCancelOrEndTransitionRequested(state: TransitionState): Boolean =
+        state.cancelTransitionToken != null || state.endTransitionToken != null
+
+    private fun interruptStartTransition(state: TransitionState) {
+        if (!ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue) {
+            return
+        }
+        if (isCancelOrEndTransitionRequested(state)) {
+            logV("interruptStartTransition, bookend requested -> finish start transition")
+            // Finish the start-drag transition, we will finish the overall transition properly when
+            // receiving #startAnimation for Cancel/End.
+            state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
+            state.dragAnimator.cancelAnimator()
+        } else {
+            logV("interruptStartTransition, bookend not requested -> animate to Home")
+            // Animate to Home, and then finish the start-drag transition. Since there is no other
+            // (end/cancel) transition requested that will be the end of the overall transition.
+            state.dragAnimator.cancelAnimator()
+            state.dragCancelCallback?.run()
+            createInterruptToHomeAnimator(transactionSupplier.get(), state) {
+                state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
+                clearState()
+            }
+        }
+        state.activeCancelAnimation?.removeAllListeners()
+        state.activeCancelAnimation?.cancel()
+        state.activeCancelAnimation = null
+        // Keep the transition state so we can deal with Cancel/End properly in #startAnimation.
+        state.startInterrupted = true
+        dragToDesktopStateListener?.onTransitionInterrupted()
+        // Cancel CUJs here as they won't be accurate now that an incoming transition is playing.
+        interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
+        interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE)
+        LatencyTracker.getInstance(context)
+            .onActionCancel(LatencyTracker.ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG)
+    }
+
+    private fun createInterruptToHomeAnimator(
+        transaction: Transaction,
+        state: TransitionState,
+        endCallback: Runnable,
+    ) {
+        val homeLeash = state.homeChange?.leash ?: error("Expected home leash to be non-null")
+        val draggedTaskLeash =
+            state.draggedTaskChange?.leash ?: error("Expected dragged leash to be non-null")
+        val homeAnimator = createInterruptAlphaAnimator(transaction, homeLeash, toShow = true)
+        val draggedTaskAnimator =
+            createInterruptAlphaAnimator(transaction, draggedTaskLeash, toShow = false)
+        val animatorSet = AnimatorSet()
+        animatorSet.playTogether(homeAnimator, draggedTaskAnimator)
+        animatorSet.addListener(
+            object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator) {
+                    endCallback.run()
+                }
+            }
+        )
+        animatorSet.start()
+    }
+
+    private fun createInterruptAlphaAnimator(
+        transaction: Transaction,
+        leash: SurfaceControl,
+        toShow: Boolean,
+    ) =
+        ValueAnimator.ofFloat(if (toShow) 0f else 1f, if (toShow) 1f else 0f).apply {
+            transaction.show(leash)
+            duration = DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
+            interpolator = Interpolators.LINEAR
+            addUpdateListener { animation ->
+                transaction
+                    .setAlpha(leash, animation.animatedValue as Float)
+                    .setFrameTimeline(Choreographer.getInstance().vsyncId)
+                    .apply()
+            }
+        }
+
     protected open fun setupEndDragToDesktop(
         info: TransitionInfo,
         startTransaction: SurfaceControl.Transaction,
@@ -781,7 +963,7 @@
         } ?: false
     }
 
-    private fun startCancelAnimation() {
+    private fun startCancelAnimation(): Animator {
         val state = requireTransitionState()
         val dragToDesktopAnimator = state.dragAnimator
 
@@ -798,7 +980,7 @@
         val dx = targetX - x
         val dy = targetY - y
         val tx: SurfaceControl.Transaction = transactionSupplier.get()
-        ValueAnimator.ofFloat(DRAG_FREEFORM_SCALE, 1f)
+        return ValueAnimator.ofFloat(DRAG_FREEFORM_SCALE, 1f)
             .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
             .apply {
                 addUpdateListener { animator ->
@@ -816,6 +998,7 @@
                 addListener(
                     object : AnimatorListenerAdapter() {
                         override fun onAnimationEnd(animation: Animator) {
+                            state.activeCancelAnimation = null
                             dragToDesktopStateListener?.onCancelToDesktopAnimationEnd()
                             // Start the cancel transition to restore order.
                             startCancelDragToDesktopTransition()
@@ -908,10 +1091,16 @@
         val dragLayer: Int,
     )
 
+    /** Listener for various events happening during the DragToDesktop transition. */
     interface DragToDesktopStateListener {
+        /** Indicates that the animation into Desktop has started. */
         fun onCommitToDesktopAnimationStart()
 
+        /** Called when the animation to cancel the desktop-drag has finished. */
         fun onCancelToDesktopAnimationEnd()
+
+        /** Indicates that the drag-to-desktop transition has been interrupted. */
+        fun onTransitionInterrupted()
     }
 
     sealed class TransitionState {
@@ -928,6 +1117,11 @@
         abstract var cancelState: CancelState
         abstract var startAborted: Boolean
         abstract val visualIndicator: DesktopModeVisualIndicator?
+        abstract var startInterrupted: Boolean
+        abstract var endTransitionToken: IBinder?
+        abstract var mergedEndTransition: Boolean
+        abstract var activeCancelAnimation: Animator?
+        abstract var dragCancelCallback: Runnable?
 
         data class FromFullscreen(
             override val draggedTaskId: Int,
@@ -943,6 +1137,11 @@
             override var cancelState: CancelState = CancelState.NO_CANCEL,
             override var startAborted: Boolean = false,
             override val visualIndicator: DesktopModeVisualIndicator?,
+            override var startInterrupted: Boolean = false,
+            override var endTransitionToken: IBinder? = null,
+            override var mergedEndTransition: Boolean = false,
+            override var activeCancelAnimation: Animator? = null,
+            override var dragCancelCallback: Runnable? = null,
             var otherRootChanges: MutableList<Change> = mutableListOf(),
         ) : TransitionState()
 
@@ -960,6 +1159,11 @@
             override var cancelState: CancelState = CancelState.NO_CANCEL,
             override var startAborted: Boolean = false,
             override val visualIndicator: DesktopModeVisualIndicator?,
+            override var startInterrupted: Boolean = false,
+            override var endTransitionToken: IBinder? = null,
+            override var mergedEndTransition: Boolean = false,
+            override var activeCancelAnimation: Animator? = null,
+            override var dragCancelCallback: Runnable? = null,
             var splitRootChange: Change? = null,
             var otherSplitTask: Int,
         ) : TransitionState()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 44f7e16..5f7fbd9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -35,6 +35,12 @@
     /** Activates the desk whose ID is `deskId` on whatever display it currently exists on. */
     oneway void activateDesk(int deskId, in RemoteTransition remoteTransition);
 
+    /** Removes the desk with the given `deskId`. */
+    oneway void removeDesk(int deskId);
+
+    /** Removes all the available desks on all displays. */
+    oneway void removeAllDesks();
+
     /** Show apps on the desktop on the given display */
     void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
 
@@ -64,8 +70,11 @@
                         in @nullable RemoteTransition remoteTransition,
                         in @nullable IMoveToDesktopCallback callback);
 
-    /** Remove desktop on the given display */
-    oneway void removeDesktop(int displayId);
+    /**
+     * Removes the default desktop on the given display.
+     * @deprecated with multi-desks, we should use `removeDesk()`.
+     */
+    oneway void removeDefaultDeskInDisplay(int displayId);
 
     /** Move a task with given `taskId` to external display */
     void moveToExternalDisplay(int taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
new file mode 100644
index 0000000..8ce624e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Handler
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
+import java.util.function.Supplier
+
+/**
+ * This supplies the package name of default home in an efficient way. The query to package manager
+ * only executes on initialization and when the preferred activity (e.g. default home) is changed.
+ */
+class DefaultHomePackageSupplier(
+    private val context: Context,
+    shellInit: ShellInit,
+    @ShellMainThread private val mainHandler: Handler,
+) : BroadcastReceiver(), Supplier<String?> {
+
+    private var defaultHomePackage: String? = null
+
+    init {
+        shellInit.addInitCallback({ onInit() }, this)
+    }
+
+    private fun onInit() {
+        context.registerReceiver(
+            this,
+            IntentFilter(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED),
+            null /* broadcastPermission */,
+            mainHandler,
+        )
+    }
+
+    private fun updateDefaultHomePackage(): String? {
+        defaultHomePackage = context.packageManager.getHomeActivities(ArrayList())?.packageName
+        return defaultHomePackage
+    }
+
+    override fun onReceive(contxt: Context?, intent: Intent?) {
+        updateDefaultHomePackage()
+    }
+
+    override fun get(): String? {
+        return defaultHomePackage ?: updateDefaultHomePackage()
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
index fc359d7..5a988fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
@@ -18,6 +18,8 @@
 import android.app.ActivityManager
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer.OnCreateCallback
+import kotlin.coroutines.suspendCoroutine
 
 /** An organizer of desk containers in which to host child desktop windows. */
 interface DesksOrganizer {
@@ -40,6 +42,13 @@
         task: ActivityManager.RunningTaskInfo,
     )
 
+    /** Reorders a desk's task to the front. */
+    fun reorderTaskToFront(
+        wct: WindowContainerTransaction,
+        deskId: Int,
+        task: ActivityManager.RunningTaskInfo,
+    )
+
     /** Minimizes the given task of the given deskId. */
     fun minimizeTask(
         wct: WindowContainerTransaction,
@@ -47,6 +56,13 @@
         task: ActivityManager.RunningTaskInfo,
     )
 
+    /** Unminimize the given task of the given desk. */
+    fun unminimizeTask(
+        wct: WindowContainerTransaction,
+        deskId: Int,
+        task: ActivityManager.RunningTaskInfo,
+    )
+
     /** Whether the change is for the given desk id. */
     fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean
 
@@ -68,3 +84,9 @@
         fun onCreated(deskId: Int)
     }
 }
+
+/** Creates a new desk container in the given display. */
+suspend fun DesksOrganizer.createDesk(displayId: Int): Int = suspendCoroutine { cont ->
+    val onCreateCallback = OnCreateCallback { deskId -> cont.resumeWith(Result.success(deskId)) }
+    createDesk(displayId, onCreateCallback)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
index f576258..49ca58e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
@@ -33,6 +33,7 @@
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.LaunchAdjacentController
 import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer.OnCreateCallback
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.sysui.ShellCommandHandler
@@ -44,6 +45,7 @@
     shellInit: ShellInit,
     shellCommandHandler: ShellCommandHandler,
     private val shellTaskOrganizer: ShellTaskOrganizer,
+    private val launchAdjacentController: LaunchAdjacentController,
 ) : DesksOrganizer, ShellTaskOrganizer.TaskListener {
 
     private val createDeskRootRequests = mutableListOf<CreateDeskRequest>()
@@ -110,7 +112,31 @@
         wct.reparent(task.token, root.taskInfo.token, /* onTop= */ true)
     }
 
+    override fun reorderTaskToFront(
+        wct: WindowContainerTransaction,
+        deskId: Int,
+        task: RunningTaskInfo,
+    ) {
+        logV("reorderTaskToFront task=${task.taskId} desk=$deskId")
+        val root = deskRootsByDeskId[deskId] ?: error("Root not found for desk: $deskId")
+        if (task.taskId in root.children) {
+            wct.reorder(task.token, /* onTop= */ true, /* includingParents= */ true)
+            return
+        }
+        val minimizationRoot =
+            checkNotNull(deskMinimizationRootsByDeskId[deskId]) {
+                "Minimization root not found for desk: $deskId"
+            }
+        if (task.taskId in minimizationRoot.children) {
+            unminimizeTask(wct, deskId, task)
+            wct.reorder(task.token, /* onTop= */ true, /* includingParents= */ true)
+            return
+        }
+        logE("Attempted to reorder task=${task.taskId} in desk=$deskId but it was not a child")
+    }
+
     override fun minimizeTask(wct: WindowContainerTransaction, deskId: Int, task: RunningTaskInfo) {
+        logV("minimizeTask task=${task.taskId} desk=$deskId")
         val deskRoot =
             checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
         val minimizationRoot =
@@ -129,6 +155,30 @@
         wct.reparent(task.token, minimizationRoot.token, /* onTop= */ true)
     }
 
+    override fun unminimizeTask(
+        wct: WindowContainerTransaction,
+        deskId: Int,
+        task: RunningTaskInfo,
+    ) {
+        val taskId = task.taskId
+        logV("unminimizeTask task=$taskId desk=$deskId")
+        val deskRoot =
+            checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
+        val minimizationRoot =
+            checkNotNull(deskMinimizationRootsByDeskId[deskId]) {
+                "Minimization root not found for desk: $deskId"
+            }
+        if (taskId in deskRoot.children) {
+            logV("Task #$taskId is already unminimized in desk=$deskId")
+            return
+        }
+        if (taskId !in minimizationRoot.children) {
+            logE("Attempted to unminimize task=$taskId in desk=$deskId but it was not a child")
+            return
+        }
+        wct.reparent(task.token, deskRoot.token, /* onTop= */ true)
+    }
+
     override fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean =
         (isDeskRootChange(change) && change.taskId == deskId) ||
             (getDeskMinimizationRootInChange(change)?.deskId == deskId)
@@ -164,6 +214,21 @@
             change.mode == TRANSIT_TO_FRONT
 
     override fun onTaskAppeared(taskInfo: RunningTaskInfo, leash: SurfaceControl) {
+        handleTaskAppeared(taskInfo, leash)
+        updateLaunchAdjacentController()
+    }
+
+    override fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
+        handleTaskInfoChanged(taskInfo)
+        updateLaunchAdjacentController()
+    }
+
+    override fun onTaskVanished(taskInfo: RunningTaskInfo) {
+        handleTaskVanished(taskInfo)
+        updateLaunchAdjacentController()
+    }
+
+    private fun handleTaskAppeared(taskInfo: RunningTaskInfo, leash: SurfaceControl) {
         // Check whether this task is appearing inside a desk.
         if (taskInfo.parentTaskId in deskRootsByDeskId) {
             val deskId = taskInfo.parentTaskId
@@ -216,7 +281,7 @@
         hideMinimizationRoot(deskMinimizationRoot)
     }
 
-    override fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
+    private fun handleTaskInfoChanged(taskInfo: RunningTaskInfo) {
         if (deskRootsByDeskId.contains(taskInfo.taskId)) {
             val deskId = taskInfo.taskId
             deskRootsByDeskId[deskId] = deskRootsByDeskId[deskId].copy(taskInfo = taskInfo)
@@ -254,7 +319,7 @@
         logE("onTaskInfoChanged: unknown task: ${taskInfo.taskId}")
     }
 
-    override fun onTaskVanished(taskInfo: RunningTaskInfo) {
+    private fun handleTaskVanished(taskInfo: RunningTaskInfo) {
         if (deskRootsByDeskId.contains(taskInfo.taskId)) {
             val deskId = taskInfo.taskId
             val deskRoot = deskRootsByDeskId[deskId]
@@ -336,6 +401,18 @@
         deskRootsByDeskId.forEach { _, deskRoot -> deskRoot.children -= taskId }
     }
 
+    private fun updateLaunchAdjacentController() {
+        deskRootsByDeskId.forEach { deskId, root ->
+            if (root.taskInfo.isVisible) {
+                // Disable launch adjacent handling if any desk is active, otherwise the split
+                // launch root and the desk root will both be eligible to take launching tasks.
+                launchAdjacentController.launchAdjacentEnabled = false
+                return
+            }
+        }
+        launchAdjacentController.launchAdjacentEnabled = true
+    }
+
     @VisibleForTesting
     data class DeskRoot(
         val deskId: Int,
@@ -377,6 +454,9 @@
     override fun dump(pw: PrintWriter, prefix: String) {
         val innerPrefix = "$prefix  "
         pw.println("$prefix$TAG")
+        pw.println(
+            "${innerPrefix}launchAdjacentEnabled=" + launchAdjacentController.launchAdjacentEnabled
+        )
         pw.println("${innerPrefix}Desk Roots:")
         deskRootsByDeskId.forEach { deskId, root ->
             val minimizationRoot = deskMinimizationRootsByDeskId[deskId]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
index 1566544f..e04a5cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
@@ -113,6 +113,8 @@
         visibleTasks: ArraySet<Int> = ArraySet(),
         minimizedTasks: ArraySet<Int> = ArraySet(),
         freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
+        leftTiledTask: Int? = null,
+        rightTiledTask: Int? = null,
     ) {
         // TODO: b/367609270 - Improve the API to support multi-user
         try {
@@ -125,17 +127,20 @@
                 val desktop =
                     getDesktop(currentRepository, desktopId)
                         .toBuilder()
-                        .updateTaskStates(visibleTasks, minimizedTasks, freeformTasksInZOrder)
+                        .updateTaskStates(
+                            visibleTasks,
+                            minimizedTasks,
+                            freeformTasksInZOrder,
+                            leftTiledTask,
+                            rightTiledTask,
+                        )
                         .updateZOrder(freeformTasksInZOrder)
 
                 persistentRepositories
                     .toBuilder()
                     .putDesktopRepoByUser(
                         userId,
-                        currentRepository
-                            .toBuilder()
-                            .putDesktop(desktopId, desktop.build())
-                            .build(),
+                        currentRepository.toBuilder().putDesktop(desktopId, desktop.build()).build(),
                     )
                     .build()
             }
@@ -149,6 +154,33 @@
         }
     }
 
+    /** Removes the desktop from the persistent repository. */
+    suspend fun removeDesktop(userId: Int, desktopId: Int) {
+        try {
+            dataStore.updateData { persistentRepositories: DesktopPersistentRepositories ->
+                val currentRepository =
+                    persistentRepositories.getDesktopRepoByUserOrDefault(
+                        userId,
+                        DesktopRepositoryState.getDefaultInstance(),
+                    )
+                persistentRepositories
+                    .toBuilder()
+                    .putDesktopRepoByUser(
+                        userId,
+                        currentRepository.toBuilder().removeDesktop(desktopId).build(),
+                    )
+                    .build()
+            }
+        } catch (throwable: Throwable) {
+            Log.e(
+                TAG,
+                "Error in removing desktop related data, data is " +
+                    "stored in a file named $DESKTOP_REPOSITORIES_DATASTORE_FILE",
+                throwable,
+            )
+        }
+    }
+
     suspend fun removeUsers(uids: List<Int>) {
         try {
             dataStore.updateData { persistentRepositories: DesktopPersistentRepositories ->
@@ -198,6 +230,8 @@
             visibleTasks: ArraySet<Int>,
             minimizedTasks: ArraySet<Int>,
             freeformTasksInZOrder: ArrayList<Int>,
+            leftTiledTask: Int?,
+            rightTiledTask: Int?,
         ): Desktop.Builder {
             clearTasksByTaskId()
 
@@ -214,7 +248,11 @@
             }
             putAllTasksByTaskId(
                 visibleTasks.associateWith {
-                    createDesktopTask(it, state = DesktopTaskState.VISIBLE)
+                    createDesktopTask(
+                        it,
+                        state = DesktopTaskState.VISIBLE,
+                        getTilingStateForTask(it, leftTiledTask, rightTiledTask),
+                    )
                 }
             )
             putAllTasksByTaskId(
@@ -225,6 +263,17 @@
             return this
         }
 
+        private fun getTilingStateForTask(
+            taskId: Int,
+            leftTiledTask: Int?,
+            rightTiledTask: Int?,
+        ): DesktopTaskTilingState =
+            when (taskId) {
+                leftTiledTask -> DesktopTaskTilingState.LEFT
+                rightTiledTask -> DesktopTaskTilingState.RIGHT
+                else -> DesktopTaskTilingState.NONE
+            }
+
         private fun Desktop.Builder.updateZOrder(
             freeformTasksInZOrder: ArrayList<Int>
         ): Desktop.Builder {
@@ -236,7 +285,12 @@
         private fun createDesktopTask(
             taskId: Int,
             state: DesktopTaskState = DesktopTaskState.VISIBLE,
+            tiling_state: DesktopTaskTilingState = DesktopTaskTilingState.NONE,
         ): DesktopTask =
-            DesktopTask.newBuilder().setTaskId(taskId).setDesktopTaskState(state).build()
+            DesktopTask.newBuilder()
+                .setTaskId(taskId)
+                .setDesktopTaskState(state)
+                .setDesktopTaskTilingState(tiling_state)
+                .build()
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
index a26ebbf..8191181 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
@@ -17,8 +17,22 @@
 package com.android.wm.shell.desktopmode.persistence
 
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import kotlinx.coroutines.flow.StateFlow
 
 /** Interface for initializing the [DesktopUserRepositories]. */
-fun interface DesktopRepositoryInitializer {
+interface DesktopRepositoryInitializer {
+    /** A factory used to recreate a desk from persistence. */
+    var deskRecreationFactory: DeskRecreationFactory
+
+    /** A flow that emits true when the repository has been initialized. */
+    val isInitialized: StateFlow<Boolean>
+
+    /** Initialize the user repositories from a persistent data store. */
     fun initialize(userRepositories: DesktopUserRepositories)
+
+    /** A factory for recreating desks. */
+    fun interface DeskRecreationFactory {
+        /** Recreates a restored desk and returns the new desk id. */
+        suspend fun recreateDesk(userId: Int, destinationDisplayId: Int, deskId: Int): Int
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
index 0507e59..5ed0b1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
@@ -17,13 +17,19 @@
 package com.android.wm.shell.desktopmode.persistence
 
 import android.content.Context
+import android.view.Display
 import android.window.DesktopExperienceFlags
 import android.window.DesktopModeFlags
+import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.desktopmode.DesktopRepository
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer.DeskRecreationFactory
+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 kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.launch
 
 /**
@@ -37,62 +43,150 @@
     private val persistentRepository: DesktopPersistentRepository,
     @ShellMainThread private val mainCoroutineScope: CoroutineScope,
 ) : DesktopRepositoryInitializer {
+
+    override var deskRecreationFactory: DeskRecreationFactory = DefaultDeskRecreationFactory()
+
+    private val _isInitialized = MutableStateFlow(false)
+    override val isInitialized: StateFlow<Boolean> = _isInitialized
+
     override fun initialize(userRepositories: DesktopUserRepositories) {
-        if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) return
+        if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue) {
+            _isInitialized.value = true
+            return
+        }
         //  TODO: b/365962554 - Handle the case that user moves to desktop before it's initialized
         mainCoroutineScope.launch {
-            val desktopUserPersistentRepositoryMap =
-                persistentRepository.getUserDesktopRepositoryMap() ?: return@launch
-            for (userId in desktopUserPersistentRepositoryMap.keys) {
-                val repository = userRepositories.getProfile(userId)
-                val desktopRepositoryState =
-                    persistentRepository.getDesktopRepositoryState(userId) ?: continue
-                val desktopByDesktopIdMap = desktopRepositoryState.desktopMap
-                for (desktopId in desktopByDesktopIdMap.keys) {
-                    val persistentDesktop =
-                        persistentRepository.readDesktop(userId, desktopId) ?: continue
-                    val maxTasks =
-                        DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
-                            ?: persistentDesktop.zOrderedTasksCount
-                    var visibleTasksCount = 0
-                    repository.addDesk(
-                        displayId = persistentDesktop.displayId,
-                        deskId =
-                            if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
-                                persistentDesktop.desktopId
-                            } else {
-                                // When disabled, desk ids are always the display id.
-                                persistentDesktop.displayId
-                            },
+            try {
+                val desktopUserPersistentRepositoryMap =
+                    persistentRepository.getUserDesktopRepositoryMap() ?: return@launch
+                for (userId in desktopUserPersistentRepositoryMap.keys) {
+                    val repository = userRepositories.getProfile(userId)
+                    val desktopRepositoryState =
+                        persistentRepository.getDesktopRepositoryState(userId) ?: continue
+                    val desksToRestore = getDesksToRestore(desktopRepositoryState, userId)
+                    logV(
+                        "initialize() will restore desks=%s user=%d",
+                        desksToRestore.map { it.desktopId },
+                        userId,
                     )
-                    persistentDesktop.zOrderedTasksList
-                        // Reverse it so we initialize the repo from bottom to top.
-                        .reversed()
-                        .mapNotNull { taskId -> persistentDesktop.tasksByTaskIdMap[taskId] }
-                        // TODO: b/362720497 - add tasks to their respective desk when multi-desk
-                        //   persistence is implemented.
-                        .forEach { task ->
-                            if (
-                                task.desktopTaskState == DesktopTaskState.VISIBLE &&
-                                    visibleTasksCount < maxTasks
-                            ) {
-                                visibleTasksCount++
-                                repository.addTask(
-                                    persistentDesktop.displayId,
-                                    task.taskId,
-                                    isVisible = false,
-                                )
-                            } else {
-                                repository.addTask(
-                                    persistentDesktop.displayId,
-                                    task.taskId,
-                                    isVisible = false,
-                                )
-                                repository.minimizeTask(persistentDesktop.displayId, task.taskId)
-                            }
+                    desksToRestore.forEach { persistentDesktop ->
+                        val maxTasks = getTaskLimit(persistentDesktop)
+                        val displayId = persistentDesktop.displayId
+                        val deskId = persistentDesktop.desktopId
+                        // TODO: b/401107440 - Implement desk restoration to other displays.
+                        val newDisplayId = Display.DEFAULT_DISPLAY
+                        val newDeskId =
+                            deskRecreationFactory.recreateDesk(
+                                userId = userId,
+                                destinationDisplayId = newDisplayId,
+                                deskId = deskId,
+                            )
+                        logV(
+                            "Recreated desk=%d in display=%d using new deskId=%d and displayId=%d",
+                            deskId,
+                            displayId,
+                            newDeskId,
+                            newDisplayId,
+                        )
+                        if (newDeskId != deskId || newDisplayId != displayId) {
+                            logV("Removing obsolete desk from persistence under deskId=%d", deskId)
+                            persistentRepository.removeDesktop(userId, deskId)
                         }
+
+                        // TODO: b/393961770 - [DesktopRepository] doesn't save desks to the
+                        //  persistent repository until a task is added to them. Update it so that
+                        //  empty desks can be restored too.
+                        repository.addDesk(displayId = displayId, deskId = newDeskId)
+                        var visibleTasksCount = 0
+                        persistentDesktop.zOrderedTasksList
+                            // Reverse it so we initialize the repo from bottom to top.
+                            .reversed()
+                            .mapNotNull { taskId -> persistentDesktop.tasksByTaskIdMap[taskId] }
+                            .forEach { task ->
+                                // Visible here means non-minimized a.k.a. expanded, it does not
+                                // mean
+                                // it is visible in WM (and |DesktopRepository|) terms.
+                                val isVisible =
+                                    task.desktopTaskState == DesktopTaskState.VISIBLE &&
+                                        visibleTasksCount < maxTasks
+
+                                repository.addTaskToDesk(
+                                    displayId = displayId,
+                                    deskId = newDeskId,
+                                    taskId = task.taskId,
+                                    isVisible = false,
+                                )
+
+                                if (isVisible) {
+                                    visibleTasksCount++
+                                } else {
+                                    repository.minimizeTaskInDesk(
+                                        displayId = displayId,
+                                        deskId = newDeskId,
+                                        taskId = task.taskId,
+                                    )
+                                }
+
+                                if (task.desktopTaskTilingState == DesktopTaskTilingState.LEFT) {
+                                    repository.addLeftTiledTask(
+                                        persistentDesktop.displayId,
+                                        task.taskId,
+                                    )
+                                } else if (
+                                    task.desktopTaskTilingState == DesktopTaskTilingState.RIGHT
+                                ) {
+                                    repository.addRightTiledTask(
+                                        persistentDesktop.displayId,
+                                        task.taskId,
+                                    )
+                                }
+                            }
+                    }
                 }
+            } finally {
+                _isInitialized.value = true
             }
         }
     }
+
+    private suspend fun getDesksToRestore(
+        state: DesktopRepositoryState,
+        userId: Int,
+    ): Set<Desktop> {
+        // TODO: b/365873835 - what about desks that won't be restored?
+        //  - invalid desk ids from multi-desk -> single-desk switching can be ignored / deleted.
+        val limitToSingleDeskPerDisplay =
+            !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+        return state.desktopMap.keys
+            .mapNotNull { deskId ->
+                persistentRepository.readDesktop(userId, deskId)?.takeIf { desk ->
+                    // Do not restore invalid desks when multi-desks is disabled. This is
+                    // possible if the feature is disabled after having created multiple desks.
+                    val isValidSingleDesk = desk.desktopId == desk.displayId
+                    (!limitToSingleDeskPerDisplay || isValidSingleDesk)
+                }
+            }
+            .toSet()
+    }
+
+    private fun getTaskLimit(persistedDesk: Desktop): Int =
+        DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
+            ?: persistedDesk.zOrderedTasksCount
+
+    private fun logV(msg: String, vararg arguments: Any?) {
+        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+
+    /** A default implementation of [DeskRecreationFactory] that reuses the desk id. */
+    private class DefaultDeskRecreationFactory : DeskRecreationFactory {
+        override suspend fun recreateDesk(
+            userId: Int,
+            destinationDisplayId: Int,
+            deskId: Int,
+        ): Int = deskId
+    }
+
+    companion object {
+        private const val TAG = "DesktopRepositoryInitializerImpl"
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto
index 0105231..86dcee8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto
@@ -9,9 +9,16 @@
   MINIMIZED = 1;
 }
 
+enum DesktopTaskTilingState {
+  NONE = 1;
+  LEFT = 2;
+  RIGHT = 3;
+}
+
 message DesktopTask {
   optional int32 task_id = 1;
   optional DesktopTaskState desktop_task_state= 2;
+  optional DesktopTaskTilingState desktop_task_tiling_state = 3;
 }
 
 message Desktop {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index dd5827a..320de2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -142,8 +142,8 @@
 ## Tracing activity starts & finishes in the app process
 
 It's sometimes useful to know when to see a stack trace of when an activity starts in the app code
-(ie. if you are repro'ing a bug related to activity starts). You can enable this system property to
-get this trace:
+or via a `WindowContainerTransaction` (ie. if you are repro'ing a bug related to activity starts).
+You can enable this system property to get this trace:
 ```shell
 # Enabling
 adb shell setprop persist.wm.debug.start_activity true
@@ -168,6 +168,21 @@
 adb reboot
 ```
 
+## Tracing transition requests in the Shell
+
+To trace where a new WM transition is started in the Shell, you can enable this system property:
+```shell
+# Enabling
+adb shell setprop persist.wm.debug.start_shell_transition true
+adb reboot
+adb logcat -s "ShellTransitions"
+
+# Disabling
+adb shell setprop persist.wm.debug.start_shell_transition \"\"
+adb reboot
+```
+
+
 ## Dumps
 
 Because the Shell library is built as a part of SystemUI, dumping the state is currently done as a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 897e2d1..2fe7865 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.util.SparseArray;
 import android.view.SurfaceControl;
+import android.window.DesktopExperienceFlags;
 import android.window.DesktopModeFlags;
 
 import com.android.internal.protolog.ProtoLog;
@@ -167,6 +168,11 @@
     }
 
     private void updateLaunchAdjacentController() {
+        if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
+            // With multiple desks, freeform tasks are children of a root task controlled by
+            // DesksOrganizer, so toggling launch-adjacent should be managed there.
+            return;
+        }
         for (int i = 0; i < mTasks.size(); i++) {
             if (mTasks.valueAt(i).mTaskInfo.isVisible) {
                 mLaunchAdjacentController.setLaunchAdjacentEnabled(false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 8059b94..0bf2ea6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -29,6 +29,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.wm.shell.desktopmode.DesktopImmersiveController;
+import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.Transitions;
@@ -52,6 +53,7 @@
     private final WindowDecorViewModel mWindowDecorViewModel;
     private final Optional<TaskChangeListener> mTaskChangeListener;
     private final FocusTransitionObserver mFocusTransitionObserver;
+    private final Optional<DesksTransitionObserver> mDesksTransitionObserver;
 
     private final Map<IBinder, List<ActivityManager.RunningTaskInfo>> mTransitionToTaskInfo =
             new HashMap<>();
@@ -63,12 +65,14 @@
             Optional<DesktopImmersiveController> desktopImmersiveController,
             WindowDecorViewModel windowDecorViewModel,
             Optional<TaskChangeListener> taskChangeListener,
-            FocusTransitionObserver focusTransitionObserver) {
+            FocusTransitionObserver focusTransitionObserver,
+            Optional<DesksTransitionObserver> desksTransitionObserver) {
         mTransitions = transitions;
         mDesktopImmersiveController = desktopImmersiveController;
         mWindowDecorViewModel = windowDecorViewModel;
         mTaskChangeListener = taskChangeListener;
         mFocusTransitionObserver = focusTransitionObserver;
+        mDesksTransitionObserver = desksTransitionObserver;
         if (FreeformComponents.requiresFreeformComponents(context)) {
             shellInit.addInitCallback(this::onInit, this);
         }
@@ -85,6 +89,10 @@
             @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startT,
             @NonNull SurfaceControl.Transaction finishT) {
+        // Update desk state first, otherwise [TaskChangeListener] may update desktop task state
+        // under an outdated active desk if a desk switch and a task update happen in the same
+        // transition, such as when unminimizing a task from an inactive desk.
+        mDesksTransitionObserver.ifPresent(o -> o.onTransitionReady(transition, info));
         if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
             // TODO(b/367268953): Remove when DesktopTaskListener is introduced and the repository
             //  is updated from there **before** the |mWindowDecorViewModel| methods are invoked.
@@ -166,7 +174,7 @@
             SurfaceControl.Transaction finishT) {
         mTaskChangeListener.ifPresent(listener -> listener.onTaskChanging(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskChanging(
-                change.getTaskInfo(), change.getLeash(), startT, finishT);
+                change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
     }
 
     private void onToFrontTransitionReady(
@@ -176,7 +184,7 @@
         mTaskChangeListener.ifPresent(
                 listener -> listener.onTaskMovingToFront(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskChanging(
-                change.getTaskInfo(), change.getLeash(), startT, finishT);
+                change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
     }
 
     private void onToBackTransitionReady(
@@ -186,7 +194,7 @@
         mTaskChangeListener.ifPresent(
                 listener -> listener.onTaskMovingToBack(change.getTaskInfo()));
         mWindowDecorViewModel.onTaskChanging(
-                change.getTaskInfo(), change.getLeash(), startT, finishT);
+                change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 04f0336..0966110 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -70,6 +70,7 @@
 import android.view.Display;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.window.DesktopModeFlags;
 import android.window.DisplayAreaInfo;
 import android.window.TaskOrganizer;
 import android.window.TaskSnapshot;
@@ -78,7 +79,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
-import com.android.window.flags.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -781,7 +781,7 @@
     // TODO(b/377581840): Update this check to include non-minimized cases, e.g. split to PiP etc.
     private boolean isPipExitingToDesktopMode() {
         DesktopRepository currentRepo = getCurrentRepo();
-        return Flags.enableDesktopWindowingPip() && currentRepo != null
+        return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue() && currentRepo != null
                 && (currentRepo.isAnyDeskActive(mTaskInfo.displayId)
                     || isDisplayInFreeform());
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 242f7fa..5706f19 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -39,11 +39,11 @@
 import android.view.InputMonitor;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
+import android.window.DesktopModeFlags;
 
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.policy.TaskResizingAlgorithm;
-import com.android.window.flags.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -183,7 +183,7 @@
     private void reloadResources() {
         final Resources res = mContext.getResources();
         mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
-        mEnableDragCornerResize = Flags.enableDesktopWindowingPip();
+        mEnableDragCornerResize = DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue();
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
index 65099c2..671eae3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
@@ -153,7 +153,12 @@
         mPipUiEventLogger = pipUiEventLogger;
 
         mPipTransitionState.addPipTransitionStateChangedListener(this);
-
+        // Clear actions after exit PiP. Otherwise, next PiP could accidentally inherit the
+        // actions provided by the previous app in PiP mode.
+        mPipBoundsState.addOnPipComponentChangedListener(((oldPipComponent, newPipComponent) -> {
+            if (mAppActions != null) mAppActions.clear();
+            mCloseAction = null;
+        }));
         mPipTaskListener.addParamsChangedListener(new PipTaskListener.PipParamsChangedCallback() {
             @Override
             public void onActionsChanged(List<RemoteAction> actions, RemoteAction closeAction) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index d663484..880e143 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -61,7 +61,7 @@
     private final PipBoundsState mPipBoundsState;
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
     private final ShellExecutor mMainExecutor;
-    private final PictureInPictureParams mPictureInPictureParams =
+    private PictureInPictureParams mPictureInPictureParams =
             new PictureInPictureParams.Builder().build();
 
     private boolean mWaitingForAspectRatioChange = false;
@@ -92,6 +92,11 @@
         }
         mPipResizeAnimatorSupplier = PipResizeAnimator::new;
         mPipScheduler.setPipParamsSupplier(this::getPictureInPictureParams);
+        // Reset {@link #mPictureInPictureParams} after exiting PiP. For instance, next Activity
+        // with null aspect ratio would accidentally inherit the aspect ratio from a previous
+        // PiP Activity.
+        mPipBoundsState.addOnPipComponentChangedListener(((oldPipComponent, newPipComponent) ->
+                mPictureInPictureParams = new PictureInPictureParams.Builder().build()));
     }
 
     void setPictureInPictureParams(@Nullable PictureInPictureParams params) {
@@ -106,8 +111,9 @@
                 listener.onActionsChanged(params.getActions(), params.getCloseAction());
             }
         }
-        mPictureInPictureParams.copyOnlySet(params != null ? params
-                : new PictureInPictureParams.Builder().build());
+        // Set the new params but make sure mPictureInPictureParams is not null.
+        mPictureInPictureParams = params == null
+                ? new PictureInPictureParams.Builder().build() : params;
     }
 
     /** Add a PipParamsChangedCallback listener. */
@@ -138,9 +144,8 @@
         if (mPictureInPictureParams.hasSetAspectRatio()
                 && mPipBoundsAlgorithm.isValidPictureInPictureAspectRatio(newAspectRatio)
                 && PipUtils.aspectRatioChanged(newAspectRatio, mPipBoundsState.getAspectRatio())) {
-            mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
-                onAspectRatioChanged(newAspectRatio);
-            });
+            mPipTransitionState.setOnIdlePipTransitionStateRunnable(
+                    () -> onAspectRatioChanged(newAspectRatio));
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitMultiDisplayHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitMultiDisplayHelper.kt
new file mode 100644
index 0000000..d99be71
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitMultiDisplayHelper.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.splitscreen
+import android.app.ActivityManager
+import android.hardware.display.DisplayManager
+import android.view.SurfaceControl
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.common.split.SplitLayout
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+
+/**
+ * Helper class for managing split-screen functionality across multiple displays.
+ */
+class SplitMultiDisplayHelper(private val displayManager: DisplayManager) {
+
+    /**
+     * A map that stores the [SplitTaskHierarchy] associated with each display ID.
+     * The keys are display IDs (integers), and the values are [SplitTaskHierarchy] objects,
+     * which encapsulate the information needed to manage split-screen tasks on that display.
+     */
+    private val displayTaskMap: MutableMap<Int, SplitTaskHierarchy> = mutableMapOf()
+
+    /**
+     * SplitTaskHierarchy is a class that encapsulates the components required
+     * for managing split-screen functionality on a specific display.
+     */
+    data class SplitTaskHierarchy(
+        var rootTaskInfo: ActivityManager.RunningTaskInfo? = null,
+        var mainStage: StageTaskListener? = null,
+        var sideStage: StageTaskListener? = null,
+        var rootTaskLeash: SurfaceControl? = null,
+        var splitLayout: SplitLayout? = null
+    )
+
+    /**
+     * Returns a list of all currently connected display IDs.
+     *
+     * @return An ArrayList of display IDs.
+     */
+    fun getDisplayIds(): ArrayList<Int> {
+        val displayIds = ArrayList<Int>()
+        displayManager.displays?.forEach { display ->
+            displayIds.add(display.displayId)
+        }
+        return displayIds
+    }
+
+    /**
+     * Swaps the [SplitTaskHierarchy] objects associated with two different display IDs.
+     *
+     * @param firstDisplayId  The ID of the first display.
+     * @param secondDisplayId The ID of the second display.
+     */
+    fun swapDisplayTaskHierarchy(firstDisplayId: Int, secondDisplayId: Int) {
+        if (!displayTaskMap.containsKey(firstDisplayId) || !displayTaskMap.containsKey(secondDisplayId)) {
+            ProtoLog.w(
+                ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                "Attempted to swap task hierarchies for invalid display IDs: %d, %d",
+                firstDisplayId,
+                secondDisplayId
+            )
+            return
+        }
+
+        if (firstDisplayId == secondDisplayId) {
+            return
+        }
+
+        val firstHierarchy = displayTaskMap[firstDisplayId]
+        val secondHierarchy = displayTaskMap[secondDisplayId]
+
+        displayTaskMap[firstDisplayId] = checkNotNull(secondHierarchy)
+        displayTaskMap[secondDisplayId] = checkNotNull(firstHierarchy)
+    }
+
+    /**
+     * Gets the root task info for the given display ID.
+     *
+     * @param displayId The ID of the display.
+     * @return The root task info, or null if not found.
+     */
+    fun getDisplayRootTaskInfo(displayId: Int): ActivityManager.RunningTaskInfo? {
+        return displayTaskMap[displayId]?.rootTaskInfo
+    }
+
+    /**
+     * Sets the root task info for the given display ID.
+     *
+     * @param displayId    The ID of the display.
+     * @param rootTaskInfo The root task info to set.
+     */
+    fun setDisplayRootTaskInfo(
+        displayId: Int,
+        rootTaskInfo: ActivityManager.RunningTaskInfo?
+    ) {
+        val hierarchy = displayTaskMap.computeIfAbsent(displayId) { SplitTaskHierarchy() }
+        hierarchy.rootTaskInfo = rootTaskInfo
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitMultiDisplayProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitMultiDisplayProvider.kt
similarity index 80%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitMultiDisplayProvider.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitMultiDisplayProvider.kt
index d2e57e5..dce3dc1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitMultiDisplayProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitMultiDisplayProvider.kt
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.splitscreen;
+package com.android.wm.shell.splitscreen
 
-import android.window.WindowContainerToken;
+import android.window.WindowContainerToken
 
-public interface SplitMultiDisplayProvider {
+interface SplitMultiDisplayProvider {
     /**
      * Returns the WindowContainerToken for the root of the given display ID.
      *
      * @param displayId The ID of the display.
      * @return The {@link WindowContainerToken} associated with the display's root task.
      */
-    WindowContainerToken getDisplayRootForDisplayId(int displayId);
+    fun getDisplayRootForDisplayId(displayId: Int): WindowContainerToken?
 }
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 7472b0e..014c810 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
@@ -93,7 +93,6 @@
 import android.app.ActivityOptions;
 import android.app.IActivityTaskManager;
 import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
 import android.app.TaskInfo;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
@@ -102,6 +101,7 @@
 import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -124,6 +124,7 @@
 import android.view.WindowManager;
 import android.widget.Toast;
 import android.window.DesktopExperienceFlags;
+import android.window.DesktopModeFlags;
 import android.window.DisplayAreaInfo;
 import android.window.RemoteTransition;
 import android.window.TransitionInfo;
@@ -278,6 +279,8 @@
     // because we will be posting and removing it from the handler.
     private final Runnable mReEnableLaunchAdjacentOnRoot = () -> setLaunchAdjacentDisabled(false);
 
+    private SplitMultiDisplayHelper mSplitMultiDisplayHelper;
+
     /**
      * Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support
      * CompatUI layouts. CompatUI is handled separately by MainStage and SideStage.
@@ -393,6 +396,11 @@
         mDesktopTasksController = desktopTasksController;
         mRootTDAOrganizer = rootTDAOrganizer;
 
+        DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+
+        mSplitMultiDisplayHelper = new SplitMultiDisplayHelper(
+                Objects.requireNonNull(displayManager));
+
         taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
 
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task");
@@ -676,7 +684,8 @@
         if (!enteredSplitSelect) {
             return null;
         }
-        if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
+        if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()
+                && !DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
             mTaskOrganizer.applyTransaction(wct);
             return null;
         }
@@ -2249,10 +2258,10 @@
 
         setRootForceTranslucent(true, wct);
         if (!enableFlexibleSplit()) {
-            //TODO(b/373709676) Need to figure out how adjacentRoots work for flex split
+            // TODO: consider support 3 splits
 
             // Make the stages adjacent to each other so they occlude what's behind them.
-            wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+            wct.setAdjacentRootSet(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
             mSplitLayout.getInvisibleBounds(mTempRect1);
             wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
         }
@@ -2263,7 +2272,7 @@
             });
             mLaunchAdjacentController.setLaunchAdjacentRoot(mSideStage.mRootTaskInfo.token);
         } else {
-            // TODO(b/373709676) Need to figure out how adjacentRoots work for flex split
+            // TODO: consider support 3 splits
         }
     }
 
@@ -2920,7 +2929,7 @@
                     prepareEnterSplitScreen(out);
                     mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
                             TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering, SNAP_TO_2_50_50);
-                } else if (isSplitScreenVisible() && isOpening) {
+                } else if (enableFlexibleTwoAppSplit() && isSplitScreenVisible() && isOpening) {
                     // launching into an existing split stage; possibly launchAdjacent
                     // If we're replacing a pip-able app, we need to let mixed handler take care of
                     // it. Otherwise we'll just treat it as an enter+resize
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
index 34d1011..f652e31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
@@ -85,8 +85,8 @@
         final ActivityManager.TaskDescription taskDescription =
                 SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo);
 
-        final SnapshotWindowRecord record = new SnapshotWindowRecord(mViewHost, wlw.mChildSurface,
-                taskDescription.getBackgroundColor(), snapshot.hasImeSurface(),
+        final SnapshotWindowRecord record = new SnapshotWindowRecord(mViewHost, rootSurface,
+                wlw.mChildSurface, taskDescription.getBackgroundColor(), snapshot.hasImeSurface(),
                 runningTaskInfo.topActivityType, removeExecutor,
                 taskId, mStartingWindowRecordManager);
         mStartingWindowRecordManager.addRecord(taskId, record);
@@ -96,14 +96,16 @@
     private class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord {
         private SurfaceControlViewHost mViewHost;
         private SurfaceControl mChildSurface;
+        private SurfaceControl mRootSurface;
         private final boolean mHasImeSurface;
 
-        SnapshotWindowRecord(SurfaceControlViewHost viewHost, SurfaceControl childSurface,
-                int bgColor, boolean hasImeSurface, int activityType,
+        SnapshotWindowRecord(SurfaceControlViewHost viewHost, SurfaceControl rootSurface,
+                SurfaceControl childSurface, int bgColor, boolean hasImeSurface, int activityType,
                 ShellExecutor removeExecutor, int id,
                 StartingSurfaceDrawer.StartingWindowRecordManager recordManager) {
             super(activityType, removeExecutor, id, recordManager);
             mViewHost = viewHost;
+            mRootSurface = rootSurface;
             mChildSurface = childSurface;
             mBGColor = bgColor;
             mHasImeSurface = hasImeSurface;
@@ -145,6 +147,10 @@
                         mTransactionPool.release(t);
                         mChildSurface = null;
                     }
+                    if (mRootSurface != null && mRootSurface.isValid()) {
+                        mRootSurface.release();
+                    }
+                    mRootSurface = null;
                     if (mViewHost != null) {
                         mViewHost.release();
                         mViewHost = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
index f5aaaad..ce98b03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
@@ -137,8 +137,7 @@
             if (mClipRect != null) {
                 boolean needCrop = false;
                 mAnimClipRect.set(mClipRect);
-                if (transformation.hasClipRect()
-                        && com.android.window.flags.Flags.respectAnimationClip()) {
+                if (transformation.hasClipRect()) {
                     mAnimClipRect.intersectUnchecked(transformation.getClipRect());
                     needCrop = true;
                 }
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 938885c..23dfb41 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
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.DesktopModeFlags.ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX;
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 
 import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
@@ -50,6 +51,7 @@
 
     private @NonNull final Context mContext;
     private @NonNull final ShellExecutor mMainExecutor;
+    private IBinder mPendingStartDragTransition;
     private Boolean mPendingHomeVisibilityUpdate;
 
     public HomeTransitionObserver(@NonNull Context context,
@@ -63,31 +65,42 @@
             @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction) {
-        if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
-            handleTransitionReadyWithBubbleAnything(info);
-        } else {
-            handleTransitionReady(info);
+        Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info);
+
+        if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
+            // Do not apply at the start of desktop drag as that updates launcher UI visibility.
+            // Store the value and apply with a next transition or when cancelling the
+            // desktop-drag transition.
+            storePendingHomeVisibilityUpdate(transition, homeVisibilityUpdate);
+            return;
+        }
+
+        if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+                && info.getType() == TRANSIT_CONVERT_TO_BUBBLE
+                && homeVisibilityUpdate == null) {
+            // We are converting to bubble and we did not get a change to home visibility in this
+            // transition. Apply the value from start of drag.
+            homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
+        }
+
+        if (homeVisibilityUpdate != null) {
+            mPendingHomeVisibilityUpdate = null;
+            mPendingStartDragTransition = null;
+            notifyHomeVisibilityChanged(homeVisibilityUpdate);
         }
     }
 
-    private void handleTransitionReady(@NonNull TransitionInfo info) {
-        for (TransitionInfo.Change change : info.getChanges()) {
-            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
-            if (taskInfo == null
-                    || info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
-                    || taskInfo.displayId != DEFAULT_DISPLAY
-                    || taskInfo.taskId == -1
-                    || !taskInfo.isRunning) {
-                continue;
-            }
-            Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info, change, taskInfo);
-            if (homeVisibilityUpdate != null) {
-                notifyHomeVisibilityChanged(homeVisibilityUpdate);
-            }
+    private void storePendingHomeVisibilityUpdate(
+            IBinder transition, Boolean homeVisibilityUpdate) {
+        if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+                && !ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue()) {
+            return;
         }
+        mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
+        mPendingStartDragTransition = transition;
     }
 
-    private void handleTransitionReadyWithBubbleAnything(@NonNull TransitionInfo info) {
+    private Boolean getHomeVisibilityUpdate(TransitionInfo info) {
         Boolean homeVisibilityUpdate = null;
         for (TransitionInfo.Change change : info.getChanges()) {
             final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -97,29 +110,12 @@
                     || !taskInfo.isRunning) {
                 continue;
             }
-
             Boolean update = getHomeVisibilityUpdate(info, change, taskInfo);
             if (update != null) {
                 homeVisibilityUpdate = update;
             }
         }
-
-        if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
-            // Do not apply at the start of desktop drag as that updates launcher UI visibility.
-            // Store the value and apply with a next transition if needed.
-            mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
-            return;
-        }
-
-        if (info.getType() == TRANSIT_CONVERT_TO_BUBBLE && homeVisibilityUpdate == null) {
-            // We are converting to bubble and we did not get a change to home visibility in this
-            // transition. Apply the value from start of drag.
-            homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
-        }
-        if (homeVisibilityUpdate != null) {
-            mPendingHomeVisibilityUpdate = null;
-            notifyHomeVisibilityChanged(homeVisibilityUpdate);
-        }
+        return homeVisibilityUpdate;
     }
 
     private Boolean getHomeVisibilityUpdate(TransitionInfo info,
@@ -146,7 +142,24 @@
 
     @Override
     public void onTransitionFinished(@NonNull IBinder transition,
-            boolean aborted) {}
+            boolean aborted) {
+        if (!ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX.isTrue()) {
+            return;
+        }
+        // Handle the case where the DragToDesktop START transition is interrupted and we never
+        // receive a CANCEL/END transition.
+        if (mPendingStartDragTransition == null
+                || mPendingStartDragTransition != transition) {
+            return;
+        }
+        mPendingStartDragTransition = null;
+        if (aborted) return;
+
+        if (mPendingHomeVisibilityUpdate != null) {
+            notifyHomeVisibilityChanged(mPendingHomeVisibilityUpdate);
+            mPendingHomeVisibilityUpdate = null;
+        }
+    }
 
     /**
      * Sets the home transition listener that receives any transitions resulting in a change of
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
deleted file mode 100644
index 978b8da..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.wm.shell.transition;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
-
-import android.annotation.NonNull;
-import android.os.RemoteException;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.IWindowContainerTransactionCallback;
-
-import com.android.internal.protolog.ProtoLog;
-
-/**
- * Utilities and interfaces for transition-like usage on top of the legacy app-transition and
- * synctransaction tools.
- */
-public class LegacyTransitions {
-
-    /**
-     * Interface for a "legacy" transition. Effectively wraps a sync callback + remoteAnimation
-     * into one callback.
-     */
-    public interface ILegacyTransition {
-        /**
-         * Called when both the associated sync transaction finishes and the remote animation is
-         * ready.
-         */
-        void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
-                RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
-                IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t);
-    }
-
-    /**
-     * Makes sure that a remote animation and corresponding sync callback are called together
-     * such that the sync callback is called first. This assumes that both the callback receiver
-     * and the remoteanimation are in the same process so that order is preserved on both ends.
-     */
-    public static class LegacyTransition {
-        private final ILegacyTransition mLegacyTransition;
-        private int mSyncId = -1;
-        private SurfaceControl.Transaction mTransaction;
-        private int mTransit;
-        private RemoteAnimationTarget[] mApps;
-        private RemoteAnimationTarget[] mWallpapers;
-        private RemoteAnimationTarget[] mNonApps;
-        private IRemoteAnimationFinishedCallback mFinishCallback = null;
-        private boolean mCancelled = false;
-        private final SyncCallback mSyncCallback = new SyncCallback();
-        private final RemoteAnimationAdapter mAdapter =
-                new RemoteAnimationAdapter(new RemoteAnimationWrapper(), 0, 0);
-
-        public LegacyTransition(@WindowManager.TransitionType int type,
-                @NonNull ILegacyTransition legacyTransition) {
-            mLegacyTransition = legacyTransition;
-            mTransit = type;
-        }
-
-        public @WindowManager.TransitionType int getType() {
-            return mTransit;
-        }
-
-        public IWindowContainerTransactionCallback getSyncCallback() {
-            return mSyncCallback;
-        }
-
-        public RemoteAnimationAdapter getAdapter() {
-            return mAdapter;
-        }
-
-        private class SyncCallback extends IWindowContainerTransactionCallback.Stub {
-            @Override
-            public void onTransactionReady(int id, SurfaceControl.Transaction t)
-                    throws RemoteException {
-                ProtoLog.v(WM_SHELL_TRANSITIONS,
-                        "LegacyTransitions.onTransactionReady(): syncId=%d", id);
-                mSyncId = id;
-                mTransaction = t;
-                checkApply(true /* log */);
-            }
-        }
-
-        private class RemoteAnimationWrapper extends IRemoteAnimationRunner.Stub {
-            @Override
-            public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
-                    RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
-                    IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
-                mTransit = transit;
-                mApps = apps;
-                mWallpapers = wallpapers;
-                mNonApps = nonApps;
-                mFinishCallback = finishedCallback;
-                checkApply(false /* log */);
-            }
-
-            @Override
-            public void onAnimationCancelled() throws RemoteException {
-                mCancelled = true;
-                mApps = mWallpapers = mNonApps = null;
-                checkApply(false /* log */);
-            }
-        }
-
-
-        private void checkApply(boolean log) throws RemoteException {
-            if (mSyncId < 0 || (mFinishCallback == null && !mCancelled)) {
-                if (log) {
-                    ProtoLog.v(WM_SHELL_TRANSITIONS, "\tSkipping hasFinishedCb=%b canceled=%b",
-                            mFinishCallback != null, mCancelled);
-                }
-                return;
-            }
-            if (log) {
-                ProtoLog.v(WM_SHELL_TRANSITIONS, "\tapply");
-            }
-            mLegacyTransition.onAnimationStart(mTransit, mApps, mWallpapers,
-                    mNonApps, mFinishCallback, mTransaction);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index e28a7fa..003ef1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -39,6 +39,7 @@
 
 import static com.android.systemui.shared.Flags.returnAnimationFrameworkLongLived;
 import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
 import static com.android.wm.shell.shared.TransitionUtil.FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY;
 import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
 import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
@@ -52,6 +53,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -138,6 +140,10 @@
         ShellCommandHandler.ShellCommandActionHandler {
     static final String TAG = "ShellTransitions";
 
+    // If set, will print the stack trace for transition starts within the process
+    static final boolean DEBUG_START_TRANSITION = Build.IS_DEBUGGABLE &&
+            SystemProperties.getBoolean("persist.wm.debug.start_shell_transition", false);
+
     /** Set to {@code true} to enable shell transitions. */
     public static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled();
     public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
@@ -346,10 +352,10 @@
         mShellController = shellController;
         // The very last handler (0 in the list) should be the default one.
         mHandlers.add(mDefaultTransitionHandler);
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: Default");
         // Next lowest priority is remote transitions.
         mHandlers.add(mRemoteTransitionHandler);
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: Remote");
         shellInit.addInitCallback(this::onInit, this);
         mHomeTransitionObserver = homeTransitionObserver;
         mFocusTransitionObserver = focusTransitionObserver;
@@ -439,7 +445,7 @@
         mHandlers.add(handler);
         // Set initial scale settings.
         handler.setAnimScaleSetting(mTransitionAnimationScaleSetting);
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: %s",
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: %s",
                 handler.getClass().getSimpleName());
     }
 
@@ -691,7 +697,7 @@
     void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
         info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
                 info.getDebugId(), transitionToken, info.toString("    " /* prefix */));
         int activeIdx = findByToken(mPendingTransitions, transitionToken);
         if (activeIdx < 0) {
@@ -753,7 +759,7 @@
                 if (tr.isIdle()) continue;
                 hadPreceding = true;
                 // Sleep starts a process of forcing all prior transitions to finish immediately
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                ProtoLog.v(WM_SHELL_TRANSITIONS,
                         "Start finish-for-sync track %d", i);
                 finishForSync(active.mToken, i, null /* forceFinish */);
             }
@@ -797,7 +803,7 @@
         if (info.getRootCount() == 0 && !KeyguardTransitionHandler.handles(info)) {
             // No root-leashes implies that the transition is empty/no-op, so just do
             // housekeeping and return.
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots in %s so"
+            ProtoLog.v(WM_SHELL_TRANSITIONS, "No transition roots in %s so"
                     + " abort", active);
             onAbort(active);
             return true;
@@ -839,7 +845,7 @@
                 && allOccluded)) {
             // Treat this as an abort since we are bypassing any merge logic and effectively
             // finishing immediately.
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+            ProtoLog.v(WM_SHELL_TRANSITIONS,
                     "Non-visible anim so abort: %s", active);
             onAbort(active);
             return true;
@@ -873,7 +879,7 @@
     void processReadyQueue(Track track) {
         if (track.mReadyTransitions.isEmpty()) {
             if (track.mActiveTransition == null) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Track %d became idle",
+                ProtoLog.v(WM_SHELL_TRANSITIONS, "Track %d became idle",
                         mTracks.indexOf(track));
                 if (areTracksIdle()) {
                     if (!mReadyDuringSync.isEmpty()) {
@@ -885,7 +891,7 @@
                             if (!success) break;
                         }
                     } else if (mPendingTransitions.isEmpty()) {
-                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition "
+                        ProtoLog.v(WM_SHELL_TRANSITIONS, "All active transition "
                                 + "animations finished");
                         mKnownTransitions.clear();
                         // Run all runnables from the run-when-idle queue.
@@ -926,7 +932,7 @@
             onMerged(playingToken, readyToken);
             return;
         }
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition %s ready while"
                 + " %s is still animating. Notify the animating transition"
                 + " in case they can be merged", ready, playing);
         mTransitionTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
@@ -955,7 +961,7 @@
         }
 
         final Track track = mTracks.get(playing.getTrack());
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
                 merged, playing);
         int readyIdx = 0;
         if (track.mReadyTransitions.isEmpty() || track.mReadyTransitions.get(0) != merged) {
@@ -996,7 +1002,7 @@
     }
 
     private void playTransition(@NonNull ActiveTransition active) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
         final var token = active.mToken;
 
         for (int i = 0; i < mObservers.size(); ++i) {
@@ -1007,12 +1013,12 @@
 
         // If a handler already chose to run this animation, try delegating to it first.
         if (active.mHandler != null) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
+            ProtoLog.v(WM_SHELL_TRANSITIONS, " try firstHandler %s",
                     active.mHandler);
             boolean consumed = active.mHandler.startAnimation(token, active.mInfo,
                     active.mStartT, active.mFinishT, (wct) -> onFinish(token, wct));
             if (consumed) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
+                ProtoLog.v(WM_SHELL_TRANSITIONS, " animated by firstHandler");
                 mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
                 if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                     Trace.instant(TRACE_TAG_WINDOW_MANAGER,
@@ -1042,14 +1048,14 @@
     ) {
         for (int i = mHandlers.size() - 1; i >= 0; --i) {
             if (mHandlers.get(i) == skip) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " skip handler %s",
+                ProtoLog.v(WM_SHELL_TRANSITIONS, " skip handler %s",
                         mHandlers.get(i));
                 continue;
             }
             boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
                     finishCB);
             if (consumed) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
+                ProtoLog.v(WM_SHELL_TRANSITIONS, " animated by %s",
                         mHandlers.get(i));
                 mTransitionTracer.logDispatched(info.getDebugId(), mHandlers.get(i));
                 if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
@@ -1155,7 +1161,7 @@
         for (int i = 0; i < mObservers.size(); ++i) {
             mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
         }
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished "
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition animation finished "
                 + "(aborted=%b), notifying core %s", active.mAborted, active);
         if (active.mStartT != null) {
             // Applied by now, so clear immediately to remove any references. Do not set to null
@@ -1209,7 +1215,7 @@
 
     void requestStartTransition(@NonNull IBinder transitionToken,
             @Nullable TransitionRequestInfo request) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
                 request.getDebugId(), transitionToken, request);
         if (mKnownTransitions.containsKey(transitionToken)) {
             throw new RuntimeException("Transition already started " + transitionToken);
@@ -1228,6 +1234,8 @@
             if (requestResult != null) {
                 active.mHandler = requestResult.first;
                 wct = requestResult.second;
+                ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition (#%d): request handled by %s",
+                        request.getDebugId(), active.mHandler.getClass().getSimpleName());
             }
             if (request.getDisplayChange() != null) {
                 TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
@@ -1273,8 +1281,12 @@
      */
     public IBinder startTransition(@WindowManager.TransitionType int type,
             @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Directly starting a new transition "
                 + "type=%s wct=%s handler=%s", transitTypeToString(type), wct, handler);
+        if (DEBUG_START_TRANSITION) {
+            Log.d(TAG, "startTransition: type=" + transitTypeToString(type)
+                    + " wct=" + wct + " handler=" + handler.getClass().getName(), new Throwable());
+        }
         final ActiveTransition active =
                 new ActiveTransition(mOrganizer.startNewTransition(type, wct));
         active.mHandler = handler;
@@ -1362,7 +1374,7 @@
             }
             // Attempt to merge a SLEEP info to signal that the playing transition needs to
             // fast-forward.
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
+            ProtoLog.v(WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
                     + " into %s via a SLEEP proxy", nextSync, playing);
             playing.mHandler.mergeAnimation(nextSync.mToken, dummyInfo, dummyT, dummyT,
                     playing.mToken, (wct) -> {});
@@ -1598,7 +1610,7 @@
         public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
                 SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
                 throws RemoteException {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady(transaction=%d)",
+            ProtoLog.v(WM_SHELL_TRANSITIONS, "onTransitionReady(transaction=%d)",
                     t.getId());
             mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
                     iBinder, transitionInfo, t, finishT));
@@ -1784,8 +1796,9 @@
         pw.println(prefix + TAG);
 
         final String innerPrefix = prefix + "  ";
-        pw.println(prefix + "Handlers:");
-        for (TransitionHandler handler : mHandlers) {
+        pw.println(prefix + "Handlers (ordered by priority):");
+        for (int i = mHandlers.size() - 1; i >= 0; i--) {
+            final TransitionHandler handler = mHandlers.get(i);
             pw.print(innerPrefix);
             pw.print(handler.getClass().getSimpleName());
             pw.println(" (" + Integer.toHexString(System.identityHashCode(handler)) + ")");
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 7871179..42321e56 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
@@ -49,6 +49,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.window.DisplayAreaInfo;
+import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -233,7 +234,8 @@
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             SurfaceControl.Transaction startT,
-            SurfaceControl.Transaction finishT) {
+            SurfaceControl.Transaction finishT,
+            @TransitionInfo.TransitionMode int changeMode) {
         final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
 
         if (!shouldShowWindowDecor(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
index 2b2cdf8..4511fbe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
@@ -31,6 +31,7 @@
 import android.view.KeyEvent;
 import android.view.SurfaceControl;
 import android.view.View;
+import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -159,7 +160,8 @@
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             SurfaceControl.Transaction startT,
-            SurfaceControl.Transaction finishT) {
+            SurfaceControl.Transaction finishT,
+            @TransitionInfo.TransitionMode int changeMode) {
         final CarWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
 
         if (!shouldShowWindowDecor(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
index 01fc644..adc5cdf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt
@@ -43,7 +43,7 @@
     private val captionWidth: Int,
     private val windowManagerWrapper: WindowManagerWrapper,
     context: Context,
-    snapshotList: List<Pair<Int, TaskSnapshot>>,
+    snapshotList: List<Pair<Int, TaskSnapshot?>>,
     onIconClickListener: ((Int) -> Unit),
     onOutsideClickListener: (() -> Unit)
 ) : ManageWindowsViewContainer(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt
index 02a5433..3a75933 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt
@@ -54,7 +54,7 @@
     private val desktopUserRepositories: DesktopUserRepositories,
     private val surfaceControlBuilderSupplier: Supplier<SurfaceControl.Builder>,
     private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>,
-    snapshotList: List<Pair<Int, TaskSnapshot>>,
+    snapshotList: List<Pair<Int, TaskSnapshot?>>,
     onIconClickListener: ((Int) -> Unit),
     onOutsideClickListener: (() -> Unit)
 ) : ManageWindowsViewContainer(
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 7ef1a93..800faca 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
@@ -17,11 +17,9 @@
 package com.android.wm.shell.windowdecor;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_HOVER_ENTER;
@@ -29,6 +27,7 @@
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
 
 import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
 import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
@@ -79,9 +78,9 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewRootImpl;
-import android.view.WindowManager;
 import android.window.DesktopModeFlags;
 import android.window.TaskSnapshot;
+import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -121,7 +120,6 @@
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
 import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
-import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
 import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
 import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction;
 import com.android.wm.shell.desktopmode.common.ToggleTaskSizeUtilsKt;
@@ -146,6 +144,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.AppHandleAndHeaderVisibilityHelper;
 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;
@@ -153,18 +152,19 @@
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
 import com.android.wm.shell.windowdecor.tiling.SnapEventHandler;
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
 
 import kotlin.Pair;
 import kotlin.Unit;
 import kotlin.jvm.functions.Function1;
 
-import org.jetbrains.annotations.NotNull;
-
 import kotlinx.coroutines.CoroutineScope;
 import kotlinx.coroutines.ExperimentalCoroutinesApi;
 import kotlinx.coroutines.MainCoroutineDispatcher;
 
+import org.jetbrains.annotations.NotNull;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -207,7 +207,9 @@
     private final Optional<DesktopTasksLimiter> mDesktopTasksLimiter;
     private final AppHandleEducationController mAppHandleEducationController;
     private final AppToWebEducationController mAppToWebEducationController;
+    private final AppHandleAndHeaderVisibilityHelper mAppHandleAndHeaderVisibilityHelper;
     private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
+    private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -294,6 +296,7 @@
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             AppHandleEducationController appHandleEducationController,
             AppToWebEducationController appToWebEducationController,
+            AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
             Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
             FocusTransitionObserver focusTransitionObserver,
@@ -332,12 +335,14 @@
                 new InputMonitorFactory(),
                 SurfaceControl.Transaction::new,
                 new AppHeaderViewHolder.Factory(),
+                new AppHandleViewHolder.Factory(),
                 rootTaskDisplayAreaOrganizer,
                 new SparseArray<>(),
                 interactionJankMonitor,
                 desktopTasksLimiter,
                 appHandleEducationController,
                 appToWebEducationController,
+                appHandleAndHeaderVisibilityHelper,
                 windowDecorCaptionHandleRepository,
                 activityOrientationChangeHandler,
                 new TaskPositionerFactory(),
@@ -380,12 +385,14 @@
             InputMonitorFactory inputMonitorFactory,
             Supplier<SurfaceControl.Transaction> transactionFactory,
             AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+            AppHandleViewHolder.Factory appHandleViewHolderFactory,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId,
             InteractionJankMonitor interactionJankMonitor,
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             AppHandleEducationController appHandleEducationController,
             AppToWebEducationController appToWebEducationController,
+            AppHandleAndHeaderVisibilityHelper appHandleAndHeaderVisibilityHelper,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
             Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
             TaskPositionerFactory taskPositionerFactory,
@@ -421,6 +428,7 @@
         mInputMonitorFactory = inputMonitorFactory;
         mTransactionFactory = transactionFactory;
         mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
+        mAppHandleViewHolderFactory = appHandleViewHolderFactory;
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
         mGenericLinksParser = genericLinksParser;
         mInputManager = mContext.getSystemService(InputManager.class);
@@ -431,6 +439,7 @@
         mDesktopTasksLimiter = desktopTasksLimiter;
         mAppHandleEducationController = appHandleEducationController;
         mAppToWebEducationController = appToWebEducationController;
+        mAppHandleAndHeaderVisibilityHelper = appHandleAndHeaderVisibilityHelper;
         mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository;
         mActivityOrientationChangeHandler = activityOrientationChangeHandler;
         mAssistContentRequester = assistContentRequester;
@@ -484,7 +493,8 @@
                 new DesktopModeOnTaskResizeAnimationListener());
         mDesktopTasksController.setOnTaskRepositionAnimationListener(
                 new DesktopModeOnTaskRepositionAnimationListener());
-        if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()) {
+        if (DesktopModeFlags.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue()
+                || DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
             mRecentsTransitionHandler.addTransitionStateListener(
                     new DesktopModeRecentsTransitionStateListener());
         }
@@ -528,6 +538,7 @@
     @Override
     public void setSplitScreenController(SplitScreenController splitScreenController) {
         mSplitScreenController = splitScreenController;
+        mAppHandleAndHeaderVisibilityHelper.setSplitScreenController(splitScreenController);
     }
 
     @Override
@@ -584,7 +595,8 @@
             RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             SurfaceControl.Transaction startT,
-            SurfaceControl.Transaction finishT) {
+            SurfaceControl.Transaction finishT,
+            @TransitionInfo.TransitionMode int changeMode) {
         final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
         if (!shouldShowWindowDecor(taskInfo)) {
             if (decoration != null) {
@@ -598,8 +610,8 @@
         } else {
             decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
                     false /* shouldSetTaskPositionAndCrop */,
-                    mFocusTransitionObserver.hasGlobalFocus(taskInfo),
-                    mExclusionRegion);
+                    mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion,
+                    /*isMovingToBack= */ changeMode == TRANSIT_TO_BACK);
         }
     }
 
@@ -614,7 +626,7 @@
         decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
                 false /* shouldSetTaskPositionAndCrop */,
                 mFocusTransitionObserver.hasGlobalFocus(taskInfo),
-                mExclusionRegion);
+                mExclusionRegion, /* isMovingToBack= */ false);
     }
 
     @Override
@@ -814,9 +826,6 @@
             return;
         }
         decoration.closeHandleMenu();
-        // When the app enters split-select, the handle will no longer be visible, meaning
-        // we shouldn't receive input for it any longer.
-        decoration.disposeStatusBarInputLayer();
         mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
         mDesktopModeUiEventLogger.log(decoration.mTaskInfo,
                 DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_SPLIT_SCREEN);
@@ -976,6 +985,7 @@
         private boolean mIsCustomHeaderGesture;
         private boolean mIsResizeGesture;
         private boolean mIsDragging;
+        private boolean mDragInterrupted;
         private boolean mLongClickDisabled;
         private int mDragPointerId = -1;
         private MotionEvent mMotionEvent;
@@ -1213,9 +1223,14 @@
                 View v, MotionEvent e) {
             final int id = v.getId();
             if (id == R.id.caption_handle) {
-                handleCaptionThroughStatusBar(e, decoration);
+                handleCaptionThroughStatusBar(e, decoration,
+                        /* interruptDragCallback= */
+                        () -> {
+                            mDragInterrupted = true;
+                            setIsDragging(decoration, /* isDragging= */ false);
+                        });
                 final boolean wasDragging = mIsDragging;
-                updateDragStatus(e.getActionMasked());
+                updateDragStatus(decoration, e);
                 final boolean upOrCancel = e.getActionMasked() == ACTION_UP
                         || e.getActionMasked() == ACTION_CANCEL;
                 if (wasDragging && upOrCancel) {
@@ -1231,6 +1246,13 @@
             return false;
         }
 
+        private void setIsDragging(
+                @Nullable DesktopModeWindowDecoration decor, boolean isDragging) {
+            mIsDragging = isDragging;
+            if (decor == null) return;
+            decor.setIsDragging(isDragging);
+        }
+
         private boolean handleFreeformMotionEvent(DesktopModeWindowDecoration decoration,
                 RunningTaskInfo taskInfo, View v, MotionEvent e) {
             final int id = v.getId();
@@ -1250,7 +1272,7 @@
                         final Rect initialBounds = mDragPositioningCallback.onDragPositioningStart(
                                 0 /* ctrlType */, e.getDisplayId(), e.getRawX(0),
                                 e.getRawY(0));
-                        updateDragStatus(e.getActionMasked());
+                        updateDragStatus(decoration, e);
                         mOnDragStartInitialBounds.set(initialBounds);
                     }
                     // Do not consume input event if a button is touched, otherwise it would
@@ -1277,7 +1299,7 @@
                             newTaskBounds);
                     //  Flip mIsDragging only if the bounds actually changed.
                     if (mIsDragging || !newTaskBounds.equals(mOnDragStartInitialBounds)) {
-                        updateDragStatus(e.getActionMasked());
+                        updateDragStatus(decoration, e);
                     }
                     return true;
                 }
@@ -1310,7 +1332,7 @@
                         // onClick call that results.
                         return false;
                     } else {
-                        updateDragStatus(e.getActionMasked());
+                        updateDragStatus(decoration, e);
                         return true;
                     }
                 }
@@ -1318,16 +1340,19 @@
             return true;
         }
 
-        private void updateDragStatus(int eventAction) {
-            switch (eventAction) {
+        private void updateDragStatus(DesktopModeWindowDecoration decor, MotionEvent e) {
+            switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
-                    mIsDragging = false;
+                    mDragInterrupted = false;
+                    setIsDragging(decor, false /* isDragging */);
                     break;
                 }
                 case MotionEvent.ACTION_MOVE: {
-                    mIsDragging = true;
+                    if (!mDragInterrupted) {
+                        setIsDragging(decor, true /* isDragging */);
+                    }
                     break;
                 }
             }
@@ -1448,7 +1473,8 @@
             if (!mInImmersiveMode && (relevantDecor == null
                     || relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM
                     || mTransitionDragActive)) {
-                handleCaptionThroughStatusBar(ev, relevantDecor);
+                handleCaptionThroughStatusBar(ev, relevantDecor,
+                        /* interruptDragCallback= */ () -> {});
             }
         }
         handleEventOutsideCaption(ev, relevantDecor);
@@ -1488,7 +1514,7 @@
      * Turn on desktop mode if handle is dragged below status bar.
      */
     private void handleCaptionThroughStatusBar(MotionEvent ev,
-            DesktopModeWindowDecoration relevantDecor) {
+            DesktopModeWindowDecoration relevantDecor, Runnable interruptDragCallback) {
         if (relevantDecor == null) {
             if (ev.getActionMasked() == ACTION_UP) {
                 mMoveToDesktopAnimator = null;
@@ -1583,13 +1609,23 @@
                                     relevantDecor.mTaskInfo,
                                     relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY(),
                                     dragStartState);
-                    if (indicatorType != TO_FULLSCREEN_INDICATOR) {
+                    if (indicatorType != TO_FULLSCREEN_INDICATOR
+                            || BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
                         if (mMoveToDesktopAnimator == null) {
                             mMoveToDesktopAnimator = new MoveToDesktopAnimator(
                                     mContext, mDragToDesktopAnimationStartBounds,
                                     relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
                             mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo,
-                                    mMoveToDesktopAnimator, relevantDecor.mTaskSurface);
+                                    mMoveToDesktopAnimator, relevantDecor.mTaskSurface,
+                                    /* dragInterruptedCallback= */ () -> {
+                                        // Don't call into DesktopTasksController to cancel the
+                                        // transition here - the transition handler already handles
+                                        // that (including removing the visual indicator).
+                                        mTransitionDragActive = false;
+                                        mMoveToDesktopAnimator = null;
+                                        relevantDecor.handleDragInterrupted();
+                                        interruptDragCallback.run();
+                                    });
                         }
                     }
                     if (mMoveToDesktopAnimator != null) {
@@ -1717,32 +1753,7 @@
     }
 
     private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
-        if (mDisplayController.getDisplay(taskInfo.displayId) == null) {
-            // If DisplayController doesn't have it tracked, it could be a private/managed display.
-            return false;
-        }
-        if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
-        if (mSplitScreenController != null
-                && mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) {
-            return false;
-        }
-        if (mDesktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) {
-            return false;
-        }
-        final boolean isOnLargeScreen =
-                mDisplayController.getDisplay(taskInfo.displayId).getMinSizeDimensionDp()
-                        >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
-        if (!DesktopModeStatus.canEnterDesktopMode(mContext)
-                && DesktopModeStatus.overridesShowAppHandle(mContext) && !isOnLargeScreen) {
-            // Devices with multiple screens may enable the app handle but it should not show on
-            // small screens
-            return false;
-        }
-        return DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(mContext)
-                && !DesktopWallpaperActivity.isWallpaperTask(taskInfo)
-                && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
-                && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
-                && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop();
+        return mAppHandleAndHeaderVisibilityHelper.shouldShowAppHandleOrHeader(taskInfo);
     }
 
     private void createWindowDecoration(
@@ -1776,6 +1787,7 @@
                         mMainChoreographer,
                         mSyncQueue,
                         mAppHeaderViewHolderFactory,
+                        mAppHandleViewHolderFactory,
                         mRootTaskDisplayAreaOrganizer,
                         mGenericLinksParser,
                         mAssistContentRequester,
@@ -1867,12 +1879,13 @@
         windowDecoration.relayout(taskInfo, startT, finishT,
                 false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
                 mFocusTransitionObserver.hasGlobalFocus(taskInfo),
-                mExclusionRegion);
+                mExclusionRegion, /* isMovingToBack= */ false);
         if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
             incrementEventReceiverTasks(taskInfo.displayId);
         }
     }
 
+    @Nullable
     private RunningTaskInfo getOtherSplitTask(int taskId) {
         @SplitPosition int remainingTaskPosition = mSplitScreenController
                 .getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 2a53157..bcf9396 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -27,8 +27,10 @@
 import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
 import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS;
 
+import static com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightId;
 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.DesktopModeStatus.isDesktopModeSupportedOnDisplay;
 import static com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.windowdecor.DragPositioningCallbackUtility.DragEventListener;
@@ -188,6 +190,7 @@
     private ExclusionRegionListener mExclusionRegionListener;
 
     private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
+    private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
     private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
     private final MaximizeMenuFactory mMaximizeMenuFactory;
     private final HandleMenuFactory mHandleMenuFactory;
@@ -207,9 +210,10 @@
     private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
     private final DesktopUserRepositories mDesktopUserRepositories;
     private boolean mIsRecentsTransitionRunning = false;
-
+    private boolean mIsDragging = false;
     private Runnable mLoadAppInfoRunnable;
     private Runnable mSetAppInfoRunnable;
+    private boolean mIsMovingToBack;
 
     public DesktopModeWindowDecoration(
             Context context,
@@ -229,6 +233,7 @@
             Choreographer choreographer,
             SyncTransactionQueue syncQueue,
             AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+            AppHandleViewHolder.Factory appHandleViewHolderFactory,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             AppToWebGenericLinksParser genericLinksParser,
             AssistContentRequester assistContentRequester,
@@ -240,10 +245,10 @@
         this (context, userContext, displayController, taskResourceLoader, splitScreenController,
                 desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
                 mainExecutor, mainDispatcher, bgScope, bgExecutor, choreographer, syncQueue,
-                appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser,
-                assistContentRequester, SurfaceControl.Builder::new,
-                SurfaceControl.Transaction::new, WindowContainerTransaction::new,
-                SurfaceControl::new, new WindowManagerWrapper(
+                appHeaderViewHolderFactory, appHandleViewHolderFactory,
+                rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
+                SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+                WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
                         context.getSystemService(WindowManager.class)),
                 new SurfaceControlViewHostFactory() {},
                 windowDecorViewHostSupplier,
@@ -271,6 +276,7 @@
             Choreographer choreographer,
             SyncTransactionQueue syncQueue,
             AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+            AppHandleViewHolder.Factory appHandleViewHolderFactory,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             AppToWebGenericLinksParser genericLinksParser,
             AssistContentRequester assistContentRequester,
@@ -300,6 +306,7 @@
         mChoreographer = choreographer;
         mSyncQueue = syncQueue;
         mAppHeaderViewHolderFactory = appHeaderViewHolderFactory;
+        mAppHandleViewHolderFactory = appHandleViewHolderFactory;
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
         mGenericLinksParser = genericLinksParser;
         mAssistContentRequester = assistContentRequester;
@@ -460,7 +467,7 @@
         // causes flickering. See b/270202228.
         final boolean applyTransactionOnDraw = taskInfo.isFreeform();
         relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
-                hasGlobalFocus, displayExclusionRegion);
+                hasGlobalFocus, displayExclusionRegion, mIsMovingToBack);
         if (!applyTransactionOnDraw) {
             t.apply();
         }
@@ -487,7 +494,8 @@
     void relayout(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
             boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
-            boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
+            boolean hasGlobalFocus, @NonNull Region displayExclusionRegion,
+            boolean isMovingToBack) {
         Trace.beginSection("DesktopModeWindowDecoration#relayout");
 
         if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_APP_TO_WEB.isTrue()) {
@@ -510,12 +518,17 @@
 
         final boolean inFullImmersive = mDesktopUserRepositories.getProfile(taskInfo.userId)
                 .isTaskInFullImmersiveState(taskInfo.taskId);
+        mIsMovingToBack = isMovingToBack;
         updateRelayoutParams(mRelayoutParams, mContext, taskInfo, mSplitScreenController,
                 applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
                 mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive,
-                mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
-                displayExclusionRegion, mIsRecentsTransitionRunning,
-                mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo));
+                mIsDragging, mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
+                displayExclusionRegion,
+                /* shouldIgnoreCornerRadius= */ mIsRecentsTransitionRunning
+                        && DesktopModeFlags
+                        .ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue(),
+                mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo),
+                mIsRecentsTransitionRunning, mIsMovingToBack);
 
         final WindowDecorLinearLayout oldRootView = mResult.mRootView;
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -540,7 +553,9 @@
             return;
         }
 
-        if (oldRootView != mResult.mRootView) {
+        if (DesktopModeFlags.SKIP_DECOR_VIEW_RELAYOUT_WHEN_CLOSING_BUGFIX.isTrue()
+                ? (oldRootView != mResult.mRootView && taskInfo.isVisibleRequested)
+                : oldRootView != mResult.mRootView) {
             disposeStatusBarInputLayer();
             mWindowDecorViewHolder = createViewHolder();
             // Load these only when first creating the view.
@@ -556,29 +571,15 @@
             });
         }
 
-        final Point position = new Point();
-        if (isAppHandle(mWindowDecorViewHolder)) {
-            position.set(determineHandlePosition());
-        }
         if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
             notifyCaptionStateChanged();
         }
 
         Trace.beginSection("DesktopModeWindowDecoration#relayout-bindData");
         if (isAppHandle(mWindowDecorViewHolder)) {
-            mWindowDecorViewHolder.bindData(new AppHandleViewHolder.HandleData(
-                    mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
-                    isCaptionVisible()
-            ));
+            updateAppHandleViewHolder();
         } else {
-            mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
-                    mTaskInfo,
-                    DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
-                    inFullImmersive,
-                    hasGlobalFocus,
-                    /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
-                            /* animatingTaskResizeOrReposition= */ false)
-            ));
+            updateAppHeaderViewHolder(inFullImmersive, hasGlobalFocus);
         }
         Trace.endSection();
 
@@ -855,9 +856,31 @@
         asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
     }
 
+    /** Update the view holder for app handle. */
+    private void updateAppHandleViewHolder() {
+        if (!isAppHandle(mWindowDecorViewHolder)) return;
+        asAppHandle(mWindowDecorViewHolder).bindData(new AppHandleViewHolder.HandleData(
+                mTaskInfo, determineHandlePosition(), mResult.mCaptionWidth,
+                mResult.mCaptionHeight, isCaptionVisible()
+        ));
+    }
+
+    /** Update the view holder for app header. */
+    private void updateAppHeaderViewHolder(boolean inFullImmersive, boolean hasGlobalFocus) {
+        if (!isAppHeader(mWindowDecorViewHolder)) return;
+        asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData(
+                mTaskInfo,
+                DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
+                inFullImmersive,
+                hasGlobalFocus,
+                /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
+                    /* animatingTaskResizeOrReposition= */ false)
+        ));
+    }
+
     private WindowDecorationViewHolder createViewHolder() {
         if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
-            return new AppHandleViewHolder(
+            return mAppHandleViewHolderFactory.create(
                     mResult.mRootView,
                     mOnCaptionTouchListener,
                     mOnCaptionButtonClickListener,
@@ -884,6 +907,10 @@
         return viewHolder instanceof AppHandleViewHolder;
     }
 
+    private boolean isAppHeader(WindowDecorationViewHolder viewHolder) {
+        return viewHolder instanceof AppHeaderViewHolder;
+    }
+
     @Nullable
     private AppHandleViewHolder asAppHandle(WindowDecorationViewHolder viewHolder) {
         if (viewHolder instanceof AppHandleViewHolder) {
@@ -911,11 +938,14 @@
             boolean isStatusBarVisible,
             boolean isKeyguardVisibleAndOccluded,
             boolean inFullImmersiveMode,
+            boolean isDragging,
             @NonNull InsetsState displayInsetsState,
             boolean hasGlobalFocus,
             @NonNull Region displayExclusionRegion,
             boolean shouldIgnoreCornerRadius,
-            boolean shouldExcludeCaptionFromAppBounds) {
+            boolean shouldExcludeCaptionFromAppBounds,
+            boolean isRecentsTransitionRunning,
+            boolean isMovingToBack) {
         final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
         final boolean isAppHeader =
                 captionLayoutId == R.layout.desktop_mode_app_header;
@@ -932,10 +962,23 @@
         // the first frame.
         relayoutParams.mAsyncViewHost = isAppHandle;
 
-        final boolean showCaption;
-        if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
+        boolean showCaption;
+        // If this relayout is occurring from an observed TRANSIT_TO_BACK transition, do not
+        // show caption (this includes split select transition).
+        if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
+                && isMovingToBack && !isDragging) {
+            showCaption = false;
+        } else if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
+            // If the task is being dragged, the caption should not be hidden so that it continues
+            // receiving input
+            showCaption = true;
+        } else if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
+                && isRecentsTransitionRunning) {
+            // Caption should not be visible in recents.
+            showCaption = false;
+        } else if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
             if (inFullImmersiveMode) {
-                showCaption = isStatusBarVisible && !isKeyguardVisibleAndOccluded;
+                showCaption = (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
             } else {
                 showCaption = taskInfo.isFreeform()
                         || (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
@@ -1090,8 +1133,8 @@
         return Resources.ID_NULL;
     }
 
-    private PointF calculateMaximizeMenuPosition(int menuWidth, int menuHeight) {
-        final PointF position = new PointF();
+    private Point calculateMaximizeMenuPosition(int menuWidth, int menuHeight) {
+        final Point position = new Point();
         final Resources resources = mContext.getResources();
         final DisplayLayout displayLayout =
                 mDisplayController.getDisplayLayout(mTaskInfo.displayId);
@@ -1106,11 +1149,11 @@
         final int[] maximizeButtonLocation = new int[2];
         maximizeWindowButton.getLocationInWindow(maximizeButtonLocation);
 
-        float menuLeft = (mPositionInParent.x + maximizeButtonLocation[0] - ((float) (menuWidth
-                - maximizeWindowButton.getWidth()) / 2));
-        float menuTop = (mPositionInParent.y + captionHeight);
-        final float menuRight = menuLeft + menuWidth;
-        final float menuBottom = menuTop + menuHeight;
+        int menuLeft = (mPositionInParent.x + maximizeButtonLocation[0] - (menuWidth
+                - maximizeWindowButton.getWidth()) / 2);
+        int menuTop = (mPositionInParent.y + captionHeight);
+        final int menuRight = menuLeft + menuWidth;
+        final int menuBottom = menuTop + menuHeight;
 
         // If the menu is out of screen bounds, shift it as needed
         if (menuLeft < 0) {
@@ -1122,7 +1165,7 @@
             menuTop = (displayHeight - menuHeight);
         }
 
-        return new PointF(menuLeft, menuTop);
+        return new Point(menuLeft, menuTop);
     }
 
     boolean isHandleMenuActive() {
@@ -1405,7 +1448,7 @@
                 supportsMultiInstance,
                 shouldShowManageWindowsButton,
                 shouldShowChangeAspectRatioButton,
-                canEnterDesktopMode(mContext),
+                isDesktopModeSupportedOnDisplay(mContext, mDisplay),
                 isBrowserApp,
                 isBrowserApp ? getAppLink() : getBrowserLink(),
                 mResult.mCaptionWidth,
@@ -1676,6 +1719,17 @@
         }
     }
 
+    /**
+     * Indicates that an app handle drag has been interrupted, this can happen e.g. if we receive an
+     * unknown transition during the drag-to-desktop transition.
+     */
+    void handleDragInterrupted() {
+        if (mResult.mRootView == null) return;
+        final View handle = mResult.mRootView.findViewById(R.id.caption_handle);
+        handle.setHovered(false);
+        handle.setPressed(false);
+    }
+
     private boolean pointInView(View v, float x, float y) {
         return v != null && v.getLeft() <= x && v.getRight() >= x
                 && v.getTop() <= y && v.getBottom() >= y;
@@ -1762,7 +1816,7 @@
     private static int getCaptionHeightIdStatic(@WindowingMode int windowingMode) {
         return windowingMode == WINDOWING_MODE_FULLSCREEN
                 ? com.android.internal.R.dimen.status_bar_height_default
-                : DesktopModeUtils.getAppHeaderHeightId();
+                : getDesktopViewAppHeaderHeightId();
     }
 
     private int getCaptionHeight(@WindowingMode int windowingMode) {
@@ -1793,9 +1847,25 @@
      * <p> When a Recents transition is active we allow that transition to take ownership of the
      * corner radius of its task surfaces, so each window decoration should stop updating the corner
      * radius of its task surface during that time.
+     *
+     * We should not allow input to reach the input layer during a Recents transition, so
+     * update the handle view holder accordingly if transition status changes.
      */
     void setIsRecentsTransitionRunning(boolean isRecentsTransitionRunning) {
-        mIsRecentsTransitionRunning = isRecentsTransitionRunning;
+        if (mIsRecentsTransitionRunning != isRecentsTransitionRunning) {
+            mIsRecentsTransitionRunning = isRecentsTransitionRunning;
+            if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
+                // We don't relayout decor on recents transition, so we need to call it directly.
+                relayout(mTaskInfo, mHasGlobalFocus, mRelayoutParams.mDisplayExclusionRegion);
+            }
+        }
+    }
+
+    /**
+     * Declares whether the window decoration is being dragged.
+     */
+    void setIsDragging(boolean isDragging) {
+        mIsDragging = isDragging;
     }
 
     /**
@@ -1853,6 +1923,7 @@
                 Choreographer choreographer,
                 SyncTransactionQueue syncQueue,
                 AppHeaderViewHolder.Factory appHeaderViewHolderFactory,
+                AppHandleViewHolder.Factory appHandleViewHolderFactory,
                 RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
                 AppToWebGenericLinksParser genericLinksParser,
                 AssistContentRequester assistContentRequester,
@@ -1880,6 +1951,7 @@
                     choreographer,
                     syncQueue,
                     appHeaderViewHolderFactory,
+                    appHandleViewHolderFactory,
                     rootTaskDisplayAreaOrganizer,
                     genericLinksParser,
                     assistContentRequester,
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 7a4a834..8a8bdca 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
@@ -23,12 +23,12 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
 
+import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
-import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEventFromTouchscreen;
 
@@ -127,7 +127,9 @@
             Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
             Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
             DisplayController displayController,
-            DesktopModeEventLogger desktopModeEventLogger) {
+            DesktopModeEventLogger desktopModeEventLogger,
+            InputChannel inputChannel,
+            InputChannel sinkInputChannel) {
         mContext = context;
         mWindowSession = windowSession;
         mBgExecutor = bgExecutor;
@@ -136,7 +138,11 @@
         mHandler = handler;
         mChoreographer = choreographer;
         mDisplayId = displayId;
-        mDecorationSurface = decorationSurface;
+        // Creates a new SurfaceControl pointing the same underlying surface with decorationSurface
+        // to ensure that mDecorationSurface will not be released while it's used on the background
+        // thread. Note that the empty name will be overridden by the next copyFrom call.
+        mDecorationSurface = surfaceControlBuilderSupplier.get().setName("").build();
+        mDecorationSurface.copyFrom(decorationSurface, "DragResizeInputListener");
         mDragPositioningCallback = callback;
         mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
         mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
@@ -154,9 +160,13 @@
             final InputSetUpResult result = setUpInputChannels(mDisplayId, mWindowSession,
                     mDecorationSurface, mClientToken, mSinkClientToken,
                     mSurfaceControlBuilderSupplier,
-                    mSurfaceControlTransactionSupplier);
+                    mSurfaceControlTransactionSupplier, inputChannel, sinkInputChannel);
             mainExecutor.execute(() -> {
                 if (mClosed) {
+                    result.mInputChannel.dispose();
+                    result.mSinkInputChannel.dispose();
+                    mSurfaceControlTransactionSupplier.get().remove(
+                            result.mInputSinkSurface).apply();
                     return;
                 }
                 mInputSinkSurface = result.mInputSinkSurface;
@@ -208,7 +218,7 @@
                 new DefaultTaskResizeInputEventReceiverFactory(), taskInfo,
                 handler, choreographer, displayId, decorationSurface, callback,
                 surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
-                displayController, desktopModeEventLogger);
+                displayController, desktopModeEventLogger, new InputChannel(), new InputChannel());
     }
 
     DragResizeInputListener(
@@ -251,11 +261,11 @@
             @NonNull IBinder clientToken,
             @NonNull IBinder sinkClientToken,
             @NonNull Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
-            @NonNull Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
+            @NonNull Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
+            @NonNull InputChannel inputChannel,
+            @NonNull InputChannel sinkInputChannel) {
         Trace.beginSection("DragResizeInputListener#setUpInputChannels");
         final InputTransferToken inputTransferToken = new InputTransferToken();
-        final InputChannel inputChannel = new InputChannel();
-        final InputChannel sinkInputChannel = new InputChannel();
         try {
             windowSession.grantInputChannel(
                     displayId,
@@ -332,6 +342,7 @@
         try {
             mWindowSession.updateInputChannel(
                     mInputChannel.getToken(),
+                    null /* hostInputToken */,
                     mDisplayId,
                     mDecorationSurface,
                     FLAG_NOT_FOCUSABLE,
@@ -373,6 +384,7 @@
         try {
             mWindowSession.updateInputChannel(
                     mSinkInputChannel.getToken(),
+                    null /* hostInputToken */,
                     mDisplayId,
                     mInputSinkSurface,
                     FLAG_NOT_FOCUSABLE,
@@ -421,6 +433,9 @@
             } catch (RemoteException e) {
                 e.rethrowFromSystemServer();
             }
+            // Removing this surface on the background thread to ensure that mInitInputChannels has
+            // already been finished.
+            mSurfaceControlTransactionSupplier.get().remove(mDecorationSurface).apply();
         });
     }
 
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 2d6f745..732f042 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
@@ -173,6 +173,9 @@
         return new Rect(mRepositionTaskBounds);
     }
 
+    @Override
+    public void close() {}
+
     private boolean isResizing() {
         return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
                 || (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
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 ce78617..9cc64ac 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
@@ -39,7 +39,6 @@
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.Space
-import android.widget.TextView
 import android.window.DesktopModeFlags
 import android.window.SurfaceSyncGroup
 import androidx.annotation.StringRes
@@ -59,10 +58,10 @@
 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.DrawableInsets
 import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
 import com.android.wm.shell.windowdecor.common.calculateMenuPosition
-import com.android.wm.shell.windowdecor.common.DrawableInsets
-import com.android.wm.shell.windowdecor.common.createRippleDrawable
+import com.android.wm.shell.windowdecor.common.createBackgroundDrawable
 import com.android.wm.shell.windowdecor.extension.isFullscreen
 import com.android.wm.shell.windowdecor.extension.isMultiWindow
 import com.android.wm.shell.windowdecor.extension.isPinned
@@ -109,19 +108,19 @@
     private val isViewAboveStatusBar: Boolean
         get() = (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue() && !taskInfo.isFreeform)
 
-    private val pillElevation: Int = loadDimensionPixelSize(
-        R.dimen.desktop_mode_handle_menu_pill_elevation)
     private val pillTopMargin: Int = loadDimensionPixelSize(
-        R.dimen.desktop_mode_handle_menu_pill_spacing_margin)
-    private val menuWidth = loadDimensionPixelSize(
-        R.dimen.desktop_mode_handle_menu_width) + pillElevation
+        R.dimen.desktop_mode_handle_menu_pill_spacing_margin
+    )
+    private val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_width)
     private val menuHeight = getHandleMenuHeight()
     private val marginMenuTop = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_margin_top)
     private val marginMenuStart = loadDimensionPixelSize(
-        R.dimen.desktop_mode_handle_menu_margin_start)
+        R.dimen.desktop_mode_handle_menu_margin_start
+    )
 
     @VisibleForTesting
     var handleMenuViewContainer: AdditionalViewContainer? = null
+
     @VisibleForTesting
     var handleMenuView: HandleMenuView? = null
 
@@ -140,7 +139,7 @@
 
     private val shouldShowMoreActionsPill: Boolean
         get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
-            shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
+                shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
 
     private var loadAppInfoJob: Job? = null
 
@@ -244,7 +243,8 @@
         val y = handleMenuPosition.y.toInt()
         handleMenuViewContainer =
             if ((!taskInfo.isFreeform && DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue())
-                    || forceShowSystemBars) {
+                || forceShowSystemBars
+            ) {
                 AdditionalSystemViewContainer(
                     windowManagerWrapper = windowManagerWrapper,
                     taskId = taskInfo.taskId,
@@ -255,7 +255,11 @@
                     flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                             WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                     view = handleMenuView.rootView,
-                    forciblyShownTypes = if (forceShowSystemBars) { systemBars() } else { 0 },
+                    forciblyShownTypes = if (forceShowSystemBars) {
+                        systemBars()
+                    } else {
+                        0
+                    },
                     ignoreCutouts = Flags.showAppHandleLargeScreens()
                             || BubbleAnythingFlagHelper.enableBubbleToFullscreen()
                 )
@@ -373,7 +377,8 @@
                 inputPoint.y - globalMenuPosition.y
             )
             if (splitScreenController.getSplitPosition(taskInfo.taskId)
-                == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+                == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+            ) {
                 val leftStageBounds = Rect()
                 splitScreenController.getStageBounds(leftStageBounds, Rect())
                 inputRelativeToMenu.x += leftStageBounds.width().toFloat()
@@ -399,11 +404,11 @@
      * Determines handle menu height based the max size and the visibility of pills.
      */
     private fun getHandleMenuHeight(): Int {
-        var menuHeight = loadDimensionPixelSize(
-            R.dimen.desktop_mode_handle_menu_height) + pillElevation
+        var menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_handle_menu_height)
         if (!shouldShowWindowingPill) {
             menuHeight -= loadDimensionPixelSize(
-                R.dimen.desktop_mode_handle_menu_windowing_pill_height)
+                R.dimen.desktop_mode_handle_menu_windowing_pill_height
+            )
             menuHeight -= pillTopMargin
         }
         if (!SHOULD_SHOW_SCREENSHOT_BUTTON) {
@@ -423,14 +428,16 @@
         }
         if (!shouldShowChangeAspectRatioButton) {
             menuHeight -= loadDimensionPixelSize(
-                R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height)
+                R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height
+            )
         }
         if (!shouldShowMoreActionsPill) {
             menuHeight -= pillTopMargin
         }
         if (!shouldShowBrowserPill) {
             menuHeight -= loadDimensionPixelSize(
-                R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height)
+                R.dimen.desktop_mode_handle_menu_open_in_browser_pill_height
+            )
             menuHeight -= pillTopMargin
         }
         return menuHeight
@@ -473,48 +480,66 @@
 
         // Insets for ripple effect of App Info Pill. and Windowing Pill. buttons
         val iconButtondrawableShiftInset = context.resources.getDimensionPixelSize(
-            R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_shift)
+            R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_shift
+        )
         val iconButtondrawableBaseInset = context.resources.getDimensionPixelSize(
-            R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_base)
+            R.dimen.desktop_mode_handle_menu_icon_button_ripple_inset_base
+        )
         private val iconButtonRippleRadius = context.resources.getDimensionPixelSize(
-            R.dimen.desktop_mode_handle_menu_icon_button_ripple_radius)
-        private val iconButtonDrawableInsetsBase = DrawableInsets(t=iconButtondrawableBaseInset,
-            b=iconButtondrawableBaseInset, l=iconButtondrawableBaseInset,
-            r=iconButtondrawableBaseInset)
-        private val iconButtonDrawableInsetsLeft = DrawableInsets(t=iconButtondrawableBaseInset,
-            b=iconButtondrawableBaseInset, l=iconButtondrawableShiftInset, r=0)
-        private val iconButtonDrawableInsetsRight = DrawableInsets(t=iconButtondrawableBaseInset,
-            b=iconButtondrawableBaseInset, l=0, r=iconButtondrawableShiftInset)
+            R.dimen.desktop_mode_handle_menu_icon_button_ripple_radius
+        )
+        private val iconButtonDrawableInsetsBase = DrawableInsets(
+            t = iconButtondrawableBaseInset,
+            b = iconButtondrawableBaseInset, l = iconButtondrawableBaseInset,
+            r = iconButtondrawableBaseInset
+        )
+        private val iconButtonDrawableInsetsLeft = DrawableInsets(
+            t = iconButtondrawableBaseInset,
+            b = iconButtondrawableBaseInset, l = iconButtondrawableShiftInset, r = 0
+        )
+        private val iconButtonDrawableInsetsRight = DrawableInsets(
+            t = iconButtondrawableBaseInset,
+            b = iconButtondrawableBaseInset, l = 0, r = iconButtondrawableShiftInset
+        )
 
         // App Info Pill.
         private val appInfoPill = rootView.requireViewById<View>(R.id.app_info_pill)
         private val collapseMenuButton = appInfoPill.requireViewById<HandleMenuImageButton>(
-            R.id.collapse_menu_button)
+            R.id.collapse_menu_button
+        )
+
         @VisibleForTesting
         val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
+
         @VisibleForTesting
         val appNameView = appInfoPill.requireViewById<MarqueedTextView>(R.id.application_name)
 
         // Windowing Pill.
         private val windowingPill = rootView.requireViewById<View>(R.id.windowing_pill)
         private val fullscreenBtn = windowingPill.requireViewById<ImageButton>(
-            R.id.fullscreen_button)
+            R.id.fullscreen_button
+        )
         private val splitscreenBtn = windowingPill.requireViewById<ImageButton>(
-            R.id.split_screen_button)
+            R.id.split_screen_button
+        )
         private val floatingBtn = windowingPill.requireViewById<ImageButton>(R.id.floating_button)
         private val floatingBtnSpace = windowingPill.requireViewById<Space>(
-            R.id.floating_button_space)
+            R.id.floating_button_space
+        )
 
         private val desktopBtn = windowingPill.requireViewById<ImageButton>(R.id.desktop_button)
         private val desktopBtnSpace = windowingPill.requireViewById<Space>(
-            R.id.desktop_button_space)
+            R.id.desktop_button_space
+        )
 
         // More Actions Pill.
         private val moreActionsPill = rootView.requireViewById<View>(R.id.more_actions_pill)
         private val screenshotBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
-            R.id.screenshot_button)
+            R.id.screenshot_button
+        )
         private val newWindowBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
-            R.id.new_window_button)
+            R.id.new_window_button
+        )
         private val manageWindowBtn = moreActionsPill
             .requireViewById<HandleMenuActionButton>(R.id.manage_windows_button)
         private val changeAspectRatioBtn = moreActionsPill
@@ -522,11 +547,14 @@
 
         // Open in Browser/App Pill.
         private val openInAppOrBrowserPill = rootView.requireViewById<View>(
-            R.id.open_in_app_or_browser_pill)
+            R.id.open_in_app_or_browser_pill
+        )
         private val openInAppOrBrowserBtn = openInAppOrBrowserPill.requireViewById<View>(
-            R.id.open_in_app_or_browser_button)
+            R.id.open_in_app_or_browser_button
+        )
         private val openByDefaultBtn = openInAppOrBrowserPill.requireViewById<ImageButton>(
-            R.id.open_by_default_button)
+            R.id.open_by_default_button
+        )
         private val decorThemeUtil = DecorThemeUtil(context)
         private val animator = HandleMenuAnimator(rootView, menuWidth, captionHeight.toFloat())
 
@@ -702,7 +730,7 @@
                 imageTintList = ColorStateList.valueOf(style.textColor)
                 this.taskInfo = this@HandleMenuView.taskInfo
 
-                background = createRippleDrawable(
+                background = createBackgroundDrawable(
                     color = style.textColor,
                     cornerRadius = iconButtonRippleRadius,
                     drawableInsets = iconButtonDrawableInsetsBase
@@ -735,12 +763,12 @@
             desktopBtn.imageTintList = style.windowingButtonColor
 
             val startInsets = if (context.isRtl) iconButtonDrawableInsetsRight
-                else iconButtonDrawableInsetsLeft
+            else iconButtonDrawableInsetsLeft
             val endInsets = if (context.isRtl) iconButtonDrawableInsetsLeft
-                else iconButtonDrawableInsetsRight
+            else iconButtonDrawableInsetsRight
 
             fullscreenBtn.apply {
-                background = createRippleDrawable(
+                background = createBackgroundDrawable(
                     color = style.textColor,
                     cornerRadius = iconButtonRippleRadius,
                     drawableInsets = startInsets
@@ -748,7 +776,7 @@
             }
 
             splitscreenBtn.apply {
-                background = createRippleDrawable(
+                background = createBackgroundDrawable(
                     color = style.textColor,
                     cornerRadius = iconButtonRippleRadius,
                     drawableInsets = iconButtonDrawableInsetsBase
@@ -756,7 +784,7 @@
             }
 
             floatingBtn.apply {
-                background = createRippleDrawable(
+                background = createBackgroundDrawable(
                     color = style.textColor,
                     cornerRadius = iconButtonRippleRadius,
                     drawableInsets = iconButtonDrawableInsetsBase
@@ -764,7 +792,7 @@
             }
 
             desktopBtn.apply {
-                background = createRippleDrawable(
+                background = createBackgroundDrawable(
                     color = style.textColor,
                     cornerRadius = iconButtonRippleRadius,
                     drawableInsets = endInsets
@@ -809,9 +837,11 @@
                 getString(R.string.open_in_browser_text)
             }
 
+            val buttonRoot = openInAppOrBrowserBtn.requireViewById<LinearLayout>(R.id.action_button)
             val label = openInAppOrBrowserBtn.requireViewById<MarqueedTextView>(R.id.label)
             val image = openInAppOrBrowserBtn.requireViewById<ImageView>(R.id.image)
             openInAppOrBrowserBtn.contentDescription = btnText
+            buttonRoot.contentDescription = btnText
             label.apply {
                 text = btnText
                 setTextColor(style.textColor)
@@ -842,7 +872,7 @@
          */
         fun shouldShowChangeAspectRatioButton(taskInfo: RunningTaskInfo): Boolean =
             taskInfo.appCompatTaskInfo.eligibleForUserAspectRatioButton() &&
-                taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+                    taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
     }
 }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
index 4b2e473..a723a7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
@@ -23,9 +23,7 @@
 import android.view.LayoutInflater
 import android.widget.ImageView
 import android.widget.LinearLayout
-import android.widget.TextView
 import androidx.core.content.withStyledAttributes
-import androidx.core.view.isGone
 import com.android.wm.shell.R
 
 /**
@@ -54,6 +52,7 @@
 
         context.withStyledAttributes(attrs, R.styleable.HandleMenuActionButton) {
             textView.text = getString(R.styleable.HandleMenuActionButton_android_text)
+            rootElement.contentDescription = getString(R.styleable.HandleMenuActionButton_android_text)
             textView.setTextColor(getColor(R.styleable.HandleMenuActionButton_android_textColor, 0))
             iconView.setImageResource(getResourceId(
                 R.styleable.HandleMenuActionButton_android_src, 0))
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 ad3525a..5d1a7a0 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
@@ -26,7 +26,7 @@
 import android.content.res.Resources
 import android.graphics.Paint
 import android.graphics.PixelFormat
-import android.graphics.PointF
+import android.graphics.Point
 import android.graphics.Rect
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.GradientDrawable
@@ -90,7 +90,7 @@
         private val displayController: DisplayController,
         private val taskInfo: RunningTaskInfo,
         private val decorWindowContext: Context,
-        private val positionSupplier: (Int, Int) -> PointF,
+        private val positionSupplier: (Int, Int) -> Point,
         private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
 ) {
     private var maximizeMenu: AdditionalViewHostViewContainer? = null
@@ -100,14 +100,14 @@
     private val cornerRadius = loadDimensionPixelSize(
             R.dimen.desktop_mode_maximize_menu_corner_radius
     ).toFloat()
-    private lateinit var menuPosition: PointF
+    private lateinit var menuPosition: Point
     private val menuPadding = loadDimensionPixelSize(R.dimen.desktop_mode_menu_padding)
 
     /** Position the menu relative to the caption's position. */
     fun positionMenu(t: Transaction) {
         menuPosition = positionSupplier(maximizeMenuView?.measureWidth() ?: 0,
                                         maximizeMenuView?.measureHeight() ?: 0)
-        t.setPosition(leash, menuPosition.x, menuPosition.y)
+        t.setPosition(leash, menuPosition.x.toFloat(), menuPosition.y.toFloat())
     }
 
     /** Creates and shows the maximize window. */
@@ -208,8 +208,8 @@
             val menuHeight = menuView.measureHeight()
             menuPosition = positionSupplier(menuWidth, menuHeight)
             val lp = WindowManager.LayoutParams(
-                    menuWidth.toInt(),
-                    menuHeight.toInt(),
+                    menuWidth,
+                    menuHeight,
                     WindowManager.LayoutParams.TYPE_APPLICATION,
                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                             or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
@@ -222,7 +222,7 @@
 
         // Bring menu to front when open
         t.setLayer(leash, TaskConstants.TASK_CHILD_LAYER_FLOATING_MENU)
-                .setPosition(leash, menuPosition.x, menuPosition.y)
+                .setPosition(leash, menuPosition.x.toFloat(), menuPosition.y.toFloat())
                 .setCornerRadius(leash, cornerRadius)
                 .show(leash)
         maximizeMenu =
@@ -1047,7 +1047,7 @@
         displayController: DisplayController,
         taskInfo: RunningTaskInfo,
         decorWindowContext: Context,
-        positionSupplier: (Int, Int) -> PointF,
+        positionSupplier: (Int, Int) -> Point,
         transactionSupplier: Supplier<Transaction>
     ): MaximizeMenu
 }
@@ -1060,7 +1060,7 @@
         displayController: DisplayController,
         taskInfo: RunningTaskInfo,
         decorWindowContext: Context,
-        positionSupplier: (Int, Int) -> PointF,
+        positionSupplier: (Int, Int) -> Point,
         transactionSupplier: Supplier<Transaction>
     ): MaximizeMenu {
         return MaximizeMenu(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
index 22bc978..cf536eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
@@ -42,8 +42,6 @@
             .setDuration(ANIMATION_DURATION.toLong())
             .apply {
                 val t = SurfaceControl.Transaction()
-                val cornerRadius = context.resources
-                    .getDimensionPixelSize(R.dimen.desktop_mode_dragged_task_radius).toFloat()
                 addUpdateListener {
                     setTaskPosition(mostRecentInput.x, mostRecentInput.y)
                     t.setScale(taskSurface, scale, scale)
@@ -57,6 +55,8 @@
 
     val taskId get() = taskInfo.taskId
     val position: PointF = PointF(0.0f, 0.0f)
+    val cornerRadius: Float = context.resources
+        .getDimensionPixelSize(R.dimen.desktop_mode_dragged_task_radius).toFloat()
 
     /**
      * Whether motion events from the drag gesture should affect the dragged surface or not. Used
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
index 1b0e0f70..eb324f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -294,6 +294,10 @@
         return Rect(repositionTaskBounds)
     }
 
+    override fun close() {
+        displayController.removeDisplayWindowListener(this)
+    }
+
     private fun resetVeilIfVisible() {
         if (isResizingOrAnimatingResize) {
             desktopWindowDecoration.hideResizeVeil()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
index 63b288d..06e5380 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskDragResizer.java
@@ -41,4 +41,10 @@
      */
     void removeDragEventListener(
             DragPositioningCallbackUtility.DragEventListener dragEventListener);
+
+    /**
+     * Releases any resources associated with this TaskDragResizer. This should be called when the
+     * associated window is closed.
+     */
+    void close();
 }
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 d2c79d7..7e941ec 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
@@ -205,6 +205,9 @@
         return new Rect(mRepositionTaskBounds);
     }
 
+    @Override
+    public void close() {}
+
     private boolean isResizing() {
         return (mCtrlType & CTRL_TYPE_TOP) != 0 || (mCtrlType & CTRL_TYPE_BOTTOM) != 0
                 || (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 1563259..5e4a0a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager;
 import android.view.SurfaceControl;
+import android.window.TransitionInfo;
 
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -83,12 +84,14 @@
      * @param taskSurface the surface of the task
      * @param startT      the start transaction to be applied before the transition
      * @param finishT     the finish transaction to restore states after the transition
+     * @param changeMode  the type of change to the task
      */
     void onTaskChanging(
             ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl taskSurface,
             SurfaceControl.Transaction startT,
-            SurfaceControl.Transaction finishT);
+            SurfaceControl.Transaction finishT,
+            @TransitionInfo.TransitionMode int changeMode);
 
     /**
      * Notifies that the given task is about to close to give the window decoration a chance to
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 7baef2b..91a899c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -361,6 +361,9 @@
         outResult.mRootView = rootView;
         final boolean fontScaleChanged = mWindowDecorConfig != null
                 && mWindowDecorConfig.fontScale != mTaskInfo.configuration.fontScale;
+        final boolean localeListChanged = mWindowDecorConfig != null
+                && !mWindowDecorConfig.getLocales()
+                    .equals(mTaskInfo.getConfiguration().getLocales());
         final int oldDensityDpi = mWindowDecorConfig != null
                 ? mWindowDecorConfig.densityDpi : DENSITY_DPI_UNDEFINED;
         final int oldNightMode =  mWindowDecorConfig != null
@@ -376,7 +379,8 @@
                 || oldLayoutResId != mLayoutResId
                 || oldNightMode != newNightMode
                 || mDecorWindowContext == null
-                || fontScaleChanged) {
+                || fontScaleChanged
+                || localeListChanged) {
             releaseViews(wct);
 
             if (!obtainDisplayOrRegisterListener()) {
@@ -695,6 +699,9 @@
     public void close() {
         Trace.beginSection("WindowDecoration#close");
         mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
+        if (mTaskDragResizer != null) {
+            mTaskDragResizer.close();
+        }
         final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
         releaseViews(wct);
         mTaskOrganizer.applyTransaction(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt
new file mode 100644
index 0000000..39ccf5b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.app.WindowConfiguration
+import android.content.Context
+import android.view.WindowManager
+import android.window.DesktopExperienceFlags.ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.desktopmode.DesktopWallpaperActivity.Companion.isWallpaperTask
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.splitscreen.SplitScreenController
+
+/**
+ * Resolves whether, given a task and its associated display that it is currently on, to show the
+ * app handle/header or not.
+ */
+class AppHandleAndHeaderVisibilityHelper (
+    private val context: Context,
+    private val displayController: DisplayController,
+    private val desktopModeCompatPolicy: DesktopModeCompatPolicy
+) {
+    var splitScreenController: SplitScreenController? = null
+
+    /**
+     * Returns, given a task's attribute and its display attribute, whether the app
+     * handle/header should show or not for this task.
+     */
+    fun shouldShowAppHandleOrHeader(taskInfo: ActivityManager.RunningTaskInfo): Boolean {
+        if (!ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY.isTrue) {
+            return allowedForTask(taskInfo)
+        }
+        return allowedForTask(taskInfo) && allowedForDisplay(taskInfo.displayId)
+    }
+
+    private fun allowedForTask(taskInfo: ActivityManager.RunningTaskInfo): Boolean {
+        // TODO (b/382023296): Remove once we no longer rely on
+        //  Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay
+        if (displayController.getDisplay(taskInfo.displayId) == null) {
+            // If DisplayController doesn't have it tracked, it could be a private/managed display.
+            return false
+        }
+        if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) return true
+        if (splitScreenController?.isTaskRootOrStageRoot(taskInfo.taskId) == true) {
+            return false
+        }
+
+        if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) {
+            return false
+        }
+
+        // TODO (b/382023296): Remove once we no longer rely on
+        //  Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay
+        val isOnLargeScreen =
+            displayController.getDisplay(taskInfo.displayId).minSizeDimensionDp >=
+                    WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
+        if (!DesktopModeStatus.canEnterDesktopMode(context)
+            && DesktopModeStatus.overridesShowAppHandle(context)
+            && !isOnLargeScreen
+        ) {
+            // Devices with multiple screens may enable the app handle but it should not show on
+            // small screens
+            return false
+        }
+        return DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)
+                && !isWallpaperTask(taskInfo)
+                && taskInfo.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED
+                && taskInfo.activityType == WindowConfiguration.ACTIVITY_TYPE_STANDARD
+                && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop
+    }
+
+    private fun allowedForDisplay(displayId: Int): Boolean {
+        // If DisplayController doesn't have it tracked, it could be a private/managed display.
+        val display = displayController.getDisplay(displayId)
+        if (display == null) return false
+
+        if (DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)) {
+            return true
+        }
+        // If on default display and on Large Screen (unfolded), show app handle
+        return DesktopModeStatus.overridesShowAppHandle(context)
+                && display.minSizeDimensionDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ButtonBackgroundDrawableUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ButtonBackgroundDrawableUtils.kt
index f44b15a..f08cfa9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ButtonBackgroundDrawableUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ButtonBackgroundDrawableUtils.kt
@@ -20,7 +20,6 @@
 import android.graphics.Color
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
-import android.graphics.drawable.RippleDrawable
 import android.graphics.drawable.ShapeDrawable
 import android.graphics.drawable.shapes.RoundRectShape
 
@@ -48,45 +47,6 @@
 }
 
 /**
- * Creates a RippleDrawable with specified color, corner radius, and insets.
- */
-fun createRippleDrawable(
-    @ColorInt color: Int,
-    cornerRadius: Int,
-    drawableInsets: DrawableInsets,
-): RippleDrawable {
-    return RippleDrawable(
-        ColorStateList(
-            arrayOf(
-                intArrayOf(android.R.attr.state_hovered),
-                intArrayOf(android.R.attr.state_pressed),
-                intArrayOf(),
-            ),
-            intArrayOf(
-                replaceColorAlpha(color, OPACITY_11),
-                replaceColorAlpha(color, OPACITY_15),
-                Color.TRANSPARENT,
-            )
-        ),
-        null /* content */,
-        LayerDrawable(arrayOf(
-            ShapeDrawable().apply {
-                shape = RoundRectShape(
-                    FloatArray(8) { cornerRadius.toFloat() },
-                    null /* inset */,
-                    null /* innerRadii */
-                )
-                paint.color = Color.WHITE
-            }
-        )).apply {
-            require(numberOfLayers == 1) { "Must only contain one layer" }
-            setLayerInset(0 /* index */,
-                drawableInsets.l, drawableInsets.t, drawableInsets.r, drawableInsets.b)
-        }
-    )
-}
-
-/**
  * Creates a background drawable with specified color, corner radius, and insets.
  */
 fun createBackgroundDrawable(
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
index 801048a..957898f 100644
--- 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
@@ -21,6 +21,7 @@
 import android.content.pm.ActivityInfo
 import android.content.pm.PackageManager
 import android.graphics.Bitmap
+import android.os.LocaleList
 import android.os.UserHandle
 import androidx.tracing.Trace
 import com.android.internal.annotations.VisibleForTesting
@@ -80,6 +81,13 @@
      */
     private val existingTasks = mutableSetOf<Int>()
 
+    /**
+     * A map of task -> localeList to keep track of the language of app name that's currently
+     * cached in |taskToResourceCache|.
+     */
+    @VisibleForTesting
+    val localeListOnCache = ConcurrentHashMap<Int, LocaleList>()
+
     init {
         shellInit.addInitCallback(this::onInit, this)
     }
@@ -99,11 +107,14 @@
     fun getName(taskInfo: RunningTaskInfo): CharSequence {
         checkWindowDecorExists(taskInfo)
         val cachedResources = taskToResourceCache[taskInfo.taskId]
-        if (cachedResources != null) {
+        val localeListActiveOnCacheTime = localeListOnCache[taskInfo.taskId]
+        if (cachedResources != null &&
+            taskInfo.getConfiguration().getLocales().equals(localeListActiveOnCacheTime)) {
             return cachedResources.appName
         }
         val resources = loadAppResources(taskInfo)
         taskToResourceCache[taskInfo.taskId] = resources
+        localeListOnCache[taskInfo.taskId] = taskInfo.getConfiguration().getLocales()
         return resources.appName
     }
 
@@ -117,6 +128,7 @@
         }
         val resources = loadAppResources(taskInfo)
         taskToResourceCache[taskInfo.taskId] = resources
+        localeListOnCache[taskInfo.taskId] = taskInfo.getConfiguration().getLocales()
         return resources.appIcon
     }
 
@@ -130,6 +142,7 @@
         }
         val resources = loadAppResources(taskInfo)
         taskToResourceCache[taskInfo.taskId] = resources
+        localeListOnCache[taskInfo.taskId] = taskInfo.getConfiguration().getLocales()
         return resources.veilIcon
     }
 
@@ -142,6 +155,7 @@
     fun onWindowDecorClosed(taskInfo: RunningTaskInfo) {
         existingTasks.remove(taskInfo.taskId)
         taskToResourceCache.remove(taskInfo.taskId)
+        localeListOnCache.remove(taskInfo.taskId)
     }
 
     private fun checkWindowDecorExists(taskInfo: RunningTaskInfo) {
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 c3d15df..854d9e1 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
@@ -17,6 +17,7 @@
 package com.android.wm.shell.windowdecor.tiling
 
 import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
 import android.content.Context
 import android.content.res.Configuration
 import android.content.res.Resources
@@ -28,9 +29,11 @@
 import android.view.SurfaceControl.Transaction
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_PIP
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
 import com.android.internal.annotations.VisibleForTesting
@@ -134,6 +137,7 @@
         // Observe drag resizing to break tiling if a task is drag resized.
         desktopModeWindowDecoration.addDragResizeListener(this)
         val callback = { initTilingForDisplayIfNeeded(taskInfo.configuration, isFirstTiledApp) }
+        updateDesktopRepository(taskInfo.taskId, snapPosition = position)
         if (isTiled) {
             val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
             toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentBounds, callback)
@@ -149,11 +153,21 @@
                     endBounds = destinationBounds,
                     callback,
                 )
+            } else {
+                callback.invoke()
             }
         }
         return isTiled
     }
 
+    private fun updateDesktopRepository(taskId: Int, snapPosition: SnapPosition) {
+        when (snapPosition) {
+            SnapPosition.LEFT -> desktopUserRepositories.current.addLeftTiledTask(displayId, taskId)
+            SnapPosition.RIGHT ->
+                desktopUserRepositories.current.addRightTiledTask(displayId, taskId)
+        }
+    }
+
     // If a task is already tiled on the same position, release this task, otherwise if the same
     // task is tiled on the opposite side, remove it from the opposite side so it's tiled correctly.
     private fun initTilingApps(
@@ -422,6 +436,8 @@
             change.taskInfo?.let {
                 if (it.isFullscreen || isMinimized(change.mode, info.type)) {
                     removeTaskIfTiled(it.taskId, /* taskVanished= */ false, it.isFullscreen)
+                } else if (isEnteringPip(change, info.type)) {
+                    removeTaskIfTiled(it.taskId, /* taskVanished= */ true, it.isFullscreen)
                 }
             }
         }
@@ -434,6 +450,27 @@
                 infoType == TRANSIT_OPEN))
     }
 
+    private fun isEnteringPip(change: Change, transitType: Int): Boolean {
+        if (change.taskInfo != null && change.taskInfo?.windowingMode == WINDOWING_MODE_PINNED) {
+            // - TRANSIT_PIP: type (from RootWindowContainer)
+            // - TRANSIT_OPEN (from apps that enter PiP instantly on opening, mostly from
+            // CTS/Flicker tests).
+            // - TRANSIT_TO_FRONT, though uncommon with triggering PiP, should semantically also
+            // be allowed to animate if the task in question is pinned already - see b/308054074.
+            // - TRANSIT_CHANGE: This can happen if the request to enter PIP happens when we are
+            // collecting for another transition, such as TRANSIT_CHANGE (display rotation).
+            if (
+                transitType == TRANSIT_PIP ||
+                    transitType == TRANSIT_OPEN ||
+                    transitType == TRANSIT_TO_FRONT ||
+                    transitType == TRANSIT_CHANGE
+            ) {
+                return true
+            }
+        }
+        return false
+    }
+
     class AppResizingHelper(
         val taskInfo: RunningTaskInfo,
         val desktopModeWindowDecoration: DesktopModeWindowDecoration,
@@ -550,22 +587,31 @@
         taskVanished: Boolean = false,
         shouldDelayUpdate: Boolean = false,
     ) {
+        val taskRepository = desktopUserRepositories.current
         if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
+            desktopUserRepositories.current.removeLeftTiledTask(displayId)
             removeTask(leftTaskResizingHelper, taskVanished, shouldDelayUpdate)
             leftTaskResizingHelper = null
-            rightTaskResizingHelper
-                ?.desktopModeWindowDecoration
-                ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
+            val taskId = rightTaskResizingHelper?.taskInfo?.taskId
+            if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+                rightTaskResizingHelper
+                    ?.desktopModeWindowDecoration
+                    ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
+            }
             tearDownTiling()
             return
         }
 
         if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
+            desktopUserRepositories.current.removeRightTiledTask(displayId)
             removeTask(rightTaskResizingHelper, taskVanished, shouldDelayUpdate)
             rightTaskResizingHelper = null
-            leftTaskResizingHelper
-                ?.desktopModeWindowDecoration
-                ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
+            val taskId = leftTaskResizingHelper?.taskInfo?.taskId
+            if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+                leftTaskResizingHelper
+                    ?.desktopModeWindowDecoration
+                    ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
+            }
             tearDownTiling()
         }
     }
@@ -600,7 +646,6 @@
 
     fun onOverviewAnimationStateChange(isRunning: Boolean) {
         if (!isTilingManagerInitialised) return
-
         if (isRunning) {
             desktopTilingDividerWindowManager?.hideDividerBar()
         } else if (allTiledTasksVisible()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index d9df899f..0985587 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -49,7 +49,7 @@
  * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
  * It hosts a simple handle bar from which to initiate a drag motion to enter desktop mode.
  */
-internal class AppHandleViewHolder(
+class AppHandleViewHolder(
     rootView: View,
     onCaptionTouchListener: View.OnTouchListener,
     onCaptionButtonClickListener: OnClickListener,
@@ -66,7 +66,7 @@
         val position: Point,
         val width: Int,
         val height: Int,
-        val isCaptionVisible: Boolean
+        val showInputLayer: Boolean
     ) : Data()
 
     private lateinit var taskInfo: RunningTaskInfo
@@ -101,7 +101,7 @@
     }
 
     override fun bindData(data: HandleData) {
-        bindData(data.taskInfo, data.position, data.width, data.height, data.isCaptionVisible)
+        bindData(data.taskInfo, data.position, data.width, data.height, data.showInputLayer)
     }
 
     private fun bindData(
@@ -109,14 +109,13 @@
         position: Point,
         width: Int,
         height: Int,
-        isCaptionVisible: Boolean
+        showInputLayer: Boolean
     ) {
         captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
         this.taskInfo = taskInfo
         // If handle is not in status bar region(i.e., bottom stage in vertical split),
         // do not create an input layer
-        if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
-        if (!isCaptionVisible) {
+        if (position.y >= SystemBarUtils.getStatusBarHeight(context) || !showInputLayer) {
             disposeStatusBarInputLayer()
             return
         }
@@ -276,4 +275,25 @@
     }
 
     override fun close() {}
+
+    /** Factory class for creating [AppHandleViewHolder] objects. */
+    class Factory {
+        /**
+         * Create a [AppHandleViewHolder] object to handle caption view and status bar
+         * input layer logic.
+         */
+        fun create(
+            rootView: View,
+            onCaptionTouchListener: View.OnTouchListener,
+            onCaptionButtonClickListener: OnClickListener,
+            windowManagerWrapper: WindowManagerWrapper,
+            handler: Handler,
+        ): AppHandleViewHolder = AppHandleViewHolder(
+            rootView,
+            onCaptionTouchListener,
+            onCaptionButtonClickListener,
+            windowManagerWrapper,
+            handler,
+        )
+    }
 }
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 c3f5b3f..30712b5 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
@@ -466,11 +466,7 @@
 
     override fun onHandleMenuOpened() {}
 
-    override fun onHandleMenuClosed() {
-        openMenuButton.post {
-            openMenuButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
-        }
-    }
+    override fun onHandleMenuClosed() {}
 
     fun onMaximizeWindowHoverExit() {
         maximizeButtonView.cancelHoverAnimation()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/UnminimizeAppFromTaskbar.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/UnminimizeAppFromTaskbar.kt
index 3e98e43..7d9f2bf 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/UnminimizeAppFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/UnminimizeAppFromTaskbar.kt
@@ -19,7 +19,7 @@
 import android.app.Instrumentation
 import android.tools.NavBar
 import android.tools.Rotation
-import android.tools.device.apphelpers.BrowserAppHelper
+import android.tools.device.apphelpers.GmailAppHelper
 import android.tools.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.traces.parsers.WindowManagerStateHelper
 import androidx.test.platform.app.InstrumentationRegistry
@@ -44,8 +44,8 @@
     private val wmHelper = WindowManagerStateHelper(instrumentation)
     private val device = UiDevice.getInstance(instrumentation)
     private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
-    private val browserHelper = BrowserAppHelper(instrumentation)
-    private val browserApp = DesktopModeAppHelper(browserHelper)
+    private val gmailHelper = GmailAppHelper(instrumentation)
+    private val gmailApp = DesktopModeAppHelper(gmailHelper)
 
     @Rule
     @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
@@ -59,20 +59,20 @@
         ChangeDisplayOrientationRule.setRotation(rotation)
         testApp.enterDesktopMode(wmHelper, device)
         tapl.showTaskbarIfHidden()
-        browserApp.launchViaIntent(wmHelper)
-        browserApp.minimizeDesktopApp(wmHelper, device)
+        gmailApp.launchViaIntent(wmHelper)
+        gmailApp.minimizeDesktopApp(wmHelper, device)
     }
 
     @Test
     open fun unminimizeApp() {
         tapl.launchedAppState.taskbar
-            .getAppIcon(browserHelper.appName)
-            .launch(browserHelper.packageName)
+            .getAppIcon(gmailHelper.appName)
+            .launch(gmailHelper.packageName)
     }
 
     @After
     fun teardown() {
         testApp.exit(wmHelper)
-        browserApp.exit(wmHelper)
+        gmailApp.exit(wmHelper)
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml
index 1de47df..e51447f 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml
@@ -50,6 +50,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
index 34d001c..7659ec9 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
@@ -50,6 +50,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
index 34d001c..7659ec9 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
@@ -50,6 +50,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
new file mode 100644
index 0000000..68f7ef0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManager.DisplayListener
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeoutOrNull
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * A TestRule to manage multiple simulated connected overlay displays.
+ */
+class SimulatedConnectedDisplayTestRule : TestRule {
+
+    private val context = InstrumentationRegistry.getInstrumentation().targetContext
+    private val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
+    private val displayManager = context.getSystemService(DisplayManager::class.java)
+    private val addedDisplays = mutableListOf<Int>()
+
+    override fun apply(base: Statement, description: Description): Statement =
+        object : Statement() {
+            override fun evaluate() {
+                try {
+                    base.evaluate()
+                } finally {
+                    teardown()
+                }
+            }
+        }
+
+    private fun teardown() {
+        cleanupTestDisplays()
+    }
+
+    /**
+     * Adds multiple overlay displays with specified dimensions. Any existing overlay displays
+     * will be removed before adding the new ones.
+     *
+     * @param displays A list of [Point] objects, where each [Point] represents the
+     *                 width and height of a simulated display.
+     * @return List of displayIds of added displays.
+     */
+    fun setupTestDisplays(displays: List<Point>): List<Int> = runBlocking {
+        // Cleanup any existing overlay displays.
+        cleanupTestDisplays()
+
+        if (displays.isEmpty()) {
+            Log.w(TAG, "setupTestDisplays called with an empty list. No displays created.")
+            return@runBlocking emptyList()
+        }
+
+        val displayAddedFlow: Flow<Int> = callbackFlow {
+            val listener = object : DisplayListener {
+                override fun onDisplayAdded(displayId: Int) {
+                    trySend(displayId)
+                }
+
+                override fun onDisplayRemoved(displayId: Int) {}
+                override fun onDisplayChanged(displayId: Int) {}
+            }
+
+            val handler = Handler(Looper.getMainLooper())
+            displayManager.registerDisplayListener(listener, handler)
+
+            awaitClose {
+                displayManager.unregisterDisplayListener(listener)
+            }
+        }
+
+        val displaySettings = displays.joinToString(separator = ";") { size ->
+            "${size.x}x${size.y}/$DEFAULT_DENSITY"
+        }
+
+        // Add the overlay displays
+        Settings.Global.putString(
+            InstrumentationRegistry.getInstrumentation().context.contentResolver,
+            Settings.Global.OVERLAY_DISPLAY_DEVICES, displaySettings
+        )
+        withTimeoutOrNull(TIMEOUT) {
+            displayAddedFlow.take(displays.size).collect { displayId ->
+                addedDisplays.add(displayId)
+            }
+        } ?: error("Timed out waiting for displays to be added.")
+        addedDisplays
+    }
+
+    /**
+     * Adds multiple overlay displays with default dimensions. Any existing overlay displays
+     * will be removed before adding the new ones.
+     *
+     * @param count number of displays to add.
+     * @return List of displayIds of added displays.
+     */
+    fun setupTestDisplays(count: Int): List<Int> {
+        val displays = List(count) { Point(DEFAULT_WIDTH, DEFAULT_HEIGHT) }
+        return setupTestDisplays(displays)
+    }
+
+    private fun cleanupTestDisplays() = runBlocking {
+        if (addedDisplays.isEmpty()) {
+            return@runBlocking
+        }
+
+        val displayRemovedFlow: Flow<Int> = callbackFlow {
+            val listener = object : DisplayListener {
+                override fun onDisplayAdded(displayId: Int) {}
+                override fun onDisplayRemoved(displayId: Int) {
+                    trySend(displayId)
+                }
+
+                override fun onDisplayChanged(displayId: Int) {}
+            }
+            val handler = Handler(Looper.getMainLooper())
+            displayManager.registerDisplayListener(listener, handler)
+
+            awaitClose {
+                displayManager.unregisterDisplayListener(listener)
+            }
+        }
+
+        // Remove overlay displays
+        Settings.Global.putString(
+            InstrumentationRegistry.getInstrumentation().context.contentResolver,
+            Settings.Global.OVERLAY_DISPLAY_DEVICES, null)
+
+        withTimeoutOrNull(TIMEOUT) {
+            displayRemovedFlow.take(addedDisplays.size).collect { displayId ->
+                addedDisplays.remove(displayId)
+            }
+        } ?: error("Timed out waiting for displays to be removed.")
+    }
+
+    private companion object {
+        const val DEFAULT_WIDTH = 1280
+        const val DEFAULT_HEIGHT = 720
+        const val DEFAULT_DENSITY = 160
+        const val TAG = "SimulatedConnectedDisplayTestRule"
+        val TIMEOUT = 10.seconds
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
index 9c1a8f1..a4ecac9 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
@@ -50,6 +50,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
index ae73dae..75ffdc6 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
@@ -50,6 +50,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
index a136936..8003cba 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
@@ -50,6 +50,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 05750a5..b139c00 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -889,8 +889,8 @@
      */
     private void doStartEvents(int startX, int moveX) {
         doMotionEvent(MotionEvent.ACTION_DOWN, startX);
-        mController.onThresholdCrossed();
         doMotionEvent(MotionEvent.ACTION_MOVE, moveX);
+        mController.onThresholdCrossed();
     }
 
     private void simulateRemoteAnimationStart() throws RemoteException {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 2ef6c55..43bcc3b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -58,8 +58,7 @@
                 /* frameTime = */ 0,
                 /* progress = */ progress,
                 /* triggerBack = */ false,
-                /* swipeEdge = */ BackEvent.EDGE_LEFT,
-                /* departingAnimationTarget = */ null);
+                /* swipeEdge = */ BackEvent.EDGE_LEFT);
     }
 
     @Before
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
index 2cc52c5..9d4cc49 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -224,8 +224,7 @@
             /* frameTime = */ 0,
             /* progress = */ progress,
             /* triggerBack = */ false,
-            /* swipeEdge = */ BackEvent.EDGE_LEFT,
-            /* departingAnimationTarget = */ null
+            /* swipeEdge = */ BackEvent.EDGE_LEFT
         )
 
     private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 7a7d88b..b3d2db6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -28,7 +28,6 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
@@ -804,7 +803,7 @@
         mBubbleData.setListener(mListener);
 
         changeExpandedStateAtTime(true, 2000L);
-        verifyZeroInteractions(mListener);
+        verifyNoMoreInteractions(mListener);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
index 3c79ea7..2de77b2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -16,6 +16,7 @@
 package com.android.wm.shell.bubbles;
 
 import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
 
 import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
 
@@ -29,6 +30,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -36,6 +38,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -46,12 +49,15 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import androidx.core.animation.AnimatorTestRule;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestSyncExecutor;
+import com.android.wm.shell.bubbles.BubbleTransitions.DraggedBubbleIconToFullscreen;
 import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
 import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
 import com.android.wm.shell.common.ShellExecutor;
@@ -63,6 +69,7 @@
 import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -74,6 +81,8 @@
 @SmallTest
 public class BubbleTransitionsTest extends ShellTestCase {
 
+    @Rule public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
+
     private static final int FULLSCREEN_TASK_WIDTH = 200;
     private static final int FULLSCREEN_TASK_HEIGHT = 100;
 
@@ -136,6 +145,7 @@
         when(tvtc.getTaskInfo()).thenReturn(taskInfo);
         when(tv.getController()).thenReturn(tvtc);
         when(mBubble.getTaskView()).thenReturn(tv);
+        when(tv.getTaskInfo()).thenReturn(taskInfo);
         mRepository.add(tvtc);
         return taskInfo;
     }
@@ -221,8 +231,8 @@
 
         PointF dragPosition = new PointF(10f, 20f);
         BubbleTransitions.DragData dragData = new BubbleTransitions.DragData(
-                /* releasedOnLeft= */ false, /* taskScale= */ 0.5f, dragPosition,
-                pendingWct);
+                /* releasedOnLeft= */ false, /* taskScale= */ 0.5f, /* cornerRadius= */ 10f,
+                dragPosition, pendingWct);
 
         final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertToBubble(
                 mBubble, taskInfo, mExpandedViewManager, mTaskViewFactory, mBubblePositioner,
@@ -253,6 +263,8 @@
         verify(startT).setPosition(snapshot, dragPosition.x, dragPosition.y);
         // Snapshot has the scale of the dragged task
         verify(startT).setScale(snapshot, dragData.getTaskScale(), dragData.getTaskScale());
+        // Snapshot has dragged task corner radius
+        verify(startT).setCornerRadius(snapshot, dragData.getCornerRadius());
     }
 
     @Test
@@ -283,4 +295,75 @@
         // directly tested.
         assertFalse(mTaskViewTransitions.hasPending());
     }
+
+    @Test
+    public void convertDraggedBubbleToFullscreen() {
+        ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+        SurfaceControl.Transaction animT = mock(SurfaceControl.Transaction.class);
+        BubbleTransitions.TransactionProvider transactionProvider = () -> animT;
+        final DraggedBubbleIconToFullscreen bt =
+                mBubbleTransitions.new DraggedBubbleIconToFullscreen(
+                        mBubble, new Point(100, 50), transactionProvider);
+        verify(mTransitions).startTransition(anyInt(), any(), eq(bt));
+
+        SurfaceControl taskLeash = new SurfaceControl.Builder().setName("taskLeash").build();
+        final TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
+        final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token, taskLeash);
+        chg.setMode(TRANSIT_TO_FRONT);
+        chg.setTaskInfo(taskInfo);
+        info.addChange(chg);
+        info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+        SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+        SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+        boolean[] transitionFinished = {false};
+        Transitions.TransitionFinishCallback finishCb = wct -> transitionFinished[0] = true;
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            bt.startAnimation(bt.mTransition, info, startT, finishT, finishCb);
+            mAnimatorTestRule.advanceTimeBy(250);
+        });
+        verify(startT).setScale(taskLeash, 0, 0);
+        verify(startT).setPosition(taskLeash, 100, 50);
+        verify(startT).apply();
+        verify(animT).setScale(taskLeash, 1, 1);
+        verify(animT).setPosition(taskLeash, 0, 0);
+        verify(animT, atLeastOnce()).apply();
+        verify(animT).close();
+        assertFalse(mTaskViewTransitions.hasPending());
+        assertTrue(transitionFinished[0]);
+    }
+
+    @Test
+    public void convertFloatingBubbleToFullscreen() {
+        final BubbleExpandedView bev = mock(BubbleExpandedView.class);
+        final ViewRootImpl vri = mock(ViewRootImpl.class);
+        when(bev.getViewRootImpl()).thenReturn(vri);
+        when(mBubble.getBubbleBarExpandedView()).thenReturn(null);
+        when(mBubble.getExpandedView()).thenReturn(bev);
+
+        ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+        final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertFromBubble(
+                mBubble, taskInfo);
+        final BubbleTransitions.ConvertFromBubble cfb = (BubbleTransitions.ConvertFromBubble) bt;
+        verify(mTransitions).startTransition(anyInt(), any(), eq(cfb));
+        verify(mBubble).setPreparingTransition(eq(bt));
+        assertTrue(mTaskViewTransitions.hasPending());
+
+        final TransitionInfo info = new TransitionInfo(TRANSIT_CHANGE, 0);
+        final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
+                mock(SurfaceControl.class));
+        chg.setMode(TRANSIT_CHANGE);
+        chg.setTaskInfo(taskInfo);
+        info.addChange(chg);
+        info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+        SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+        SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+        Transitions.TransitionFinishCallback finishCb = wct -> {};
+        cfb.startAnimation(cfb.mTransition, info, startT, finishT, finishCb);
+
+        // Can really only verify that it interfaces with the taskViewTransitions queue.
+        // The actual functioning of this is tightly-coupled with SurfaceFlinger and renderthread
+        // in order to properly synchronize surface manipulation with drawing and thus can't be
+        // directly tested.
+        assertFalse(mTaskViewTransitions.hasPending());
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
index c4b9c9b..b4b9679 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
@@ -25,7 +25,6 @@
 import static org.mockito.ArgumentMatchers.floatThat;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.os.SystemClock;
 import android.testing.AndroidTestingRunner;
@@ -84,7 +83,7 @@
         verify(mMotionEventListener).onMove(0, -600);
         // Check that velocity up is about 5000
         verify(mMotionEventListener).onUp(eq(0f), floatThat(f -> Math.round(f) == -5000));
-        verifyZeroInteractions(mMotionEventListener);
+        verifyNoMoreInteractions(mMotionEventListener);
         verify(mInterceptTouchRunnable).run();
     }
 
@@ -94,8 +93,8 @@
         mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 100));
         mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 100));
 
-        verifyZeroInteractions(mMotionEventListener);
-        verifyZeroInteractions(mInterceptTouchRunnable);
+        verifyNoMoreInteractions(mMotionEventListener);
+        verifyNoMoreInteractions(mInterceptTouchRunnable);
     }
 
     @Test
@@ -107,7 +106,7 @@
         verify(mMotionEventListener).onDown(0, 990);
         verify(mMotionEventListener).onMove(100, 0);
         verify(mMotionEventListener).onUp(0, 0);
-        verifyZeroInteractions(mMotionEventListener);
+        verifyNoMoreInteractions(mMotionEventListener);
         verify(mInterceptTouchRunnable).run();
     }
 
@@ -119,7 +118,7 @@
 
         verify(mMotionEventListener).onDown(0, 990);
         verifyNoMoreInteractions(mMotionEventListener);
-        verifyZeroInteractions(mInterceptTouchRunnable);
+        verifyNoMoreInteractions(mInterceptTouchRunnable);
     }
 
     @Test
@@ -129,7 +128,7 @@
         verify(mMotionEventListener).onDown(0, 990);
         verify(mMotionEventListener).onCancel();
         verifyNoMoreInteractions(mMotionEventListener);
-        verifyZeroInteractions(mInterceptTouchRunnable);
+        verifyNoMoreInteractions(mInterceptTouchRunnable);
     }
 
     private MotionEvent newEvent(int actionDown, float x, float y) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
index 3323740..1472464 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
@@ -22,7 +22,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.content.Context;
 
@@ -105,6 +105,6 @@
         int sameDevicePosture = mDevicePostureCaptor.getValue();
         mDevicePostureController.onDevicePostureChanged(sameDevicePosture);
 
-        verifyZeroInteractions(mOnDevicePostureChangedListener);
+        verifyNoMoreInteractions(mOnDevicePostureChangedListener);
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index ee9d177..a53277a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -32,7 +32,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.graphics.Insets;
 import android.graphics.Point;
@@ -108,26 +108,26 @@
     public void insetsControlChanged_schedulesNoWorkOnExecutor() {
         Looper.prepare();
         mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
-        verifyZeroInteractions(mExecutor);
+        verifyNoMoreInteractions(mExecutor);
     }
 
     @Test
     public void insetsChanged_schedulesNoWorkOnExecutor() {
         Looper.prepare();
         mPerDisplay.insetsChanged(insetsStateWithIme(false));
-        verifyZeroInteractions(mExecutor);
+        verifyNoMoreInteractions(mExecutor);
     }
 
     @Test
     public void showInsets_schedulesNoWorkOnExecutor() {
         mPerDisplay.showInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
-        verifyZeroInteractions(mExecutor);
+        verifyNoMoreInteractions(mExecutor);
     }
 
     @Test
     public void hideInsets_schedulesNoWorkOnExecutor() {
         mPerDisplay.hideInsets(ime(), true /* fromIme */, ImeTracker.Token.empty());
-        verifyZeroInteractions(mExecutor);
+        verifyNoMoreInteractions(mExecutor);
     }
 
     // With the refactor, the control's isInitiallyVisible is used to apply to the IME, therefore
@@ -135,7 +135,7 @@
     @Test
     @RequiresFlagsDisabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void reappliesVisibilityToChangedLeash() {
-        verifyZeroInteractions(mT);
+        verifyNoMoreInteractions(mT);
         mPerDisplay.mImeShowing = false;
 
         mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
index 96d202c..7d18669 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TabletopModeControllerTest.java
@@ -29,7 +29,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -145,7 +145,7 @@
         mConfiguration.windowConfiguration.setDisplayRotation(Surface.ROTATION_0);
         mPipTabletopController.onDisplayConfigurationChanged(DEFAULT_DISPLAY, mConfiguration);
 
-        verifyZeroInteractions(mOnTabletopModeChangedListener);
+        verifyNoMoreInteractions(mOnTabletopModeChangedListener);
     }
 
     // Test cases starting from folded state (DEVICE_POSTURE_CLOSED)
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 e92e243..a2066db 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
@@ -21,7 +21,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
@@ -157,7 +157,7 @@
         opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE),
                 packageName);
 
-        verifyZeroInteractions(mMockExecutor);
+        verifyNoMoreInteractions(mMockExecutor);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java
index 01b76ed..1066276 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipBoundsStateTest.java
@@ -19,6 +19,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -128,6 +130,31 @@
     }
 
     @Test
+    public void setLastPipComponentName_notChanged_doesNotCallbackComponentChangedListener() {
+        mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+        PipBoundsState.OnPipComponentChangedListener mockListener =
+                mock(PipBoundsState.OnPipComponentChangedListener.class);
+
+        mPipBoundsState.addOnPipComponentChangedListener(mockListener);
+        mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+
+        verify(mockListener, never()).onPipComponentChanged(any(), any());
+    }
+
+    @Test
+    public void setLastPipComponentName_changed_callbackComponentChangedListener() {
+        mPipBoundsState.setLastPipComponentName(mTestComponentName1);
+        PipBoundsState.OnPipComponentChangedListener mockListener =
+                mock(PipBoundsState.OnPipComponentChangedListener.class);
+
+        mPipBoundsState.addOnPipComponentChangedListener(mockListener);
+        mPipBoundsState.setLastPipComponentName(mTestComponentName2);
+
+        verify(mockListener).onPipComponentChanged(
+                eq(mTestComponentName1), eq(mTestComponentName2));
+    }
+
+    @Test
     public void testSetLastPipComponentName_notChanged_doesNotClearReentryState() {
         mPipBoundsState.setLastPipComponentName(mTestComponentName1);
         mPipBoundsState.saveReentryState(DEFAULT_SNAP_FRACTION);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java
index 4cdb1e5..25dbc64 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java
@@ -22,6 +22,7 @@
 
 import static com.android.window.flags.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_PIP;
 import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP;
+import static com.android.wm.shell.Flags.FLAG_ENABLE_PIP2;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -119,12 +120,28 @@
     }
 
     @Test
-    @EnableFlags(FLAG_ENABLE_CONNECTED_DISPLAYS_PIP)
+    @EnableFlags({
+            FLAG_ENABLE_CONNECTED_DISPLAYS_PIP, FLAG_ENABLE_PIP2
+    })
     public void isConnectedDisplaysPipEnabled_returnsTrue() {
         assertTrue(mPipDesktopState.isConnectedDisplaysPipEnabled());
     }
 
     @Test
+    public void isPipInDesktopMode_anyDeskActive_returnsTrue() {
+        when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true);
+
+        assertTrue(mPipDesktopState.isPipInDesktopMode());
+    }
+
+    @Test
+    public void isPipInDesktopMode_noDeskActive_returnsFalse() {
+        when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(false);
+
+        assertFalse(mPipDesktopState.isPipInDesktopMode());
+    }
+
+    @Test
     public void getOutPipWindowingMode_exitToDesktop_displayFreeform_returnsUndefined() {
         when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true);
         setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
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 598a101..597e4a5 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
@@ -59,6 +59,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.compatui.api.CompatUIInfo;
+import com.android.wm.shell.compatui.impl.CompatUIRequests;
 import com.android.wm.shell.desktopmode.DesktopRepository;
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
 import com.android.wm.shell.sysui.ShellController;
@@ -738,6 +739,22 @@
         verify(mController, never()).removeLayouts(taskInfo.taskId);
     }
 
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
+    public void testSendCompatUIRequest_createRestartDialog() {
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ false);
+        doReturn(true).when(mMockRestartDialogLayout)
+                .needsToBeRecreated(any(TaskInfo.class),
+                        any(ShellTaskOrganizer.TaskListener.class));
+        doReturn(true).when(mCompatUIConfiguration).isRestartDialogEnabled();
+        doReturn(true).when(mCompatUIConfiguration).shouldShowRestartDialogAgain(eq(taskInfo));
+
+        mController.sendCompatUIRequest(new CompatUIRequests.DisplayCompatShowRestartDialog(
+                taskInfo, mMockTaskListener));
+        verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
+    }
+
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat) {
         return createTaskInfo(displayId, taskId, hasSizeCompat, /* isVisible */ false,
                 /* isFocused */ false, /* isTopActivityTransparent */ false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxInputControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxInputControllerTest.kt
index fa95fae..9c45cd2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxInputControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxInputControllerTest.kt
@@ -35,6 +35,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
@@ -191,6 +192,7 @@
         fun checkUpdateSessionRegion(times: Int = 1, displayId: Int = DISPLAY_ID, region: Region) {
             verify(windowSession, times(times)).updateInputChannel(
                 any(),
+                anyOrNull(),
                 eq(displayId),
                 any(),
                 any(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/crashhandling/ShellCrashHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/crashhandling/ShellCrashHandlerTest.kt
new file mode 100644
index 0000000..5c77f78
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/crashhandling/ShellCrashHandlerTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.crashhandling
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.PendingIntent
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.platform.test.annotations.DisableFlags
+import android.view.Display.DEFAULT_DISPLAY
+import android.window.IWindowContainerToken
+import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.HomeIntentProvider
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellInit
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.Before
+import org.junit.Rule
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+class ShellCrashHandlerTest : ShellTestCase() {
+    @JvmField
+    @Rule
+    val extendedMockitoRule =
+        ExtendedMockitoRule.Builder(this)
+            .mockStatic(DesktopModeStatus::class.java)
+            .mockStatic(PendingIntent::class.java)
+            .build()!!
+
+    private val testExecutor = mock<ShellExecutor>()
+    private val context = mock<Context>()
+    private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
+
+    private lateinit var homeIntentProvider: HomeIntentProvider
+    private lateinit var crashHandler: ShellCrashHandler
+    private lateinit var shellInit: ShellInit
+
+
+    @Before
+    fun setup() {
+        whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
+        whenever(PendingIntent.getActivity(any(), any(), any(), any(), any())).thenReturn(mock())
+
+        shellInit = spy(ShellInit(testExecutor))
+
+        homeIntentProvider = HomeIntentProvider(context)
+        crashHandler = ShellCrashHandler(context, shellTaskOrganizer, homeIntentProvider, shellInit)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun init_freeformTaskExists_sendsHomeIntent() {
+        val wctCaptor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(createTaskInfo(1)))
+
+        shellInit.init()
+
+        verify(shellTaskOrganizer).applyTransaction(
+            wctCaptor.capture()
+        )
+        wctCaptor.value.assertPendingIntentAt(0, launchHomeIntent(DEFAULT_DISPLAY))
+    }
+
+    private fun launchHomeIntent(displayId: Int): Intent {
+        return Intent(Intent.ACTION_MAIN).apply {
+            if (displayId != DEFAULT_DISPLAY) {
+                addCategory(Intent.CATEGORY_SECONDARY_HOME)
+            } else {
+                addCategory(Intent.CATEGORY_HOME)
+            }
+        }
+    }
+
+    private fun createTaskInfo(id: Int, windowingMode: Int = WINDOWING_MODE_FREEFORM) =
+        RunningTaskInfo().apply {
+            taskId = id
+            displayId = DEFAULT_DISPLAY
+            configuration.windowConfiguration.windowingMode = windowingMode
+            token = WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java))
+            baseIntent = Intent().apply { component = ComponentName("package", "component.name") }
+        }
+
+    private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent: Intent) {
+        val op = hierarchyOps[index]
+        assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+        assertThat(op.activityIntent?.component).isEqualTo(intent.component)
+        assertThat(op.activityIntent?.categories).isEqualTo(intent.categories)
+    }
+}
\ No newline at end of file
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 8ad54f5..2aebcdc 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
@@ -28,14 +28,22 @@
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
 import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.whenever
@@ -46,6 +54,7 @@
  *
  * Usage: atest WMShellUnitTests:DesktopDisplayEventHandlerTest
  */
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class DesktopDisplayEventHandlerTest : ShellTestCase() {
@@ -55,6 +64,8 @@
     @Mock private lateinit var mockDesktopRepository: DesktopRepository
     @Mock private lateinit var mockDesktopTasksController: DesktopTasksController
     @Mock private lateinit var desktopDisplayModeController: DesktopDisplayModeController
+    private val desktopRepositoryInitializer = FakeDesktopRepositoryInitializer()
+    private val testScope = TestScope()
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var shellInit: ShellInit
@@ -77,7 +88,9 @@
             DesktopDisplayEventHandler(
                 context,
                 shellInit,
+                testScope.backgroundScope,
                 displayController,
+                desktopRepositoryInitializer,
                 mockDesktopUserRepositories,
                 mockDesktopTasksController,
                 desktopDisplayModeController,
@@ -89,17 +102,58 @@
 
     @After
     fun tearDown() {
+        testScope.cancel()
         mockitoSession.finishMocking()
     }
 
     @Test
-    fun testDisplayAdded_supportsDesks_createsDesk() {
-        whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+    fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_createsDesk() =
+        testScope.runTest {
+            whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
 
-        onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+            onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+            desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+            runCurrent()
 
-        verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
-    }
+            verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
+        }
+
+    @Test
+    fun testDisplayAdded_supportsDesks_desktopRepositoryNotInitialized_doesNotCreateDesk() =
+        testScope.runTest {
+            whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+
+            onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+            runCurrent()
+
+            verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+        }
+
+    @Test
+    fun testDisplayAdded_supportsDesks_desktopRepositoryInitializedTwice_createsDeskOnce() =
+        testScope.runTest {
+            whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+
+            onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+            desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+            desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+            runCurrent()
+
+            verify(mockDesktopTasksController, times(1)).createDesk(DEFAULT_DISPLAY)
+        }
+
+    @Test
+    fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_deskExists_doesNotCreateDesk() =
+        testScope.runTest {
+            whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+            whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(1)
+
+            onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+            desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+            runCurrent()
+
+            verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+        }
 
     @Test
     fun testDisplayAdded_cannotEnterDesktopMode_doesNotCreateDesk() {
@@ -141,4 +195,15 @@
         onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
         verify(desktopDisplayModeController).refreshDisplayWindowingMode()
     }
+
+    private class FakeDesktopRepositoryInitializer : DesktopRepositoryInitializer {
+        override var deskRecreationFactory: DesktopRepositoryInitializer.DeskRecreationFactory =
+            DesktopRepositoryInitializer.DeskRecreationFactory { _, _, deskId -> deskId }
+
+        override val isInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+        override fun initialize(userRepositories: DesktopUserRepositories) {
+            isInitialized.value = true
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index cc37c44..96b826f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -21,29 +21,42 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
 import android.content.ContentResolver
+import android.hardware.input.InputManager
 import android.os.Binder
+import android.os.Handler
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.UsesFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import android.provider.Settings
 import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.IWindowManager
+import android.view.InputDevice
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.window.DisplayAreaInfo
 import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.server.display.feature.flags.Flags as DisplayFlags
 import com.android.window.flags.Flags
 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.TestRunningTaskInfoBuilder
+import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.transition.Transitions
 import com.google.common.truth.Truth.assertThat
 import com.google.testing.junit.testparameterinjector.TestParameter
 import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import com.google.testing.junit.testparameterinjector.TestParameterValuesProvider
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -56,6 +69,7 @@
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
 
 /**
  * Test class for [DesktopDisplayModeController]
@@ -64,13 +78,20 @@
  */
 @SmallTest
 @RunWith(TestParameterInjector::class)
-class DesktopDisplayModeControllerTest : ShellTestCase() {
+@UsesFlags(com.android.server.display.feature.flags.Flags::class)
+class DesktopDisplayModeControllerTest(
+    @TestParameter(valuesProvider = FlagsParameterizationProvider::class)
+    flags: FlagsParameterization
+) : ShellTestCase() {
     private val transitions = mock<Transitions>()
     private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
     private val mockWindowManager = mock<IWindowManager>()
     private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
     private val desktopWallpaperActivityTokenProvider =
         mock<DesktopWallpaperActivityTokenProvider>()
+    private val inputManager = mock<InputManager>()
+    private val displayController = mock<DisplayController>()
+    private val mainHandler = mock<Handler>()
 
     private lateinit var controller: DesktopDisplayModeController
 
@@ -81,9 +102,28 @@
         TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
     private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
     private val wallpaperToken = MockToken().token()
+    private val defaultDisplay = mock<Display>()
+    private val externalDisplay = mock<Display>()
+    private val mouseDevice = mock<InputDevice>()
+
+    private lateinit var extendedDisplaySettingsRestoreSession:
+        ExtendedDisplaySettingsRestoreSession
+
+    private lateinit var mockitoSession: StaticMockitoSession
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
 
     @Before
     fun setUp() {
+        mockitoSession =
+            mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .mockStatic(DesktopModeStatus::class.java)
+                .startMocking()
+        extendedDisplaySettingsRestoreSession =
+            ExtendedDisplaySettingsRestoreSession(context.contentResolver)
         whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder())
         whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
             .thenReturn(defaultTDA)
@@ -95,74 +135,129 @@
                 mockWindowManager,
                 shellTaskOrganizer,
                 desktopWallpaperActivityTokenProvider,
+                inputManager,
+                displayController,
+                mainHandler,
             )
         runningTasks.add(freeformTask)
         runningTasks.add(fullscreenTask)
         whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks))
         whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
+        whenever(displayController.getDisplay(DEFAULT_DISPLAY)).thenReturn(defaultDisplay)
+        whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay)
+        setTabletModeStatus(SwitchState.UNKNOWN)
+        whenever(
+            DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+                context,
+                defaultDisplay
+            )
+        ).thenReturn(true)
+        whenever(mouseDevice.supportsSource(InputDevice.SOURCE_MOUSE)).thenReturn(true)
+        whenever(inputManager.getInputDevice(EXTERNAL_DEVICE_ID)).thenReturn(mouseDevice)
+        setMouseConnected(false)
     }
 
-    private fun testDisplayWindowingModeSwitch(
-        defaultWindowingMode: Int,
-        extendedDisplayEnabled: Boolean,
-        expectToSwitch: Boolean,
-    ) {
-        defaultTDA.configuration.windowConfiguration.windowingMode = defaultWindowingMode
-        whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(defaultWindowingMode)
-        val settingsSession =
-            ExtendedDisplaySettingsSession(
-                context.contentResolver,
-                if (extendedDisplayEnabled) 1 else 0,
-            )
+    @After
+    fun tearDown() {
+        extendedDisplaySettingsRestoreSession.restore()
+        mockitoSession.finishMocking()
+    }
 
-        settingsSession.use {
-            connectExternalDisplay()
-            if (expectToSwitch) {
-                // Assumes [connectExternalDisplay] properly triggered the switching transition.
-                // Will verify the transition later along with [disconnectExternalDisplay].
-                defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
-            }
-            disconnectExternalDisplay()
+    private fun testDisplayWindowingModeSwitchOnDisplayConnected(expectToSwitch: Boolean) {
+        defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+        whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
+        setExtendedMode(true)
 
-            if (expectToSwitch) {
-                val arg = argumentCaptor<WindowContainerTransaction>()
-                verify(transitions, times(2))
-                    .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
-                assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
-                    .isEqualTo(WINDOWING_MODE_FREEFORM)
-                assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
-                    .isEqualTo(WINDOWING_MODE_FULLSCREEN)
-                assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
-                    .isEqualTo(defaultWindowingMode)
-                assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
-                    .isEqualTo(WINDOWING_MODE_FULLSCREEN)
-            } else {
-                verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
-            }
+        connectExternalDisplay()
+        if (expectToSwitch) {
+            // Assumes [connectExternalDisplay] properly triggered the switching transition.
+            // Will verify the transition later along with [disconnectExternalDisplay].
+            defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+        }
+        disconnectExternalDisplay()
+
+        if (expectToSwitch) {
+            val arg = argumentCaptor<WindowContainerTransaction>()
+            verify(transitions, times(2))
+                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+            assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FREEFORM)
+            assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+            assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+            assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+        } else {
+            verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
         }
     }
 
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
-    fun displayWindowingModeSwitchOnDisplayConnected_flagDisabled(
-        @TestParameter param: ModeSwitchTestCase
-    ) {
-        testDisplayWindowingModeSwitch(
-            param.defaultWindowingMode,
-            param.extendedDisplayEnabled,
-            // When the flag is disabled, never switch.
-            expectToSwitch = false,
-        )
+    fun displayWindowingModeSwitchOnDisplayConnected_flagDisabled() {
+        // When the flag is disabled, never switch.
+        testDisplayWindowingModeSwitchOnDisplayConnected(/* expectToSwitch= */ false)
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
-    fun displayWindowingModeSwitchOnDisplayConnected(@TestParameter param: ModeSwitchTestCase) {
-        testDisplayWindowingModeSwitch(
-            param.defaultWindowingMode,
-            param.extendedDisplayEnabled,
-            param.expectToSwitchByDefault,
-        )
+    fun displayWindowingModeSwitchOnDisplayConnected() {
+        testDisplayWindowingModeSwitchOnDisplayConnected(/* expectToSwitch= */ true)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
+    @DisableFlags(Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH)
+    fun testTargetWindowingMode_formfactorDisabled(
+        @TestParameter param: ExternalDisplayBasedTargetModeTestCase,
+        @TestParameter tabletModeStatus: SwitchState,
+        @TestParameter hasAnyMouseDevice: Boolean,
+    ) {
+        whenever(mockWindowManager.getWindowingMode(anyInt()))
+            .thenReturn(param.defaultWindowingMode)
+        if (param.hasExternalDisplay) {
+            connectExternalDisplay()
+        } else {
+            disconnectExternalDisplay()
+        }
+        setTabletModeStatus(tabletModeStatus)
+        setMouseConnected(hasAnyMouseDevice)
+        setExtendedMode(param.extendedDisplayEnabled)
+        whenever(
+            DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+                context,
+                defaultDisplay
+            )
+        ).thenReturn(param.isDefaultDisplayDesktopEligible)
+
+        assertThat(controller.getTargetWindowingModeForDefaultDisplay())
+            .isEqualTo(param.expectedWindowingMode)
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING,
+        Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH,
+    )
+    fun testTargetWindowingMode(@TestParameter param: FormFactorBasedTargetModeTestCase) {
+        if (param.hasExternalDisplay) {
+            connectExternalDisplay()
+        } else {
+            disconnectExternalDisplay()
+        }
+        setTabletModeStatus(param.tabletModeStatus)
+        setExtendedMode(param.extendedDisplayEnabled)
+        whenever(
+            DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+                context,
+                defaultDisplay
+            )
+        ).thenReturn(param.isDefaultDisplayDesktopEligible)
+        setMouseConnected(param.hasAnyMouseDevice)
+
+        assertThat(controller.getTargetWindowingModeForDefaultDisplay())
+            .isEqualTo(param.expectedWindowingMode)
     }
 
     @Test
@@ -170,18 +265,16 @@
     fun displayWindowingModeSwitch_existingTasksOnConnected() {
         defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
         whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
+        setExtendedMode(true)
 
-        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
-            connectExternalDisplay()
+        connectExternalDisplay()
 
-            val arg = argumentCaptor<WindowContainerTransaction>()
-            verify(transitions, times(1))
-                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
-            assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_UNDEFINED)
-            assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
-        }
+        val arg = argumentCaptor<WindowContainerTransaction>()
+        verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+        assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED)
+        assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
     }
 
     @Test
@@ -191,18 +284,16 @@
         whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
             WINDOWING_MODE_FULLSCREEN
         }
+        setExtendedMode(true)
 
-        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
-            disconnectExternalDisplay()
+        disconnectExternalDisplay()
 
-            val arg = argumentCaptor<WindowContainerTransaction>()
-            verify(transitions, times(1))
-                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
-            assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_FREEFORM)
-            assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_UNDEFINED)
-        }
+        val arg = argumentCaptor<WindowContainerTransaction>()
+        verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+        assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+        assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED)
     }
 
     private fun connectExternalDisplay() {
@@ -217,49 +308,576 @@
         controller.refreshDisplayWindowingMode()
     }
 
-    private class ExtendedDisplaySettingsSession(
-        private val contentResolver: ContentResolver,
-        private val overrideValue: Int,
-    ) : AutoCloseable {
+    private fun setTabletModeStatus(status: SwitchState) {
+        whenever(inputManager.isInTabletMode()).thenReturn(status.value)
+    }
+
+    private fun setExtendedMode(enabled: Boolean) {
+        if (DisplayFlags.enableDisplayContentModeManagement()) {
+            whenever(
+                DesktopModeStatus.isDesktopModeSupportedOnDisplay(
+                    context,
+                    externalDisplay
+                )
+            ).thenReturn(enabled)
+        } else {
+            Settings.Global.putInt(
+                context.contentResolver,
+                DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
+                if (enabled) 1 else 0,
+            )
+        }
+    }
+
+    private fun setMouseConnected(connected: Boolean) {
+        whenever(inputManager.inputDeviceIds)
+            .thenReturn(if (connected) intArrayOf(EXTERNAL_DEVICE_ID) else intArrayOf())
+    }
+
+    private class ExtendedDisplaySettingsRestoreSession(
+        private val contentResolver: ContentResolver
+    ) {
         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() {
+        fun restore() {
             Settings.Global.putInt(contentResolver, settingName, initialValue)
         }
     }
 
+    private class FlagsParameterizationProvider : TestParameterValuesProvider() {
+        override fun provideValues(
+            context: TestParameterValuesProvider.Context
+        ): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(
+                Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH,
+                DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT,
+            )
+        }
+    }
+
     companion object {
         const val EXTERNAL_DISPLAY_ID = 100
+        const val EXTERNAL_DEVICE_ID = 10
 
-        enum class ModeSwitchTestCase(
+        enum class SwitchState(val value: Int) {
+            UNKNOWN(InputManager.SWITCH_STATE_UNKNOWN),
+            ON(InputManager.SWITCH_STATE_ON),
+            OFF(InputManager.SWITCH_STATE_OFF),
+        }
+
+        enum class ExternalDisplayBasedTargetModeTestCase(
             val defaultWindowingMode: Int,
+            val hasExternalDisplay: Boolean,
             val extendedDisplayEnabled: Boolean,
-            val expectToSwitchByDefault: Boolean,
+            val isDefaultDisplayDesktopEligible: Boolean,
+            val expectedWindowingMode: Int,
         ) {
-            FULLSCREEN_DISPLAY(
-                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
-                extendedDisplayEnabled = true,
-                expectToSwitchByDefault = true,
-            ),
-            FULLSCREEN_DISPLAY_MIRRORING(
-                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
-                extendedDisplayEnabled = false,
-                expectToSwitchByDefault = false,
-            ),
-            FREEFORM_DISPLAY(
+            FREEFORM_EXTERNAL_EXTENDED_NO_PROJECTED(
                 defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = true,
                 extendedDisplayEnabled = true,
-                expectToSwitchByDefault = false,
+                isDefaultDisplayDesktopEligible = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
             ),
-            FREEFORM_DISPLAY_MIRRORING(
+            FULLSCREEN_EXTERNAL_EXTENDED_NO_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FREEFORM_NO_EXTERNAL_EXTENDED_NO_PROJECTED(
                 defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_NO_EXTERNAL_EXTENDED_NO_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            FREEFORM_EXTERNAL_MIRROR_NO_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = true,
                 extendedDisplayEnabled = false,
-                expectToSwitchByDefault = false,
+                isDefaultDisplayDesktopEligible = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_EXTERNAL_MIRROR_NO_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            FREEFORM_NO_EXTERNAL_MIRROR_NO_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_NO_EXTERNAL_MIRROR_NO_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            FREEFORM_EXTERNAL_EXTENDED_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_EXTERNAL_EXTENDED_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            FREEFORM_NO_EXTERNAL_EXTENDED_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_NO_EXTERNAL_EXTENDED_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            FREEFORM_EXTERNAL_MIRROR_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_EXTERNAL_MIRROR_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            FREEFORM_NO_EXTERNAL_MIRROR_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            FULLSCREEN_NO_EXTERNAL_MIRROR_PROJECTED(
+                defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                isDefaultDisplayDesktopEligible = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+        }
+
+        enum class FormFactorBasedTargetModeTestCase(
+            val hasExternalDisplay: Boolean,
+            val extendedDisplayEnabled: Boolean,
+            val tabletModeStatus: SwitchState,
+            val isDefaultDisplayDesktopEligible: Boolean,
+            val hasAnyMouseDevice: Boolean,
+            val expectedWindowingMode: Int,
+        ) {
+            EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_TABLET_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_EXTENDED_TABLET_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_EXTENDED_TABLET_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_TABLET_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_TABLET_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_UNKNOWN_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED_NO_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = false,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            EXTERNAL_MIRROR_TABLET_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = true,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+            ),
+            EXTERNAL_EXTENDED_TABLET_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_EXTENDED_TABLET_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_TABLET_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_TABLET_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.ON,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.OFF,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = true,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            EXTERNAL_MIRROR_UNKNOWN_PROJECTED_MOUSE(
+                hasExternalDisplay = true,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+            ),
+            NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED_MOUSE(
+                hasExternalDisplay = false,
+                extendedDisplayEnabled = false,
+                tabletModeStatus = SwitchState.UNKNOWN,
+                isDefaultDisplayDesktopEligible = false,
+                hasAnyMouseDevice = true,
+                expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
             ),
         }
     }
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 006c3ca..4c18ee1 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
@@ -114,6 +114,8 @@
                 transactionSupplier = transactionSupplier,
             )
         desktopRepository = userRepositories.current
+        desktopRepository.addDesk(DEFAULT_DISPLAY, DEFAULT_DESK_ID)
+        desktopRepository.setActiveDesk(DEFAULT_DISPLAY, DEFAULT_DESK_ID)
     }
 
     @Test
@@ -835,5 +837,6 @@
     companion object {
         private val STABLE_BOUNDS = Rect(0, 100, 2000, 1900)
         private val DISPLAY_BOUNDS = Rect(0, 0, 2000, 2000)
+        private const val DEFAULT_DESK_ID = 0
     }
 }
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 e9f92cf..0c585b3 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
@@ -431,6 +431,38 @@
     }
 
     @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX,
+        Flags.FLAG_ENABLE_DESKTOP_OPENING_DEEPLINK_MINIMIZE_ANIMATION_BUGFIX,
+    )
+    fun startAndAnimateLaunchTransition_withMinimizeChange_wrongTaskId_reparentsMinimizeChange() {
+        val wct = WindowContainerTransaction()
+        val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val launchTaskChange = createChange(launchingTask, mode = TRANSIT_OPEN)
+        val minimizeChange = createChange(minimizingTask)
+        val transition = Binder()
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(transition)
+
+        mixedHandler.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            taskId = Int.MAX_VALUE,
+            minimizingTaskId = minimizingTask.taskId,
+        )
+        mixedHandler.startAnimation(
+            transition,
+            createCloseTransitionInfo(TRANSIT_OPEN, listOf(launchTaskChange, minimizeChange)),
+            SurfaceControl.Transaction(),
+            SurfaceControl.Transaction(),
+        ) {}
+
+        verify(rootTaskDisplayAreaOrganizer)
+            .reparentToDisplayArea(anyInt(), eq(minimizeChange.leash), any())
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
     fun startAnimation_pendingTransition_noLaunchChange_returnsFalse() {
         val wct = WindowContainerTransaction()
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 8a5acfa..695cf60 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
@@ -23,7 +23,7 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
 import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker
 import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
-import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
 import com.android.window.flags.Flags
@@ -102,7 +102,7 @@
                 eq(sessionId),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -127,7 +127,7 @@
                 eq(sessionId),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -135,7 +135,7 @@
         desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -157,7 +157,7 @@
                 eq(sessionId),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
         assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID)
     }
 
@@ -166,7 +166,7 @@
         desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -205,7 +205,7 @@
                 eq(UNSET_FOCUS_REASON),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -213,7 +213,7 @@
         desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -252,7 +252,7 @@
                 eq(UNSET_FOCUS_REASON),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -260,7 +260,7 @@
         desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -302,7 +302,7 @@
                 eq(UNSET_FOCUS_REASON),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -346,7 +346,7 @@
                 eq(UNSET_FOCUS_REASON),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -390,7 +390,7 @@
                 eq(UNSET_FOCUS_REASON),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -434,7 +434,7 @@
                 eq(FocusReason.UNKNOWN.reason),
             )
         }
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -446,7 +446,7 @@
         )
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
@@ -485,7 +485,7 @@
         )
 
         verifyNoLogging()
-        verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+        verifyNoMoreInteractions(staticMockMarker(EventLogTags::class.java))
     }
 
     @Test
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 b7d25b5..bd37610 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
@@ -83,7 +83,7 @@
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 /**
@@ -596,7 +596,7 @@
             .logTaskRemoved(
                 eq(DEFAULT_TASK_UPDATE.copy(minimizeReason = MinimizeReason.MINIMIZE_BUTTON))
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     @Test
@@ -668,7 +668,7 @@
             .logTaskInfoChanged(
                 eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     @Test
@@ -701,7 +701,7 @@
                     )
                 )
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     @Test
@@ -729,7 +729,7 @@
                     )
                 )
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     @Test
@@ -753,7 +753,7 @@
             .logTaskInfoChanged(
                 eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
 
         // task 2 resize
         val newTaskInfo2 =
@@ -781,7 +781,7 @@
                     )
                 )
             )
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     @Test
@@ -892,14 +892,14 @@
                 eq(taskUpdate.visibleTaskCount.toString()),
             )
         }
-        verifyZeroInteractions(desktopModeEventLogger)
+        verifyNoMoreInteractions(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)
+        verifyNoMoreInteractions(desktopModeEventLogger)
     }
 
     private companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandlerTest.kt
index fbc9406..a7ea66e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandlerTest.kt
@@ -16,8 +16,10 @@
 
 package com.android.wm.shell.desktopmode
 
+import android.graphics.Rect
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
 import android.view.WindowManager
 import android.window.TransitionInfo
 import androidx.test.filters.SmallTest
@@ -30,6 +32,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
 
 @SmallTest
 @RunWithLooper
@@ -39,7 +43,8 @@
 
     @Before
     fun setUp() {
-        handler = DesktopModeMoveToDisplayTransitionHandler(StubTransaction())
+        handler =
+            DesktopModeMoveToDisplayTransitionHandler(StubTransaction(), mock(), mock(), mock())
     }
 
     @Test
@@ -55,7 +60,9 @@
                 info =
                     TransitionInfo(WindowManager.TRANSIT_CHANGE, /* flags= */ 0).apply {
                         addChange(
-                            TransitionInfo.Change(mock(), mock()).apply { setDisplayId(1, 1) }
+                            TransitionInfo.Change(mock(), mock()).apply {
+                                setDisplayId(/* start= */ 1, /* end= */ 1)
+                            }
                         )
                     },
                 startTransaction = StubTransaction(),
@@ -74,7 +81,9 @@
                 info =
                     TransitionInfo(WindowManager.TRANSIT_CHANGE, /* flags= */ 0).apply {
                         addChange(
-                            TransitionInfo.Change(mock(), mock()).apply { setDisplayId(1, 2) }
+                            TransitionInfo.Change(mock(), mock()).apply {
+                                setDisplayId(/* start= */ 1, /* end= */ 2)
+                            }
                         )
                     },
                 startTransaction = StubTransaction(),
@@ -84,4 +93,77 @@
 
         assertTrue("Should animate display change transition", animates)
     }
+
+    @Test
+    fun startAnimation_movingActivityEmbedding_shouldSetCorrectBounds() {
+        val leashLeft = mock<SurfaceControl>()
+        val leashRight = mock<SurfaceControl>()
+        val leashContainer = mock<SurfaceControl>()
+        val startTransaction = spy(StubTransaction())
+
+        handler.startAnimation(
+            transition = mock(),
+            info =
+                TransitionInfo(WindowManager.TRANSIT_CHANGE, /* flags= */ 0).apply {
+                    addChange(
+                        TransitionInfo.Change(mock(), mock()).apply {
+                            flags = TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY
+                            leash = leashLeft
+                            setDisplayId(/* start= */ 1, /* end= */ 2)
+                            setEndAbsBounds(
+                                Rect(
+                                    /* left= */ 100,
+                                    /* top= */ 100,
+                                    /* right= */ 500,
+                                    /* bottom= */ 700,
+                                )
+                            )
+                            setEndRelOffset(/* left= */ 0, /* top= */ 0)
+                        }
+                    )
+                    addChange(
+                        TransitionInfo.Change(mock(), mock()).apply {
+                            flags = TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY
+                            leash = leashRight
+                            setDisplayId(1, 2)
+                            setEndAbsBounds(
+                                Rect(
+                                    /* left= */ 500,
+                                    /* top= */ 100,
+                                    /* right= */ 900,
+                                    /* bottom= */ 700,
+                                )
+                            )
+                            setEndRelOffset(/* left= */ 400, /* top= */ 0)
+                        }
+                    )
+                    addChange(
+                        TransitionInfo.Change(mock(), mock()).apply {
+                            flags = TransitionInfo.FLAG_TRANSLUCENT
+                            leash = leashContainer
+                            setDisplayId(/* start= */ 1, /* end= */ 2)
+                            setEndAbsBounds(
+                                Rect(
+                                    /* left= */ 100,
+                                    /* top= */ 100,
+                                    /* right= */ 900,
+                                    /* bottom= */ 700,
+                                )
+                            )
+                            setEndRelOffset(/* left= */ 100, /* top= */ 100)
+                        }
+                    )
+                },
+            startTransaction = startTransaction,
+            finishTransaction = StubTransaction(),
+            finishCallback = mock(),
+        )
+
+        verify(startTransaction).setPosition(leashLeft, 0f, 0f)
+        verify(startTransaction).setPosition(leashRight, 400f, 0f)
+        verify(startTransaction).setPosition(leashContainer, 100f, 100f)
+        verify(startTransaction).setWindowCrop(leashLeft, 400, 600)
+        verify(startTransaction).setWindowCrop(leashRight, 400, 600)
+        verify(startTransaction).setWindowCrop(leashContainer, 800, 600)
+    }
 }
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 fe1dc29..652fae0 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
@@ -24,7 +24,6 @@
 import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
-import android.view.Display
 import android.view.SurfaceControl
 import androidx.test.filters.SmallTest
 import com.android.internal.policy.SystemBarUtils
@@ -37,6 +36,7 @@
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
 import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.shared.R as sharedR
 import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider
 import com.android.wm.shell.windowdecor.tiling.SnapEventHandler
 import com.google.common.truth.Truth.assertThat
@@ -67,7 +67,6 @@
     private lateinit var taskInfo: RunningTaskInfo
     @Mock private lateinit var syncQueue: SyncTransactionQueue
     @Mock private lateinit var displayController: DisplayController
-    @Mock private lateinit var display: Display
     @Mock private lateinit var taskSurface: SurfaceControl
     @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
     @Mock private lateinit var displayLayout: DisplayLayout
@@ -80,15 +79,45 @@
 
     @Before
     fun setUp() {
-        whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
-        whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
-        whenever(displayLayout.stableInsets()).thenReturn(STABLE_INSETS)
-        whenever(displayController.getDisplay(anyInt())).thenReturn(display)
+        setUpDisplayBoundsTablet()
         whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
         whenever(displayController.getDisplay(anyInt())).thenReturn(mContext.display)
         whenever(bubbleBoundsProvider.getBubbleBarExpandedViewDropTargetBounds(any()))
             .thenReturn(Rect())
         taskInfo = DesktopTestHelpers.createFullscreenTask()
+
+        mContext.orCreateTestableResources.addOverride(
+            com.android.internal.R.bool.config_isDesktopModeSupported,
+            true,
+        )
+        mContext.orCreateTestableResources.addOverride(
+            com.android.internal.R.bool.config_canInternalDisplayHostDesktops,
+            true,
+        )
+    }
+
+    private fun setUpDisplayBoundsTablet() {
+        whenever(displayLayout.width()).thenReturn(TABLET_DISPLAY_BOUNDS.width())
+        whenever(displayLayout.height()).thenReturn(TABLET_DISPLAY_BOUNDS.height())
+        whenever(displayLayout.stableInsets()).thenReturn(TABLET_STABLE_INSETS)
+    }
+
+    private fun setUpDisplayBoundsFoldable() {
+        whenever(displayLayout.width()).thenReturn(FOLDABLE_DISPLAY_BOUNDS.width())
+        whenever(displayLayout.height()).thenReturn(FOLDABLE_DISPLAY_BOUNDS.height())
+        whenever(displayLayout.stableInsets()).thenReturn(FOLDABLE_STABLE_INSETS)
+    }
+
+    private fun disableDesktop() {
+        mContext.orCreateTestableResources.addOverride(
+            com.android.internal.R.bool.config_canInternalDisplayHostDesktops,
+            false,
+        )
+    }
+
+    private fun setUpFoldable() {
+        setUpDisplayBoundsFoldable()
+        disableDesktop()
     }
 
     @Test
@@ -96,7 +125,7 @@
         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))
+            .isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400, 2 * TABLET_STABLE_INSETS.top))
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
         testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
@@ -107,9 +136,9 @@
         assertThat(testRegion.bounds)
             .isEqualTo(
                 Rect(
-                    (DISPLAY_BOUNDS.width() / 2f - toFullscreenWidth / 2f).toInt(),
+                    (TABLET_DISPLAY_BOUNDS.width() / 2f - toFullscreenWidth / 2f).toInt(),
                     Short.MIN_VALUE.toInt(),
-                    (DISPLAY_BOUNDS.width() / 2f + toFullscreenWidth / 2f).toInt(),
+                    (TABLET_DISPLAY_BOUNDS.width() / 2f + toFullscreenWidth / 2f).toInt(),
                     transitionHeight,
                 )
             )
@@ -117,7 +146,7 @@
         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))
+            .isEqualTo(Rect(0, Short.MIN_VALUE.toInt(), 2400, 2 * TABLET_STABLE_INSETS.top))
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
         testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
@@ -136,7 +165,7 @@
                 TRANSITION_AREA_WIDTH,
                 CAPTION_HEIGHT,
             )
-        assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
+        assertThat(testRegion).isEqualTo(Rect(0, -50, 32, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
         testRegion =
             visualIndicator.calculateSplitLeftRegion(
@@ -144,7 +173,7 @@
                 TRANSITION_AREA_WIDTH,
                 CAPTION_HEIGHT,
             )
-        assertThat(testRegion.bounds).isEqualTo(Rect(0, transitionHeight, 32, 1600))
+        assertThat(testRegion).isEqualTo(Rect(0, transitionHeight, 32, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
         testRegion =
             visualIndicator.calculateSplitLeftRegion(
@@ -152,7 +181,7 @@
                 TRANSITION_AREA_WIDTH,
                 CAPTION_HEIGHT,
             )
-        assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
+        assertThat(testRegion).isEqualTo(Rect(0, -50, 32, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
         testRegion =
             visualIndicator.calculateSplitLeftRegion(
@@ -160,7 +189,7 @@
                 TRANSITION_AREA_WIDTH,
                 CAPTION_HEIGHT,
             )
-        assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
+        assertThat(testRegion).isEqualTo(Rect(0, -50, 32, 1600))
     }
 
     @Test
@@ -174,7 +203,7 @@
                 TRANSITION_AREA_WIDTH,
                 CAPTION_HEIGHT,
             )
-        assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
+        assertThat(testRegion).isEqualTo(Rect(2368, -50, 2400, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
         testRegion =
             visualIndicator.calculateSplitRightRegion(
@@ -182,7 +211,7 @@
                 TRANSITION_AREA_WIDTH,
                 CAPTION_HEIGHT,
             )
-        assertThat(testRegion.bounds).isEqualTo(Rect(2368, transitionHeight, 2400, 1600))
+        assertThat(testRegion).isEqualTo(Rect(2368, transitionHeight, 2400, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
         testRegion =
             visualIndicator.calculateSplitRightRegion(
@@ -190,7 +219,7 @@
                 TRANSITION_AREA_WIDTH,
                 CAPTION_HEIGHT,
             )
-        assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
+        assertThat(testRegion).isEqualTo(Rect(2368, -50, 2400, 1600))
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
         testRegion =
             visualIndicator.calculateSplitRightRegion(
@@ -198,41 +227,37 @@
                 TRANSITION_AREA_WIDTH,
                 CAPTION_HEIGHT,
             )
-        assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
+        assertThat(testRegion).isEqualTo(Rect(2368, -50, 2400, 1600))
     }
 
     @Test
     fun testBubbleLeftRegionCalculation() {
-        val bubbleRegionWidth =
-            context.resources.getDimensionPixelSize(R.dimen.bubble_transform_area_width)
-        val bubbleRegionHeight =
-            context.resources.getDimensionPixelSize(R.dimen.bubble_transform_area_height)
-        val expectedRect = Rect(0, 1600 - bubbleRegionHeight, bubbleRegionWidth, 1600)
+        val bubbleRegionSize =
+            context.resources.getDimensionPixelSize(sharedR.dimen.drag_zone_bubble_tablet)
+        val expectedRect = Rect(0, 1600 - bubbleRegionSize, bubbleRegionSize, 1600)
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
         var testRegion = visualIndicator.calculateBubbleLeftRegion(displayLayout)
-        assertThat(testRegion.bounds).isEqualTo(expectedRect)
+        assertThat(testRegion).isEqualTo(expectedRect)
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
         testRegion = visualIndicator.calculateBubbleLeftRegion(displayLayout)
-        assertThat(testRegion.bounds).isEqualTo(expectedRect)
+        assertThat(testRegion).isEqualTo(expectedRect)
     }
 
     @Test
     fun testBubbleRightRegionCalculation() {
-        val bubbleRegionWidth =
-            context.resources.getDimensionPixelSize(R.dimen.bubble_transform_area_width)
-        val bubbleRegionHeight =
-            context.resources.getDimensionPixelSize(R.dimen.bubble_transform_area_height)
-        val expectedRect = Rect(2400 - bubbleRegionWidth, 1600 - bubbleRegionHeight, 2400, 1600)
+        val bubbleRegionSize =
+            context.resources.getDimensionPixelSize(sharedR.dimen.drag_zone_bubble_tablet)
+        val expectedRect = Rect(2400 - bubbleRegionSize, 1600 - bubbleRegionSize, 2400, 1600)
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
         var testRegion = visualIndicator.calculateBubbleRightRegion(displayLayout)
-        assertThat(testRegion.bounds).isEqualTo(expectedRect)
+        assertThat(testRegion).isEqualTo(expectedRect)
 
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
         testRegion = visualIndicator.calculateBubbleRightRegion(displayLayout)
-        assertThat(testRegion.bounds).isEqualTo(expectedRect)
+        assertThat(testRegion).isEqualTo(expectedRect)
     }
 
     @Test
@@ -258,52 +283,107 @@
         com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
         com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
     )
-    fun testDefaultIndicatorWithNoDesktop() {
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.bool.config_isDesktopModeSupported,
-            false,
-        )
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.bool.config_isDesktopModeDevOptionSupported,
-            false,
-        )
-
-        // Fullscreen to center, no desktop indicator
+    fun testDefaultIndicators_bubblesEnabled() {
         createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
-        var result = visualIndicator.updateIndicatorType(PointF(500f, 500f))
-        assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
-        // Fullscreen to split
-        result = visualIndicator.updateIndicatorType(PointF(10000f, 500f))
-        assertThat(result)
-            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
-        result = visualIndicator.updateIndicatorType(PointF(-10000f, 500f))
-        assertThat(result)
-            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
-        // Fullscreen to bubble
-        result = visualIndicator.updateIndicatorType(PointF(100f, 1500f))
+        var result = visualIndicator.updateIndicatorType(PointF(10f, 1500f))
         assertThat(result)
             .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_LEFT_INDICATOR)
         result = visualIndicator.updateIndicatorType(PointF(2300f, 1500f))
         assertThat(result)
             .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR)
-        // Split to center, no desktop indicator
-        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
-        result = visualIndicator.updateIndicatorType(PointF(500f, 500f))
-        assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
-        // Split to fullscreen
-        result = visualIndicator.updateIndicatorType(PointF(500f, 0f))
+    }
+
+    @Test
+    @EnableFlags(
+        com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
+        com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
+    )
+    fun testDefaultIndicators_foldable_leftRightSplit() {
+        setUpFoldable()
+
+        createVisualIndicator(
+            DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN,
+            isSmallTablet = true,
+            isLeftRightSplit = true,
+        )
+        var result = visualIndicator.updateIndicatorType(foldCenter())
         assertThat(result)
             .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
-        // Split to bubble
-        result = visualIndicator.updateIndicatorType(PointF(100f, 1500f))
+
+        result = visualIndicator.updateIndicatorType(foldLeftEdge())
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
+
+        result = visualIndicator.updateIndicatorType(foldRightEdge())
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
+
+        result = visualIndicator.updateIndicatorType(foldLeftBottom())
         assertThat(result)
             .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_LEFT_INDICATOR)
-        result = visualIndicator.updateIndicatorType(PointF(2300f, 1500f))
+
+        result = visualIndicator.updateIndicatorType(foldRightBottom())
         assertThat(result)
             .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR)
-        // Drag app to center, no desktop indicator
-        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
-        result = visualIndicator.updateIndicatorType(PointF(500f, 500f))
+
+        createVisualIndicator(
+            DesktopModeVisualIndicator.DragStartState.FROM_SPLIT,
+            isSmallTablet = true,
+            isLeftRightSplit = true,
+        )
+        result = visualIndicator.updateIndicatorType(foldCenter())
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+        result = visualIndicator.updateIndicatorType(foldLeftBottom())
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_LEFT_INDICATOR)
+
+        result = visualIndicator.updateIndicatorType(foldRightBottom())
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR)
+    }
+
+    @Test
+    @EnableFlags(
+        com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
+        com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
+    )
+    fun testDefaultIndicators_foldable_topBottomSplit() {
+        setUpFoldable()
+
+        createVisualIndicator(
+            DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN,
+            isSmallTablet = true,
+            isLeftRightSplit = false,
+        )
+        var result = visualIndicator.updateIndicatorType(foldCenter())
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+        result = visualIndicator.updateIndicatorType(foldLeftEdge())
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+        result = visualIndicator.updateIndicatorType(foldLeftBottom())
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_LEFT_INDICATOR)
+
+        result = visualIndicator.updateIndicatorType(foldRightBottom())
+        assertThat(result)
+            .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR)
+
+        createVisualIndicator(
+            DesktopModeVisualIndicator.DragStartState.FROM_SPLIT,
+            isSmallTablet = true,
+            isLeftRightSplit = false,
+        )
+        // No indicator as top/bottom split apps should not be dragged
+        result = visualIndicator.updateIndicatorType(foldCenter())
+        assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+        result = visualIndicator.updateIndicatorType(foldLeftBottom())
+        assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+        result = visualIndicator.updateIndicatorType(foldRightBottom())
         assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
     }
 
@@ -381,7 +461,11 @@
         verify(taskDisplayAreaOrganizer, never()).attachToDisplayArea(anyInt(), any())
     }
 
-    private fun createVisualIndicator(dragStartState: DesktopModeVisualIndicator.DragStartState) {
+    private fun createVisualIndicator(
+        dragStartState: DesktopModeVisualIndicator.DragStartState,
+        isSmallTablet: Boolean = false,
+        isLeftRightSplit: Boolean = true,
+    ) {
         visualIndicator =
             DesktopModeVisualIndicator(
                 desktopExecutor,
@@ -395,20 +479,53 @@
                 dragStartState,
                 bubbleBoundsProvider,
                 snapEventHandler,
+                isSmallTablet,
+                isLeftRightSplit,
             )
     }
 
+    private fun foldCenter(): PointF {
+        return PointF(
+            FOLDABLE_DISPLAY_BOUNDS.centerX().toFloat(),
+            FOLDABLE_DISPLAY_BOUNDS.centerY().toFloat(),
+        )
+    }
+
+    private fun foldLeftEdge(): PointF {
+        return PointF(0f, 50f)
+    }
+
+    private fun foldRightEdge(): PointF {
+        return PointF(750f, 50f)
+    }
+
+    private fun foldLeftBottom(): PointF {
+        return PointF(0f, 650f)
+    }
+
+    private fun foldRightBottom(): PointF {
+        return PointF(750f, 650f)
+    }
+
     companion object {
         private const val TRANSITION_AREA_WIDTH = 32
         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 =
+        private val TABLET_DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
+        private val TABLET_STABLE_INSETS =
             Rect(
-                DISPLAY_BOUNDS.left,
-                DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
-                DISPLAY_BOUNDS.right,
-                DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
+                TABLET_DISPLAY_BOUNDS.left,
+                TABLET_DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
+                TABLET_DISPLAY_BOUNDS.right,
+                TABLET_DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
+            )
+        private val FOLDABLE_DISPLAY_BOUNDS = Rect(0, 0, 800, 700)
+        private val FOLDABLE_STABLE_INSETS =
+            Rect(
+                FOLDABLE_DISPLAY_BOUNDS.left,
+                FOLDABLE_DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
+                FOLDABLE_DISPLAY_BOUNDS.right,
+                FOLDABLE_DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
             )
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt
new file mode 100644
index 0000000..ef394d8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
+import android.os.Binder
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import android.view.WindowManager.TRANSIT_PIP
+import android.window.TransitionInfo
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+/**
+ * Tests for [DesktopPipTransitionObserver].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopPipTransitionObserverTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopPipTransitionObserverTest : ShellTestCase() {
+
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+    private lateinit var observer: DesktopPipTransitionObserver
+
+    private val transition = Binder()
+    private var onSuccessInvokedCount = 0
+
+    @Before
+    fun setUp() {
+        observer = DesktopPipTransitionObserver()
+
+        onSuccessInvokedCount = 0
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+    fun onTransitionReady_taskInPinnedWindowingMode_onSuccessInvoked() {
+        val taskId = 1
+        val pipTransition = createPendingPipTransition(taskId)
+        val successfulChange = createChange(taskId, WINDOWING_MODE_PINNED)
+        observer.addPendingPipTransition(pipTransition)
+
+        observer.onTransitionReady(
+            transition = transition,
+            info = TransitionInfo(
+                TRANSIT_PIP, /* flags= */
+                0
+            ).apply { addChange(successfulChange) },
+        )
+
+        assertThat(onSuccessInvokedCount).isEqualTo(1)
+    }
+
+    private fun createPendingPipTransition(
+        taskId: Int
+    ): DesktopPipTransitionObserver.PendingPipTransition {
+        return DesktopPipTransitionObserver.PendingPipTransition(
+            token = transition,
+            taskId = taskId,
+            onSuccess = { onSuccessInvokedCount += 1 },
+        )
+    }
+
+    private fun createChange(taskId: Int, windowingMode: Int): TransitionInfo.Change {
+        return TransitionInfo.Change(mock(), mock()).apply {
+            taskInfo =
+                TestRunningTaskInfoBuilder()
+                    .setTaskId(taskId)
+                    .setWindowingMode(windowingMode)
+                    .build()
+        }
+    }
+}
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 de92d39..3fb0083 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
@@ -58,6 +58,7 @@
 import org.mockito.kotlin.any
 import org.mockito.kotlin.clearInvocations
 import org.mockito.kotlin.eq
+import org.mockito.kotlin.isNull
 import org.mockito.kotlin.never
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
@@ -275,6 +276,18 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun removeActiveTask_excludingDesk_leavesTaskInDesk() {
+        repo.addDesk(displayId = 2, deskId = 11)
+        repo.addDesk(displayId = 3, deskId = 12)
+        repo.addTaskToDesk(displayId = 3, deskId = 12, taskId = 100, isVisible = true)
+
+        repo.removeActiveTask(taskId = 100, excludedDeskId = 12)
+
+        assertThat(repo.getActiveTaskIdsInDesk(12)).contains(100)
+    }
+
+    @Test
     fun isActiveTask_nonExistingTask_returnsFalse() {
         assertThat(repo.isActiveTask(99)).isFalse()
     }
@@ -319,6 +332,8 @@
                         visibleTasks = ArraySet(arrayOf(1)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(),
+                        leftTiledTask = null,
+                        rightTiledTask = null,
                     )
                 verify(persistentRepository)
                     .addOrUpdateDesktop(
@@ -327,6 +342,8 @@
                         visibleTasks = ArraySet(arrayOf(1, 2)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(),
+                        leftTiledTask = null,
+                        rightTiledTask = null,
                     )
             }
         }
@@ -346,6 +363,52 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    fun leftTiledTask_updatedInRepoAndPersisted() {
+        runTest(StandardTestDispatcher()) {
+            repo.addLeftTiledTask(displayId = DEFAULT_DISPLAY, taskId = 1)
+
+            assertThat(repo.getLeftTiledTask(displayId = DEFAULT_DISPLAY)).isEqualTo(1)
+            verify(persistentRepository)
+                .addOrUpdateDesktop(
+                    DEFAULT_USER_ID,
+                    DEFAULT_DESKTOP_ID,
+                    visibleTasks = ArraySet(),
+                    minimizedTasks = ArraySet(),
+                    freeformTasksInZOrder = arrayListOf(),
+                    leftTiledTask = 1,
+                    rightTiledTask = null,
+                )
+
+            repo.removeRightTiledTask(displayId = DEFAULT_DISPLAY)
+            assertThat(repo.getRightTiledTask(displayId = DEFAULT_DISPLAY)).isNull()
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    fun rightTiledTask_updatedInRepoAndPersisted() {
+        runTest(StandardTestDispatcher()) {
+            repo.addRightTiledTask(displayId = DEFAULT_DISPLAY, taskId = 1)
+
+            assertThat(repo.getRightTiledTask(displayId = DEFAULT_DISPLAY)).isEqualTo(1)
+            verify(persistentRepository)
+                .addOrUpdateDesktop(
+                    DEFAULT_USER_ID,
+                    DEFAULT_DESKTOP_ID,
+                    visibleTasks = ArraySet(),
+                    minimizedTasks = ArraySet(),
+                    freeformTasksInZOrder = arrayListOf(),
+                    leftTiledTask = null,
+                    rightTiledTask = 1,
+                )
+
+            repo.removeLeftTiledTask(displayId = DEFAULT_DISPLAY)
+            assertThat(repo.getLeftTiledTask(displayId = DEFAULT_DISPLAY)).isNull()
+        }
+    }
+
+    @Test
     fun isOnlyVisibleNonClosingTask_singleVisibleMinimizedTask() {
         val taskId = 1
         repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
@@ -651,6 +714,8 @@
                         visibleTasks = ArraySet(),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(5),
+                        leftTiledTask = null,
+                        rightTiledTask = null,
                     )
                 verify(persistentRepository)
                     .addOrUpdateDesktop(
@@ -659,6 +724,8 @@
                         visibleTasks = ArraySet(arrayOf(5)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(6, 5),
+                        leftTiledTask = null,
+                        rightTiledTask = null,
                     )
                 verify(persistentRepository)
                     .addOrUpdateDesktop(
@@ -667,6 +734,8 @@
                         visibleTasks = ArraySet(arrayOf(5, 6)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(7, 6, 5),
+                        leftTiledTask = null,
+                        rightTiledTask = null,
                     )
             }
         }
@@ -717,6 +786,8 @@
                         visibleTasks = ArraySet(),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(5),
+                        leftTiledTask = null,
+                        rightTiledTask = null,
                     )
                 verify(persistentRepository)
                     .addOrUpdateDesktop(
@@ -725,6 +796,8 @@
                         visibleTasks = ArraySet(arrayOf(5)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(6, 5),
+                        leftTiledTask = null,
+                        rightTiledTask = null,
                     )
                 verify(persistentRepository)
                     .addOrUpdateDesktop(
@@ -733,6 +806,8 @@
                         visibleTasks = ArraySet(arrayOf(5, 6)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(7, 6, 5),
+                        leftTiledTask = null,
+                        rightTiledTask = null,
                     )
                 verify(persistentRepository, times(2))
                     .addOrUpdateDesktop(
@@ -741,6 +816,8 @@
                         visibleTasks = ArraySet(arrayOf(5, 7)),
                         minimizedTasks = ArraySet(arrayOf(6)),
                         freeformTasksInZOrder = arrayListOf(7, 6, 5),
+                        leftTiledTask = null,
+                        rightTiledTask = null,
                     )
             }
         }
@@ -781,6 +858,8 @@
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
                     freeformTasksInZOrder = arrayListOf(1),
+                    leftTiledTask = null,
+                    rightTiledTask = null,
                 )
             verify(persistentRepository)
                 .addOrUpdateDesktop(
@@ -789,6 +868,8 @@
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
                     freeformTasksInZOrder = ArrayList(),
+                    leftTiledTask = null,
+                    rightTiledTask = null,
                 )
         }
     }
@@ -818,6 +899,8 @@
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
                     freeformTasksInZOrder = arrayListOf(1),
+                    leftTiledTask = null,
+                    rightTiledTask = null,
                 )
             verify(persistentRepository)
                 .addOrUpdateDesktop(
@@ -826,6 +909,8 @@
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
                     freeformTasksInZOrder = ArrayList(),
+                    leftTiledTask = null,
+                    rightTiledTask = null,
                 )
         }
     }
@@ -857,6 +942,8 @@
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
                     freeformTasksInZOrder = arrayListOf(1),
+                    leftTiledTask = null,
+                    rightTiledTask = null,
                 )
             verify(persistentRepository, never())
                 .addOrUpdateDesktop(
@@ -865,6 +952,8 @@
                     visibleTasks = ArraySet(),
                     minimizedTasks = ArraySet(),
                     freeformTasksInZOrder = ArrayList(),
+                    leftTiledTask = null,
+                    rightTiledTask = null,
                 )
         }
     }
@@ -1203,6 +1292,17 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    fun removeDesk_removesFromPersistence() =
+        runTest(StandardTestDispatcher()) {
+            repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+
+            repo.removeDesk(deskId = 2)
+
+            verify(persistentRepository).removeDesktop(DEFAULT_USER_ID, 2)
+        }
+
+    @Test
     fun getTaskInFullImmersiveState_byDisplay() {
         repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
         repo.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -1214,36 +1314,6 @@
     }
 
     @Test
-    fun setTaskInPip_savedAsMinimizedPipInDisplay() {
-        assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isFalse()
-
-        repo.setTaskInPip(DEFAULT_DESKTOP_ID, taskId = 1, enterPip = true)
-
-        assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isTrue()
-    }
-
-    @Test
-    fun removeTaskInPip_removedAsMinimizedPipInDisplay() {
-        repo.setTaskInPip(DEFAULT_DESKTOP_ID, taskId = 1, enterPip = true)
-        assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isTrue()
-
-        repo.setTaskInPip(DEFAULT_DESKTOP_ID, taskId = 1, enterPip = false)
-
-        assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isFalse()
-    }
-
-    @Test
-    fun setTaskInPip_multipleDisplays_bothAreInPip() {
-        repo.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
-        repo.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
-        repo.setTaskInPip(DEFAULT_DESKTOP_ID, taskId = 1, enterPip = true)
-        repo.setTaskInPip(SECOND_DISPLAY, taskId = 2, enterPip = true)
-
-        assertThat(repo.isTaskMinimizedPipInDisplay(DEFAULT_DESKTOP_ID, taskId = 1)).isTrue()
-        assertThat(repo.isTaskMinimizedPipInDisplay(SECOND_DISPLAY, taskId = 2)).isTrue()
-    }
-
-    @Test
     @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun addTask_deskDoesNotExists_createsDesk() {
         repo.addTask(displayId = 999, taskId = 6, isVisible = true)
@@ -1398,6 +1468,8 @@
                 visibleTasks = any(),
                 minimizedTasks = any(),
                 freeformTasksInZOrder = any(),
+                leftTiledTask = isNull(),
+                rightTiledTask = isNull(),
             )
     }
 
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 54360a8..4ace1b2 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
@@ -119,9 +119,8 @@
     }
 
     @Test
-    fun onTaskChanging_freeformTask_nonActiveTaskInDesktopRepo_addsTaskToDesktopRepo() {
+    fun onTaskChanging_freeformTask_addsTaskToDesktopRepo() {
         val task = createFreeformTask().apply { isVisible = true }
-        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
 
         desktopTaskChangeListener.onTaskChanging(task)
 
@@ -129,28 +128,6 @@
     }
 
     @Test
-    fun onTaskChanging_freeformTask_activeVisibleTaskInDesktopRepo_updatesTaskVisibility() {
-        val task = createFreeformTask().apply { isVisible = true }
-        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
-
-        desktopTaskChangeListener.onTaskChanging(task)
-
-        verify(desktopUserRepositories.current)
-            .updateTask(task.displayId, task.taskId, task.isVisible)
-    }
-
-    @Test
-    fun onTaskChanging_freeformTask_activeNonVisibleTask_updatesTaskVisibility() {
-        val task = createFreeformTask().apply { isVisible = false }
-        whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
-
-        desktopTaskChangeListener.onTaskChanging(task)
-
-        verify(desktopUserRepositories.current)
-            .updateTask(task.displayId, task.taskId, task.isVisible)
-    }
-
-    @Test
     fun onTaskMovingToFront_fullscreenTask_activeTaskInDesktopRepo_removesTaskFromRepo() {
         val task = createFullscreenTask().apply { isVisible = true }
         whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
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 8f499c9..71e46bc 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
@@ -22,6 +22,7 @@
 import android.app.KeyguardManager
 import android.app.PendingIntent
 import android.app.PictureInPictureParams
+import android.app.WindowConfiguration
 import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
 import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -31,6 +32,7 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 import android.content.pm.ActivityInfo
 import android.content.pm.ActivityInfo.CONFIG_DENSITY
 import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
@@ -55,6 +57,7 @@
 import android.testing.TestableContext
 import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
+import android.view.Display.INVALID_DISPLAY
 import android.view.DragEvent
 import android.view.Gravity
 import android.view.MotionEvent
@@ -85,6 +88,7 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito.never
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightPx
 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_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS
@@ -101,6 +105,7 @@
 import com.android.wm.shell.bubbles.BubbleController
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.HomeIntentProvider
 import com.android.wm.shell.common.MultiInstanceHelper
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
@@ -260,6 +265,7 @@
     @Mock private lateinit var desksOrganizer: DesksOrganizer
     @Mock private lateinit var userProfileContexts: UserProfileContexts
     @Mock private lateinit var desksTransitionsObserver: DesksTransitionObserver
+    @Mock private lateinit var desktopPipTransitionObserver: DesktopPipTransitionObserver
     @Mock private lateinit var packageManager: PackageManager
     @Mock private lateinit var mockDisplayContext: Context
     @Mock private lateinit var dragToDisplayTransitionHandler: DragToDisplayTransitionHandler
@@ -275,6 +281,7 @@
     private lateinit var testScope: CoroutineScope
     private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
     private lateinit var spyContext: TestableContext
+    private lateinit var homeIntentProvider: HomeIntentProvider
 
     private val shellExecutor = TestShellExecutor()
     private val bgExecutor = TestShellExecutor()
@@ -294,6 +301,10 @@
     private val wallpaperToken = MockToken().token()
     private val homeComponentName = ComponentName(HOME_LAUNCHER_PACKAGE_NAME, /* class */ "")
 
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
     @Before
     fun setUp() {
         Dispatchers.setMain(StandardTestDispatcher())
@@ -323,12 +334,14 @@
                 transitions,
                 userRepositories,
                 shellTaskOrganizer,
+                desksOrganizer,
                 MAX_TASK_LIMIT,
                 mockInteractionJankMonitor,
                 mContext,
                 mockHandler,
             )
         desktopModeCompatPolicy = spy(DesktopModeCompatPolicy(spyContext))
+        homeIntentProvider = HomeIntentProvider(context)
 
         whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
         whenever(transitions.startTransition(anyInt(), any(), anyOrNull())).thenAnswer { Binder() }
@@ -347,6 +360,7 @@
             .thenReturn(Binder())
         whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
         whenever(displayController.getDisplayContext(anyInt())).thenReturn(mockDisplayContext)
+        whenever(mockDisplayContext.resources).thenReturn(resources)
         whenever(displayController.getDisplay(anyInt())).thenReturn(display)
         whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
             (i.arguments.first() as Rect).set(STABLE_BOUNDS)
@@ -381,6 +395,7 @@
         whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
         whenever(userProfileContexts[anyInt()]).thenReturn(context)
         whenever(userProfileContexts.getOrCreate(anyInt())).thenReturn(context)
+        whenever(freeformTaskTransitionStarter.startPipTransition(any())).thenReturn(Binder())
 
         controller = createController()
         controller.setSplitScreenController(splitScreenController)
@@ -428,6 +443,7 @@
             dragToDesktopTransitionHandler,
             mMockDesktopImmersiveController,
             userRepositories,
+            repositoryInitializer,
             recentsTransitionHandler,
             multiInstanceHelper,
             shellExecutor,
@@ -444,10 +460,12 @@
             overviewToDesktopTransitionObserver,
             desksOrganizer,
             desksTransitionsObserver,
+            Optional.of(desktopPipTransitionObserver),
             userProfileContexts,
             desktopModeCompatPolicy,
             dragToDisplayTransitionHandler,
             moveToDisplayTransitionHandler,
+            homeIntentProvider,
         )
 
     @After
@@ -621,11 +639,13 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun isDesktopModeShowing_noTasks_returnsFalse() {
         assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun isDesktopModeShowing_noTasksVisible_returnsFalse() {
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
@@ -636,6 +656,15 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun isDesktopModeShowing_noActiveDesk_returnsFalse() {
+        taskRepository.setDeskInactive(deskId = 0)
+
+        assertThat(controller.isDesktopModeShowing(displayId = 0)).isFalse()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun isDesktopModeShowing_tasksActiveAndVisible_returnsTrue() {
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
@@ -650,6 +679,7 @@
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
         Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
     )
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun isDesktopModeShowing_topTransparentFullscreenTask_returnsTrue() {
         val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
         taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
@@ -659,6 +689,20 @@
 
     @Test
     @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun isDesktopModeShowing_deskInactive_topTransparentFullscreenTask_returnsTrue() {
+        val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+        taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
+        taskRepository.setDeskInactive(deskId = 0)
+
+        assertThat(controller.isDesktopModeShowing(displayId = DEFAULT_DISPLAY)).isTrue()
+    }
+
+    @Test
+    @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
         Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
     )
@@ -1047,11 +1091,29 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun isAnyDeskActive_noTasks_returnsFalse() {
         assertThat(controller.isAnyDeskActive(DEFAULT_DISPLAY)).isFalse()
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun isAnyDeskActive_noActiveDesk_returnsFalse() {
+        taskRepository.setDeskInactive(deskId = 0)
+
+        assertThat(controller.isAnyDeskActive(DEFAULT_DISPLAY)).isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun isAnyDeskActive_withActiveDesk_returnsTrue() {
+        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+
+        assertThat(controller.isAnyDeskActive(DEFAULT_DISPLAY)).isTrue()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun isAnyDeskActive_twoTasks_bothVisible_returnsTrue() {
         setUpHomeTask()
 
@@ -1062,6 +1124,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun isInDesktop_twoTasks_oneVisible_returnsTrue() {
         setUpHomeTask()
 
@@ -1072,6 +1135,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun isAnyDeskActive_twoTasksVisibleOnDifferentDisplays_returnsTrue() {
         taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
         taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -1091,7 +1155,7 @@
         controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
 
         val finalBounds = findBoundsChange(wct, task)
-        assertThat(finalBounds).isEqualTo(Rect())
+        assertThat(finalBounds).isNull()
     }
 
     @Test
@@ -1102,7 +1166,7 @@
         controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
 
         val finalBounds = findBoundsChange(wct, task)
-        assertThat(finalBounds).isEqualTo(Rect())
+        assertThat(finalBounds).isNull()
     }
 
     @Test
@@ -1113,7 +1177,7 @@
         controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
 
         val finalBounds = findBoundsChange(wct, task)
-        assertThat(finalBounds).isEqualTo(Rect())
+        assertThat(finalBounds).isNull()
     }
 
     @Test
@@ -1124,7 +1188,55 @@
         controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
 
         val finalBounds = findBoundsChange(wct, task)
-        assertThat(finalBounds).isEqualTo(Rect())
+        assertThat(finalBounds).isNull()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES)
+    fun addMoveToDeskTaskChanges_newTaskInstance_inheritsClosingInstanceBounds() {
+        // Setup existing task.
+        val existingTask = setUpFreeformTask(active = true)
+        val testComponent = ComponentName(/* package */ "test.package", /* class */ "test.class")
+        existingTask.topActivity = testComponent
+        existingTask.configuration.windowConfiguration.setBounds(Rect(0, 0, 500, 500))
+        // Set up new instance of already existing task.
+        val launchingTask = setUpFullscreenTask()
+        launchingTask.topActivity = testComponent
+        launchingTask.baseIntent.addFlags(FLAG_ACTIVITY_NEW_TASK)
+
+        // Move new instance to desktop. By default multi instance is not supported so first
+        // instance will close.
+        val wct = WindowContainerTransaction()
+        controller.addMoveToDeskTaskChanges(wct, launchingTask, deskId = 0)
+
+        // New instance should inherit task bounds of old instance.
+        assertThat(findBoundsChange(wct, launchingTask))
+            .isEqualTo(existingTask.configuration.windowConfiguration.bounds)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES)
+    fun handleRequest_newTaskInstance_inheritsClosingInstanceBounds() {
+        setUpLandscapeDisplay()
+        // Setup existing task.
+        val existingTask = setUpFreeformTask(active = true)
+        val testComponent = ComponentName(/* package */ "test.package", /* class */ "test.class")
+        existingTask.topActivity = testComponent
+        existingTask.configuration.windowConfiguration.setBounds(Rect(0, 0, 500, 500))
+        // Set up new instance of already existing task.
+        val launchingTask = setUpFreeformTask(active = false)
+        taskRepository.removeTask(launchingTask.displayId, launchingTask.taskId)
+        launchingTask.topActivity = testComponent
+        launchingTask.baseIntent.addFlags(FLAG_ACTIVITY_NEW_TASK)
+
+        // Move new instance to desktop. By default multi instance is not supported so first
+        // instance will close.
+        val wct = controller.handleRequest(Binder(), createTransition(launchingTask))
+
+        assertNotNull(wct, "should handle request")
+        val finalBounds = findBoundsChange(wct, launchingTask)
+        // New instance should inherit task bounds of old instance.
+        assertThat(finalBounds).isEqualTo(existingTask.configuration.windowConfiguration.bounds)
     }
 
     @Test
@@ -1358,7 +1470,7 @@
         controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
 
         val finalBounds = findBoundsChange(wct, task)
-        val captionInsets = getAppHeaderHeight(context)
+        val captionInsets = getDesktopViewAppHeaderHeightPx(context)
         finalBounds!!.top += captionInsets
         val finalAspectRatio =
             maxOf(finalBounds.height(), finalBounds.width()) /
@@ -1380,7 +1492,7 @@
         controller.addMoveToDeskTaskChanges(wct, task, deskId = 0)
 
         val finalBounds = findBoundsChange(wct, task)
-        val captionInsets = getAppHeaderHeight(context)
+        val captionInsets = getDesktopViewAppHeaderHeightPx(context)
         finalBounds!!.top += captionInsets
         val finalAspectRatio =
             maxOf(finalBounds.height(), finalBounds.width()) /
@@ -1808,6 +1920,7 @@
         val wallpaperToken = MockToken().token()
         whenever(desktopWallpaperActivityTokenProvider.getToken(SECOND_DISPLAY))
             .thenReturn(wallpaperToken)
+        taskRepository.addDesk(SECOND_DISPLAY, deskId = 2)
         val task = setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = 2, background = true)
 
         controller.moveTaskToDefaultDeskAndActivate(
@@ -2363,6 +2476,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun moveTaskToFront_postsWctWithReorderOp() {
         val task1 = setUpFreeformTask()
         setUpFreeformTask()
@@ -2385,9 +2499,34 @@
     }
 
     @Test
-    fun moveTaskToFront_bringsTasksOverLimit_minimizesBackTask() {
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveTaskToFront_reordersToFront() {
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+        setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+        whenever(
+                desktopMixedTransitionHandler.startLaunchTransition(
+                    eq(TRANSIT_TO_FRONT),
+                    any(),
+                    eq(task1.taskId),
+                    anyOrNull(),
+                    anyOrNull(),
+                )
+            )
+            .thenReturn(Binder())
+
+        controller.moveTaskToFront(task1, remoteTransition = null)
+
+        verify(desksOrganizer).reorderTaskToFront(any(), eq(0), eq(task1))
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveTaskToFront_bringsTasksOverLimit_multiDesksDisabled_minimizesBackTask() {
         setUpHomeTask()
-        val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT + 1).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+            }
         whenever(
                 desktopMixedTransitionHandler.startLaunchTransition(
                     eq(TRANSIT_TO_FRONT),
@@ -2408,6 +2547,32 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveTaskToFront_bringsTasksOverLimit_multiDesksEnabled_minimizesBackTask() {
+        val deskId = 0
+        setUpHomeTask()
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT + 1).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+            }
+        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)
+        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[1])
+    }
+
+    @Test
     fun moveTaskToFront_minimizedTask_marksTaskAsUnminimized() {
         val transition = Binder()
         val freeformTask = setUpFreeformTask()
@@ -2496,8 +2661,13 @@
     }
 
     @Test
-    fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_minimizesBackTask() {
-        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_multiDesksDisabled_minimizesBackTask() {
+        val deskId = 0
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+            }
         val task = createRecentTaskInfo(1001)
         whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
         whenever(
@@ -2520,6 +2690,33 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_multiDesksEnabled_minimizesBackTask() {
+        val deskId = 0
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+            }
+        val task = createRecentTaskInfo(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, unminimizeReason = UnminimizeReason.UNKNOWN)
+
+        val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
+    }
+
+    @Test
     fun moveToNextDisplay_noOtherDisplays() {
         whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY))
         val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -2528,7 +2725,8 @@
     }
 
     @Test
-    fun moveToNextDisplay_moveFromFirstToSecondDisplay() {
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveToNextDisplay_moveFromFirstToSecondDisplay_multiDesksDisabled() {
         // Set up two display ids
         taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
         whenever(rootTaskDisplayAreaOrganizer.displayIds)
@@ -2554,7 +2752,27 @@
     }
 
     @Test
-    fun moveToNextDisplay_moveFromSecondToFirstDisplay() {
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveToNextDisplay_moveFromFirstToSecondDisplay_multiDesksEnabled() {
+        // Set up two display ids
+        val targetDeskId = 2
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+        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)
+
+        verify(desksOrganizer).moveTaskToDesk(any(), eq(targetDeskId), eq(task))
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveToNextDisplay_moveFromSecondToFirstDisplay_multiDesksDisabled() {
         // Set up two display ids
         whenever(rootTaskDisplayAreaOrganizer.displayIds)
             .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
@@ -2580,6 +2798,25 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveToNextDisplay_moveFromSecondToFirstDisplay_multiDesksEnabled() {
+        // Set up two display ids
+        val targetDeskId = 0
+        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)
+
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
+        val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
+        controller.moveToNextDisplay(task.taskId)
+
+        verify(desksOrganizer).moveTaskToDesk(any(), eq(targetDeskId), eq(task))
+    }
+
+    @Test
     @EnableFlags(
         FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
         Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
@@ -2643,6 +2880,7 @@
     @EnableFlags(FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
     fun moveToNextDisplay_sizeInDpPreserved() {
         // Set up two display ids
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2)
         whenever(rootTaskDisplayAreaOrganizer.displayIds)
             .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
         // Create a mock for the target display area: second display
@@ -2684,6 +2922,7 @@
     @EnableFlags(FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT)
     fun moveToNextDisplay_shiftWithinDestinationDisplayBounds() {
         // Set up two display ids
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2)
         whenever(rootTaskDisplayAreaOrganizer.displayIds)
             .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
         // Create a mock for the target display area: second display
@@ -2725,6 +2964,7 @@
         // Set up two display ids
         whenever(rootTaskDisplayAreaOrganizer.displayIds)
             .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2)
         // Create a mock for the target display area: second display
         val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
         whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
@@ -2833,7 +3073,8 @@
     }
 
     @Test
-    fun moveToNextDisplay_toDesktopInOtherDisplay_bringsExistingTasksToFront() {
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveToNextDisplay_toDesktopInOtherDisplay_multiDesksDisabled_bringsExistingTasksToFront() {
         val transition = Binder()
         val sourceDeskId = 0
         val targetDeskId = 2
@@ -2859,6 +3100,64 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveToNextDisplay_toDesktopInOtherDisplay_multiDesksEnabled_bringsExistingTasksToFront() {
+        val transition = Binder()
+        val sourceDeskId = 0
+        val targetDeskId = 2
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+        taskRepository.setDeskInactive(deskId = targetDeskId)
+        // 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)
+        whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+            .thenReturn(transition)
+        val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+        val task2 = setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+
+        controller.moveToNextDisplay(task1.taskId)
+
+        // Existing desktop task in the target display is moved to front.
+        val wct = getLatestTransition()
+        assertNotNull(wct)
+        verify(desksOrganizer).reorderTaskToFront(wct, targetDeskId, task2)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun moveToNextDisplay_toDesktopInOtherDisplay_appliesTaskLimit() {
+        val transition = Binder()
+        val sourceDeskId = 0
+        val targetDeskId = 2
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+        taskRepository.setDeskInactive(deskId = targetDeskId)
+        val targetDeskTasks =
+            (1..MAX_TASK_LIMIT + 1).map { _ ->
+                setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+            }
+        // 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)
+        whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+            .thenReturn(transition)
+        val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+
+        controller.moveToNextDisplay(task.taskId)
+
+        val wct = getLatestTransition()
+        assertNotNull(wct)
+        verify(desksOrganizer).minimizeTask(wct, targetDeskId, targetDeskTasks[0])
+    }
+
+    @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
         Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
@@ -2927,10 +3226,11 @@
         verify(desksOrganizer).activateDesk(any(), eq(targetDeskId))
         verify(desksTransitionsObserver)
             .addPendingTransition(
-                DeskTransition.ActivateDesk(
+                DeskTransition.ActiveDeskWithTask(
                     token = transition,
                     displayId = SECOND_DISPLAY,
                     deskId = targetDeskId,
+                    enterTaskId = task.taskId,
                 )
             )
     }
@@ -2972,7 +3272,7 @@
     @Test
     fun moveToNextDisplay_movingToDesktop_sendsTaskbarRoundingUpdate() {
         val transition = Binder()
-        val sourceDeskId = 1
+        val sourceDeskId = 0
         val targetDeskId = 2
         taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
         taskRepository.setDeskInactive(deskId = targetDeskId)
@@ -3206,6 +3506,7 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
     fun onPipTaskMinimize_autoEnterEnabled_startPipTransition() {
         val task = setUpPipTask(autoEnterEnabled = true)
         val handler = mock(TransitionHandler::class.java)
@@ -3220,6 +3521,7 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
     fun onPipTaskMinimize_autoEnterDisabled_startMinimizeTransition() {
         val task = setUpPipTask(autoEnterEnabled = false)
         whenever(
@@ -3239,6 +3541,103 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+    fun onPipTaskMinimize_autoEnterEnabled_sendsTaskbarRoundingUpdate() {
+        val task = setUpPipTask(autoEnterEnabled = true)
+        val handler = mock(TransitionHandler::class.java)
+        whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
+            .thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
+
+        controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+
+        verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(anyBoolean())
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP,
+    )
+    fun onDesktopTaskEnteredPip_pipIsLastTask_removesWallpaper() {
+        val task = setUpPipTask(autoEnterEnabled = true)
+
+        controller.onDesktopTaskEnteredPip(
+            taskId = task.taskId,
+            deskId = DEFAULT_DISPLAY,
+            displayId = task.displayId,
+            taskIsLastVisibleTaskBeforePip = true,
+        )
+
+        // Wallpaper is moved to the back
+        val wct = getLatestTransition()
+        wct.assertReorder(wallpaperToken, /* toTop= */ false)
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun onDesktopTaskEnteredPip_pipIsLastTask_deactivatesDesk() {
+        val deskId = DEFAULT_DISPLAY
+        val task = setUpPipTask(autoEnterEnabled = true, deskId = deskId)
+        val transition = Binder()
+        whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
+
+        controller.onDesktopTaskEnteredPip(
+            taskId = task.taskId,
+            deskId = deskId,
+            displayId = task.displayId,
+            taskIsLastVisibleTaskBeforePip = true,
+        )
+
+        verify(desksOrganizer).deactivateDesk(any(), eq(deskId))
+        verify(desksTransitionsObserver)
+            .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+    fun onDesktopTaskEnteredPip_pipIsLastTask_launchesHome() {
+        val task = setUpPipTask(autoEnterEnabled = true)
+
+        controller.onDesktopTaskEnteredPip(
+            taskId = task.taskId,
+            deskId = DEFAULT_DISPLAY,
+            displayId = task.displayId,
+            taskIsLastVisibleTaskBeforePip = true,
+        )
+
+        val wct = getLatestTransition()
+        wct.assertPendingIntent(launchHomeIntent(DEFAULT_DISPLAY))
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+    fun onDesktopTaskEnteredPip_pipIsNotLastTask_doesntExitDesktopMode() {
+        val task = setUpPipTask(autoEnterEnabled = true)
+        val deskId = DEFAULT_DISPLAY
+        setUpFreeformTask(deskId = deskId) // launch another freeform task
+        val transition = Binder()
+        whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
+
+        controller.onDesktopTaskEnteredPip(
+            taskId = task.taskId,
+            deskId = deskId,
+            displayId = task.displayId,
+            taskIsLastVisibleTaskBeforePip = false,
+        )
+
+        // No transition to exit Desktop mode is started
+        verifyWCTNotExecuted()
+        verify(desktopModeEnterExitTransitionListener, never())
+            .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+        verify(desksOrganizer, never()).deactivateDesk(any(), eq(deskId))
+        verify(desksTransitionsObserver, never())
+            .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+    }
+
+    @Test
     fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntRemoveWallpaper() {
         val task = setUpFreeformTask(active = true)
         val transition = Binder()
@@ -3456,6 +3855,24 @@
     }
 
     @Test
+    fun onDesktopWindowMinimize_sendsTaskbarRoundingUpdate() {
+        val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+        val transition = Binder()
+        whenever(
+                freeformTaskTransitionStarter.startMinimizedModeTransition(
+                    any(),
+                    anyInt(),
+                    anyBoolean(),
+                )
+            )
+            .thenReturn(transition)
+
+        controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+
+        verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(anyBoolean())
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun handleRequest_fullscreenTask_switchToDesktop_movesTaskToDesk() {
         taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = 5)
@@ -3489,7 +3906,8 @@
     }
 
     @Test
-    fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTask_freeformVisible_multiDesksDisabled_returnSwitchToFreeformWCT() {
         val homeTask = setUpHomeTask()
         val freeformTask = setUpFreeformTask()
         markTaskVisible(freeformTask)
@@ -3505,7 +3923,22 @@
     }
 
     @Test
-    fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_returnSwitchToFreeformWCT() {
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTask_deskActive_multiDesksEnabled_movesToDesk() {
+        val deskId = 0
+        taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId = deskId)
+        setUpHomeTask()
+        val fullscreenTask = createFullscreenTask()
+
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        assertNotNull(wct, "should handle request")
+        verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_multiDesksDisabled_returnSwitchToFreeformWCT() {
         val homeTask = setUpHomeTask()
         val freeformTask = setUpFreeformTask()
         markTaskVisible(freeformTask)
@@ -3530,7 +3963,23 @@
     }
 
     @Test
-    fun handleRequest_fullscreenTaskToFreeform_underTaskLimit_dontMinimize() {
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskWithTaskOnHome_activeDesk_multiDesksEnabled_movesToDesk() {
+        val deskId = 0
+        setUpHomeTask()
+        setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        val fullscreenTask = createFullscreenTask()
+        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        assertNotNull(wct, "should handle request")
+        verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskToDesk_underTaskLimit_multiDesksDisabled_dontMinimize() {
         val freeformTask = setUpFreeformTask()
         markTaskVisible(freeformTask)
         val fullscreenTask = createFullscreenTask()
@@ -3543,7 +3992,29 @@
     }
 
     @Test
-    fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() {
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskToDesk_underTaskLimit_multiDesksEnabled_dontMinimize() {
+        val deskId = 5
+        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        val freeformTask =
+            setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId).also {
+                markTaskVisible(it)
+            }
+
+        // Launch a fullscreen task while in the desk.
+        val fullscreenTask = createFullscreenTask()
+        val transition = Binder()
+        val wct = controller.handleRequest(transition, createTransition(fullscreenTask))
+
+        assertNotNull(wct)
+        verify(desksOrganizer, never()).minimizeTask(eq(wct), eq(deskId), any())
+        assertNull(desktopTasksLimiter.getMinimizingTask(transition))
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskToDesk_bringsTasksOverLimit_multiDesksDisabled_otherTaskIsMinimized() {
         val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
         freeformTasks.forEach { markTaskVisible(it) }
         val fullscreenTask = createFullscreenTask()
@@ -3557,7 +4028,34 @@
     }
 
     @Test
-    fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() {
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskToDesk_bringsTasksOverLimit_multiDesksEnabled_otherTaskIsMinimized() {
+        val deskId = 5
+        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId).also {
+                    markTaskVisible(it)
+                }
+            }
+
+        // Launch a fullscreen task while in the desk.
+        setUpHomeTask()
+        val fullscreenTask = createFullscreenTask()
+        val transition = Binder()
+        val wct = controller.handleRequest(transition, createTransition(fullscreenTask))
+
+        assertNotNull(wct)
+        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
+        val minimizingTask =
+            assertNotNull(desktopTasksLimiter.getMinimizingTask(transition)?.taskId)
+        assertThat(minimizingTask).isEqualTo(freeformTasks[0].taskId)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_multiDesksDisabled_otherTaskIsMinimized() {
         val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
         freeformTasks.forEach { markTaskVisible(it) }
         val fullscreenTask = createFullscreenTask()
@@ -3578,7 +4076,31 @@
     }
 
     @Test
-    fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() {
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_multiDesksEnabled_otherTaskIsMinimized() {
+        val deskId = 5
+        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+            }
+        freeformTasks.forEach { markTaskVisible(it) }
+        val fullscreenTask = createFullscreenTask()
+        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        assertNotNull(wct)
+        // The launching task is moved to the desk.
+        verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+        // The bottom-most task is minimized.
+        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_multiDesksDisabled_existingAndNewTasksAreMinimized() {
         val minimizedTask = setUpFreeformTask()
         taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId)
         val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
@@ -3604,7 +4126,90 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_multiDesksEnabled_existingAndNewTasksAreMinimized() {
+        // A desk with a minimized tasks, and non-minimized tasks already at the task limit.
+        val deskId = 5
+        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        val minimizedTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        taskRepository.minimizeTaskInDesk(
+            displayId = DEFAULT_DISPLAY,
+            deskId = deskId,
+            taskId = minimizedTask.taskId,
+        )
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId).also {
+                    markTaskVisible(it)
+                }
+            }
+
+        // Launch a fullscreen task that brings Home to front with it.
+        setUpHomeTask()
+        val fullscreenTask = createFullscreenTask()
+        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        assertNotNull(wct)
+        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTaskWithTaskOnHome_taskAddedToDesk() {
+        // A desk with a minimized tasks, and non-minimized tasks already at the task limit.
+        val deskId = 5
+        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+
+        // Launch a fullscreen task that brings Home to front with it.
+        setUpHomeTask()
+        val fullscreenTask = createFullscreenTask()
+        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+        val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+        assertNotNull(wct)
+        verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+    )
+    fun handleRequest_fullscreenTaskWithTaskOnHome_activatesDesk() {
+        val deskId = 5
+        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = deskId)
+
+        // Launch a fullscreen task that brings Home to front with it.
+        val homeTask = setUpHomeTask()
+        val fullscreenTask = createFullscreenTask()
+        fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+        val transition = Binder()
+        val wct = controller.handleRequest(transition, createTransition(fullscreenTask))
+
+        assertNotNull(wct)
+        wct.assertReorder(homeTask, toTop = true)
+        wct.assertReorder(wallpaperToken, toTop = true)
+        verify(desksOrganizer).activateDesk(wct, deskId)
+        verify(desksTransitionsObserver)
+            .addPendingTransition(
+                DeskTransition.ActiveDeskWithTask(
+                    token = transition,
+                    displayId = DEFAULT_DISPLAY,
+                    deskId = deskId,
+                    enterTaskId = fullscreenTask.taskId,
+                )
+            )
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
         whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
@@ -3627,7 +4232,28 @@
     }
 
     @Test
-    fun handleRequest_fullscreenTask_noTasks_enforceDesktop_fullscreenDisplay_returnNull() {
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun handleRequest_fullscreenTask_noInDesk_enforceDesktop_freeformDisplay_movesToDesk() {
+        val deskId = 0
+        taskRepository.setDeskInactive(deskId)
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
+        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")
+        verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+    }
+
+    @Test
+    fun handleRequest_fullscreenTask_notInDesk_enforceDesktop_fullscreenDisplay_returnNull() {
+        taskRepository.setDeskInactive(deskId = 0)
         whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
         val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
         tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
@@ -3639,6 +4265,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() {
         val freeformTask = setUpFreeformTask()
         markTaskHidden(freeformTask)
@@ -3647,12 +4274,23 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun handleRequest_fullscreenTask_noOtherTasks_returnNull() {
         val fullscreenTask = createFullscreenTask()
         assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTask_notInDesk_returnNull() {
+        taskRepository.setDeskInactive(deskId = 0)
+        val fullscreenTask = createFullscreenTask()
+
+        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() {
         val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY)
         createFreeformTask(displayId = SECOND_DISPLAY)
@@ -3663,8 +4301,27 @@
     }
 
     @Test
-    fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() {
-        val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_fullscreenTask_deskInOtherDisplayActive_returnNull() {
+        taskRepository.setDeskInactive(deskId = 0)
+        val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY)
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2)
+        taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = 2)
+
+        val result =
+            controller.handleRequest(Binder(), createTransition(fullscreenTaskDefaultDisplay))
+
+        assertThat(result).isNull()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_multiDesksDisabled_minimize() {
+        val deskId = 0
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+            }
         freeformTasks.forEach { markTaskVisible(it) }
         val newFreeformTask = createFreeformTask()
 
@@ -3677,6 +4334,24 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_multiDesksEnabled_minimize() {
+        val deskId = 0
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+            }
+        freeformTasks.forEach { markTaskVisible(it) }
+        val newFreeformTask = createFreeformTask()
+
+        val wct =
+            controller.handleRequest(Binder(), createTransition(newFreeformTask, TRANSIT_OPEN))
+
+        assertNotNull(wct)
+        verify(desksOrganizer).minimizeTask(wct, deskId, freeformTasks[0])
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     fun handleRequest_freeformTaskFromInactiveDesk_tracksDeskDeactivation() {
         val deskId = 0
         val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
@@ -3691,7 +4366,8 @@
 
     @Test
     fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() {
-        val freeformTask = setUpFreeformTask()
+        taskRepository.setDeskInactive(deskId = 0)
+        val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
         markTaskHidden(freeformTask)
 
         val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
@@ -3721,9 +4397,10 @@
         whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
         val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
         tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
-
-        val freeformTask = setUpFreeformTask()
+        taskRepository.setDeskInactive(deskId = 0)
+        val freeformTask = setUpFreeformTask(DEFAULT_DISPLAY, deskId = 0)
         markTaskHidden(freeformTask)
+
         val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
 
         assertNotNull(wct, "should handle request")
@@ -3732,7 +4409,10 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    @DisableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
     fun handleRequest_freeformTask_desktopWallpaperDisabled_freeformNotVisible_reorderedToTop() {
         val freeformTask1 = setUpFreeformTask()
         val freeformTask2 = createFreeformTask()
@@ -3751,7 +4431,8 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-    fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_multiDesksDisabled_reorderedToTop() {
         whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val freeformTask1 = setUpFreeformTask()
         val freeformTask2 = createFreeformTask()
@@ -3774,7 +4455,33 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun handleRequest_freeformTask_desktopWallpaperEnabled_notInDesk_reorderedToTop() {
+        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
+        val deskId = 0
+        taskRepository.setDeskInactive(deskId)
+        val freeformTask1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        val freeformTask2 = createFreeformTask()
+
+        val wct =
+            controller.handleRequest(
+                Binder(),
+                createTransition(freeformTask2, type = TRANSIT_TO_FRONT),
+            )
+
+        assertNotNull(wct, "Should handle request")
+        verify(desksOrganizer).reorderTaskToFront(wct, deskId, freeformTask1)
+        wct.assertReorder(freeformTask2, toTop = true)
+    }
+
+    @Test
+    @DisableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
     fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() {
         val task = createFreeformTask()
         val result = controller.handleRequest(Binder(), createTransition(task))
@@ -3785,23 +4492,10 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-    fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
-        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
-        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)
+    @DisableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
     fun handleRequest_freeformTask_dskWallpaperDisabled_freeformOnOtherDisplayOnly_reorderedToTop() {
         val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
         // Second display task
@@ -3817,10 +4511,13 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
+        taskRepository.setDeskInactive(deskId = 0)
         whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
         val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
         // Second display task
-        createFreeformTask(displayId = SECOND_DISPLAY)
+        taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = 2)
+        taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = 2)
+        setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = 2)
 
         val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
 
@@ -3956,7 +4653,8 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-    fun handleRequest_topActivityTransparentWithoutDisplay_returnSwitchToFreeformWCT() {
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_topActivityTransparentWithoutDisplay_multiDesksDisabled_returnSwitchToFreeformWCT() {
         val freeformTask = setUpFreeformTask()
         markTaskVisible(freeformTask)
 
@@ -3973,6 +4671,29 @@
     }
 
     @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun handleRequest_topActivityTransparentWithoutDisplay_multiDesksEnabled_returnSwitchToFreeformWCT() {
+        val deskId = 0
+        val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        markTaskVisible(freeformTask)
+
+        val task =
+            setUpFullscreenTask().apply {
+                isActivityStackTransparent = true
+                isTopActivityNoDisplay = true
+                numActivities = 1
+            }
+
+        val wct = controller.handleRequest(Binder(), createTransition(task))
+
+        assertNotNull(wct)
+        verify(desksOrganizer).moveTaskToDesk(wct, deskId, task)
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
     @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun handleRequest_topActivityTransparentWithDisplay_returnSwitchToFullscreenWCT() {
@@ -4031,7 +4752,8 @@
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
         Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
     )
-    fun handleRequest_onlyTopTransparentFullscreenTask_returnSwitchToFreeformWCT() {
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_onlyTopTransparentFullscreenTask_multiDesksDisabled_returnSwitchToFreeformWCT() {
         val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
         taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
 
@@ -4043,8 +4765,28 @@
     }
 
     @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun handleRequest_onlyTopTransparentFullscreenTask_multiDesksEnabled_movesToDesktop() {
+        val deskId = 0
+        val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+        taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
+        taskRepository.setDeskInactive(deskId = deskId)
+
+        val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+
+        val wct = controller.handleRequest(Binder(), createTransition(task))
+        assertNotNull(wct)
+        verify(desksOrganizer).moveTaskToDesk(wct, deskId, task)
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
     fun handleRequest_desktopNotShowing_topTransparentFullscreenTask_returnNull() {
+        taskRepository.setDeskInactive(deskId = 0)
         val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
 
         assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
@@ -4098,7 +4840,8 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
-    fun handleRequest_systemUIActivityWithoutDisplay_returnSwitchToFreeformWCT() {
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun handleRequest_systemUIActivityWithoutDisplay_multiDesksDisabled_returnSwitchToFreeformWCT() {
         val freeformTask = setUpFreeformTask()
         markTaskVisible(freeformTask)
 
@@ -4117,6 +4860,32 @@
             .isEqualTo(WINDOWING_MODE_FREEFORM)
     }
 
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun handleRequest_systemUIActivityWithoutDisplay_multiDesksEnabled_movesTaskToDesk() {
+        val deskId = 0
+        val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+        markTaskVisible(freeformTask)
+
+        // Set task as systemUI package
+        val systemUIPackageName =
+            context.resources.getString(com.android.internal.R.string.config_systemUi)
+        val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
+        val task =
+            setUpFullscreenTask(displayId = DEFAULT_DISPLAY).apply {
+                baseActivity = baseComponent
+                isTopActivityNoDisplay = true
+            }
+
+        val wct = controller.handleRequest(Binder(), createTransition(task))
+
+        assertNotNull(wct)
+        verify(desksOrganizer).moveTaskToDesk(wct, deskId, task)
+    }
+
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
     fun handleRequest_defaultHomePackageWithDisplay_returnSwitchToFullscreenWCT() {
         val freeformTask = setUpFreeformTask()
@@ -4159,6 +4928,7 @@
 
     @Test
     fun handleRequest_systemUIActivityWithDisplay_returnSwitchToFullscreenWCT_enforcedDesktop() {
+        taskRepository.setDeskInactive(deskId = 0)
         whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
         val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
         tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
@@ -4366,6 +5136,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun handleRequest_closeTransition_singleTaskNoToken_secondaryDisplay_launchesHome() {
         taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
         taskRepository.setActiveDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -4792,6 +5563,55 @@
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
         Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
     )
+    fun activateDesk_hasNonRunningTask_startsTask() {
+        val deskId = 0
+        val nonRunningTask =
+            setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0, background = true)
+
+        val transition = Binder()
+        val deskChange = mock(TransitionInfo.Change::class.java)
+        whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull()))
+            .thenReturn(transition)
+        whenever(desksOrganizer.isDeskActiveAtEnd(deskChange, deskId)).thenReturn(true)
+        // Make desk inactive by activating another desk.
+        taskRepository.addDesk(DEFAULT_DISPLAY, deskId = 1)
+        taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId = 1)
+
+        controller.activateDesk(deskId, RemoteTransition(TestRemoteTransition()))
+
+        val wct = getLatestWct(TRANSIT_TO_FRONT, OneShotRemoteHandler::class.java)
+        assertNotNull(wct)
+        wct.assertLaunchTask(nonRunningTask.taskId, WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun activateDesk_hasRunningTask_reordersTask() {
+        val deskId = 0
+        val runningTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+
+        val transition = Binder()
+        val deskChange = mock(TransitionInfo.Change::class.java)
+        whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull()))
+            .thenReturn(transition)
+        whenever(desksOrganizer.isDeskActiveAtEnd(deskChange, deskId)).thenReturn(true)
+        // Make desk inactive by activating another desk.
+        taskRepository.addDesk(DEFAULT_DISPLAY, deskId = 1)
+        taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId = 1)
+
+        controller.activateDesk(deskId, RemoteTransition(TestRemoteTransition()))
+
+        verify(desksOrganizer).reorderTaskToFront(any(), eq(deskId), eq(runningTask))
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
     fun moveTaskToDesk_multipleDesks_addsPendingTransition() {
         val transition = Binder()
         whenever(enterDesktopTransitionHandler.moveToDesktop(any(), any())).thenReturn(transition)
@@ -5578,7 +6398,8 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-    fun openInstance_fromFreeformAddsNewWindow() {
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun openInstance_fromFreeformAddsNewWindow_multiDesksDisabled() {
         setUpLandscapeDisplay()
         val task = setUpFreeformTask()
         val taskToRequest = setUpFreeformTask()
@@ -5592,7 +6413,9 @@
                 )
             )
             .thenReturn(Binder())
+
         runOpenInstance(task, taskToRequest.taskId)
+
         verify(desktopMixedTransitionHandler)
             .startLaunchTransition(anyInt(), any(), anyInt(), anyOrNull(), anyOrNull())
         val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_TO_FRONT)
@@ -5601,10 +6424,42 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
-    fun openInstance_fromFreeform_minimizesIfNeeded() {
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun openInstance_fromFreeformAddsNewWindow_multiDesksEnabled() {
         setUpLandscapeDisplay()
-        val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+        val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+        val taskToRequest = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+        whenever(
+                desktopMixedTransitionHandler.startLaunchTransition(
+                    eq(TRANSIT_TO_FRONT),
+                    any(),
+                    eq(taskToRequest.taskId),
+                    anyOrNull(),
+                    anyOrNull(),
+                )
+            )
+            .thenReturn(Binder())
+
+        runOpenInstance(task, taskToRequest.taskId)
+
+        verify(desktopMixedTransitionHandler)
+            .startLaunchTransition(anyInt(), any(), anyInt(), anyOrNull(), anyOrNull())
+        verify(desksOrganizer).reorderTaskToFront(any(), eq(0), eq(taskToRequest))
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun openInstance_fromFreeform_multiDesksDisabled_minimizesIfNeeded() {
+        setUpLandscapeDisplay()
+        val deskId = 0
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT + 1).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+            }
         val oldestTask = freeformTasks.first()
         val newestTask = freeformTasks.last()
 
@@ -5630,6 +6485,40 @@
     }
 
     @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun openInstance_fromFreeform_multiDesksEnabled_minimizesIfNeeded() {
+        setUpLandscapeDisplay()
+        val deskId = 0
+        val freeformTasks =
+            (1..MAX_TASK_LIMIT + 1).map { _ ->
+                setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = deskId)
+            }
+        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
+        verify(desksOrganizer).minimizeTask(wct, deskId, oldestTask)
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
     fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
         setUpLandscapeDisplay()
@@ -6447,6 +7336,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
     fun shouldPlayDesktopAnimation_notShowingDesktop_doesNotPlay() {
+        taskRepository.setDeskInactive(deskId = 0)
         val triggerTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
         taskRepository.setTaskInFullImmersiveState(
             displayId = triggerTask.displayId,
@@ -6523,6 +7413,7 @@
     @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
     fun shouldPlayDesktopAnimation_fullscreenStaysFullscreen_doesNotPlay() {
         val triggerTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+        taskRepository.setDeskInactive(deskId = 0)
         assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
 
         assertThat(
@@ -6558,6 +7449,7 @@
     @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
     fun shouldPlayDesktopAnimation_freeformExitsDesktop_doesNotPlay() {
         val triggerTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, active = false)
+        taskRepository.setDeskInactive(deskId = 0)
         assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse()
 
         assertThat(
@@ -6582,12 +7474,21 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun testCreateDesk_invalidDisplay_dropsRequest() {
+        controller.createDesk(INVALID_DISPLAY)
+
+        verify(desksOrganizer, never()).createDesk(any(), any())
+    }
+
+    @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
         Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
     )
     fun startLaunchTransition_desktopNotShowing_movesWallpaperToFront() {
-        val launchingTask = createFreeformTask()
+        taskRepository.setDeskInactive(deskId = 0)
+        val launchingTask = createFreeformTask(displayId = DEFAULT_DISPLAY)
         val wct = WindowContainerTransaction()
         wct.reorder(launchingTask.token, /* onTop= */ true)
         whenever(
@@ -6601,7 +7502,13 @@
             )
             .thenReturn(Binder())
 
-        controller.startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+        controller.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            launchingTaskId = null,
+            deskId = 0,
+            displayId = DEFAULT_DISPLAY,
+        )
 
         val latestWct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
         val launchingTaskReorderIndex = latestWct.indexOfReorder(launchingTask, toTop = true)
@@ -6625,6 +7532,8 @@
             transitionType = TRANSIT_OPEN,
             wct = WindowContainerTransaction(),
             launchingTaskId = null,
+            deskId = 0,
+            displayId = DEFAULT_DISPLAY,
         )
 
         verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(any())
@@ -6634,6 +7543,51 @@
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
         Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun startLaunchTransition_launchingTaskFromInactiveDesk_otherDeskActive_activatesDesk() {
+        val activeDeskId = 4
+        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = activeDeskId)
+        taskRepository.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = activeDeskId)
+        val inactiveDesk = 5
+        taskRepository.addDesk(displayId = DEFAULT_DISPLAY, deskId = inactiveDesk)
+        val launchingTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = inactiveDesk)
+        val transition = Binder()
+        whenever(
+                desktopMixedTransitionHandler.startLaunchTransition(
+                    eq(TRANSIT_OPEN),
+                    any(),
+                    eq(launchingTask.taskId),
+                    anyOrNull(),
+                    anyOrNull(),
+                )
+            )
+            .thenReturn(transition)
+
+        val wct = WindowContainerTransaction()
+        controller.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            launchingTaskId = launchingTask.taskId,
+            deskId = inactiveDesk,
+            displayId = DEFAULT_DISPLAY,
+        )
+
+        verify(desksOrganizer).activateDesk(any(), eq(inactiveDesk))
+        verify(desksTransitionsObserver)
+            .addPendingTransition(
+                DeskTransition.ActivateDesk(
+                    token = transition,
+                    displayId = DEFAULT_DISPLAY,
+                    deskId = inactiveDesk,
+                )
+            )
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
     )
     fun startLaunchTransition_desktopShowing_doesNotReorderWallpaper() {
         val wct = WindowContainerTransaction()
@@ -6648,8 +7602,14 @@
             )
             .thenReturn(Binder())
 
-        setUpFreeformTask()
-        controller.startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+        setUpFreeformTask(deskId = 0, displayId = DEFAULT_DISPLAY)
+        controller.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            launchingTaskId = null,
+            deskId = 0,
+            displayId = DEFAULT_DISPLAY,
+        )
 
         val latestWct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
         assertNull(latestWct.hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() })
@@ -6817,8 +7777,12 @@
         return task
     }
 
-    private fun setUpPipTask(autoEnterEnabled: Boolean): RunningTaskInfo =
-        setUpFreeformTask().apply {
+    private fun setUpPipTask(
+        autoEnterEnabled: Boolean,
+        displayId: Int = DEFAULT_DISPLAY,
+        deskId: Int = DEFAULT_DISPLAY,
+    ): RunningTaskInfo =
+        setUpFreeformTask(displayId = displayId, deskId = deskId).apply {
             pictureInPictureParams =
                 PictureInPictureParams.Builder().setAutoEnterEnabled(autoEnterEnabled).build()
         }
@@ -6997,7 +7961,15 @@
     }
 
     private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
-        wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
+        wct.changes.entries
+            .find { (token, change) ->
+                token == task.token.asBinder() &&
+                    (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0
+            }
+            ?.value
+            ?.configuration
+            ?.windowConfiguration
+            ?.bounds
 
     private fun verifyWCTNotExecuted() {
         verify(transitions, never()).startTransition(anyInt(), any(), isNull())
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 7610364..eeecb00 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
@@ -39,12 +39,14 @@
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
+import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
 import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
 import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -67,10 +69,13 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.any
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
 import org.mockito.quality.Strictness
 
 /**
@@ -84,6 +89,7 @@
 class DesktopTasksLimiterTest : ShellTestCase() {
 
     @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
+    @Mock lateinit var desksOrganizer: DesksOrganizer
     @Mock lateinit var transitions: Transitions
     @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
     @Mock lateinit var handler: Handler
@@ -128,6 +134,7 @@
                 transitions,
                 userRepositories,
                 shellTaskOrganizer,
+                desksOrganizer,
                 MAX_TASK_LIMIT,
                 interactionJankMonitor,
                 mContext,
@@ -148,6 +155,7 @@
                 transitions,
                 userRepositories,
                 shellTaskOrganizer,
+                desksOrganizer,
                 0,
                 interactionJankMonitor,
                 mContext,
@@ -163,6 +171,7 @@
                 transitions,
                 userRepositories,
                 shellTaskOrganizer,
+                desksOrganizer,
                 -5,
                 interactionJankMonitor,
                 mContext,
@@ -178,6 +187,7 @@
             transitions,
             userRepositories,
             shellTaskOrganizer,
+            desksOrganizer,
             maxTasksLimit = null,
             interactionJankMonitor,
             mContext,
@@ -394,7 +404,8 @@
     }
 
     @Test
-    fun addAndGetMinimizeTaskChanges_tasksWithinLimit_noTaskMinimized() {
+    @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun addAndGetMinimizeTaskChanges_tasksWithinLimit_multiDesksDisabled_noTaskMinimized() {
         desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
         desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
         (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
@@ -402,7 +413,7 @@
         val wct = WindowContainerTransaction()
         val minimizedTaskId =
             desktopTasksLimiter.addAndGetMinimizeTaskChanges(
-                displayId = DEFAULT_DISPLAY,
+                deskId = 0,
                 wct = wct,
                 newFrontTaskId = setUpFreeformTask().taskId,
             )
@@ -412,7 +423,27 @@
     }
 
     @Test
-    fun addAndGetMinimizeTaskChanges_tasksAboveLimit_backTaskMinimized() {
+    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun addAndGetMinimizeTaskChanges_tasksWithinLimit_multiDesksEnabled_noTaskMinimized() {
+        desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+        desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+        (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
+
+        val wct = WindowContainerTransaction()
+        val minimizedTaskId =
+            desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+                deskId = 0,
+                wct = wct,
+                newFrontTaskId = setUpFreeformTask().taskId,
+            )
+
+        assertThat(minimizedTaskId).isNull()
+        verify(desksOrganizer, never()).minimizeTask(eq(wct), eq(0), any())
+    }
+
+    @Test
+    @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun addAndGetMinimizeTaskChanges_tasksAboveLimit_multiDesksDisabled_backTaskMinimized() {
         desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
         desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
         // The following list will be ordered bottom -> top, as the last task is moved to top last.
@@ -421,7 +452,7 @@
         val wct = WindowContainerTransaction()
         val minimizedTaskId =
             desktopTasksLimiter.addAndGetMinimizeTaskChanges(
-                displayId = DEFAULT_DISPLAY,
+                deskId = DEFAULT_DISPLAY,
                 wct = wct,
                 newFrontTaskId = setUpFreeformTask().taskId,
             )
@@ -433,7 +464,28 @@
     }
 
     @Test
-    fun addAndGetMinimizeTaskChanges_nonMinimizedTasksWithinLimit_noTaskMinimized() {
+    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun addAndGetMinimizeTaskChanges_tasksAboveLimit_multiDesksEnabled_backTaskMinimized() {
+        desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+        desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+        // The following list will be ordered bottom -> top, as the last task is moved to top last.
+        val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
+
+        val wct = WindowContainerTransaction()
+        val minimizedTaskId =
+            desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+                deskId = DEFAULT_DISPLAY,
+                wct = wct,
+                newFrontTaskId = setUpFreeformTask().taskId,
+            )
+
+        assertThat(minimizedTaskId).isEqualTo(tasks.first().taskId)
+        verify(desksOrganizer).minimizeTask(wct, deskId = 0, tasks.first())
+    }
+
+    @Test
+    @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun addAndGetMinimizeTaskChanges_nonMinimizedTasksWithinLimit_multiDesksDisabled_noTaskMinimized() {
         desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
         desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
         val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
@@ -442,7 +494,7 @@
         val wct = WindowContainerTransaction()
         val minimizedTaskId =
             desktopTasksLimiter.addAndGetMinimizeTaskChanges(
-                displayId = 0,
+                deskId = 0,
                 wct = wct,
                 newFrontTaskId = setUpFreeformTask().taskId,
             )
@@ -452,6 +504,26 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun addAndGetMinimizeTaskChanges_nonMinimizedTasksWithinLimit_multiDesksEnabled_noTaskMinimized() {
+        desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+        desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
+        val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
+        desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = tasks[0].taskId)
+
+        val wct = WindowContainerTransaction()
+        val minimizedTaskId =
+            desktopTasksLimiter.addAndGetMinimizeTaskChanges(
+                deskId = 0,
+                wct = wct,
+                newFrontTaskId = setUpFreeformTask().taskId,
+            )
+
+        assertThat(minimizedTaskId).isNull()
+        verify(desksOrganizer, never()).minimizeTask(eq(wct), eq(0), any())
+    }
+
+    @Test
     fun getTaskToMinimize_tasksWithinLimit_returnsNull() {
         desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
         desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
@@ -485,6 +557,7 @@
                 transitions,
                 userRepositories,
                 shellTaskOrganizer,
+                desksOrganizer,
                 MAX_TASK_LIMIT2,
                 interactionJankMonitor,
                 mContext,
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 fd8842b..5ef1ace 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
@@ -22,7 +22,6 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
-import android.os.Binder
 import android.os.IBinder
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
@@ -30,7 +29,6 @@
 import android.view.WindowManager
 import android.view.WindowManager.TRANSIT_CLOSE
 import android.view.WindowManager.TRANSIT_OPEN
-import android.view.WindowManager.TRANSIT_PIP
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.IWindowContainerToken
@@ -41,22 +39,18 @@
 import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
 import com.android.modules.utils.testing.ExtendedMockitoRule
 import com.android.window.flags.Flags
-import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP
 import com.android.wm.shell.MockToken
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.back.BackAnimationController
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
-import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
-import com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP
-import com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP
-import com.android.wm.shell.util.StubTransaction
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import java.util.Optional
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -92,10 +86,10 @@
     private val userRepositories = mock<DesktopUserRepositories>()
     private val taskRepository = mock<DesktopRepository>()
     private val mixedHandler = mock<DesktopMixedTransitionHandler>()
+    private val pipTransitionObserver = mock<DesktopPipTransitionObserver>()
     private val backAnimationController = mock<BackAnimationController>()
     private val desktopWallpaperActivityTokenProvider =
         mock<DesktopWallpaperActivityTokenProvider>()
-    private val desksTransitionObserver = mock<DesksTransitionObserver>()
     private val wallpaperToken = MockToken().token()
 
     private lateinit var transitionObserver: DesktopTasksTransitionObserver
@@ -117,9 +111,9 @@
                 transitions,
                 shellTaskOrganizer,
                 mixedHandler,
+                Optional.of(pipTransitionObserver),
                 backAnimationController,
                 desktopWallpaperActivityTokenProvider,
-                desksTransitionObserver,
                 shellInit,
             )
     }
@@ -340,6 +334,50 @@
     }
 
     @Test
+    @EnableFlags(
+        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+        Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+    )
+    fun nonTopTransparentTaskOpened_clearTopTransparentTaskIdFromRepository() {
+        val mockTransition = Mockito.mock(IBinder::class.java)
+        val topTransparentTask = createTaskInfo(1)
+        val nonTopTransparentTask = createTaskInfo(2)
+        whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+            .thenReturn(topTransparentTask.taskId)
+
+        transitionObserver.onTransitionReady(
+            transition = mockTransition,
+            info = createOpenChangeTransition(nonTopTransparentTask),
+            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 nonTopTransparentTaskSentToFront_clearTopTransparentTaskIdFromRepository() {
+        val mockTransition = Mockito.mock(IBinder::class.java)
+        val topTransparentTask = createTaskInfo(1)
+        val nonTopTransparentTask = createTaskInfo(2)
+        whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+            .thenReturn(topTransparentTask.taskId)
+
+        transitionObserver.onTransitionReady(
+            transition = mockTransition,
+            info = createToFrontTransition(nonTopTransparentTask),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+    }
+
+    @Test
     fun transitCloseWallpaper_wallpaperActivityVisibilitySaved() {
         val wallpaperTask = createWallpaperTaskInfo()
 
@@ -353,71 +391,6 @@
         verify(desktopWallpaperActivityTokenProvider).removeToken(wallpaperTask.displayId)
     }
 
-    @Test
-    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
-    fun pendingPipTransitionAborted_taskRepositoryOnPipAbortedInvoked() {
-        val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM)
-        val pipTransition = Binder()
-        whenever(taskRepository.isTaskMinimizedPipInDisplay(any(), any())).thenReturn(true)
-
-        transitionObserver.onTransitionReady(
-            transition = pipTransition,
-            info = createOpenChangeTransition(task, type = TRANSIT_PIP),
-            startTransaction = mock(),
-            finishTransaction = mock(),
-        )
-        transitionObserver.onTransitionFinished(transition = pipTransition, aborted = true)
-
-        verify(taskRepository).onPipAborted(task.displayId, task.taskId)
-    }
-
-    @Test
-    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
-    fun exitPipTransition_taskRepositoryClearTaskInPip() {
-        val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM)
-        whenever(taskRepository.isTaskMinimizedPipInDisplay(any(), any())).thenReturn(true)
-
-        transitionObserver.onTransitionReady(
-            transition = mock(),
-            info = createOpenChangeTransition(task, type = TRANSIT_EXIT_PIP),
-            startTransaction = mock(),
-            finishTransaction = mock(),
-        )
-
-        verify(taskRepository).setTaskInPip(task.displayId, task.taskId, enterPip = false)
-    }
-
-    @Test
-    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
-    fun removePipTransition_taskRepositoryClearTaskInPip() {
-        val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM)
-        whenever(taskRepository.isTaskMinimizedPipInDisplay(any(), any())).thenReturn(true)
-
-        transitionObserver.onTransitionReady(
-            transition = mock(),
-            info = createOpenChangeTransition(task, type = TRANSIT_REMOVE_PIP),
-            startTransaction = mock(),
-            finishTransaction = mock(),
-        )
-
-        verify(taskRepository).setTaskInPip(task.displayId, task.taskId, enterPip = false)
-    }
-
-    @Test
-    fun onTransitionReady_forwardsToDesksTransitionObserver() {
-        val transition = Binder()
-        val info = TransitionInfo(TRANSIT_CLOSE, /* flags= */ 0)
-
-        transitionObserver.onTransitionReady(
-            transition = transition,
-            info = info,
-            StubTransaction(),
-            StubTransaction(),
-        )
-
-        verify(desksTransitionObserver).onTransitionReady(transition, info)
-    }
-
     private fun createBackNavigationTransition(
         task: RunningTaskInfo?,
         type: Int = TRANSIT_TO_BACK,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index c00083b..b511fc3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -22,6 +22,7 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
 import android.content.ComponentName
 import android.graphics.Rect
 import android.view.Display.DEFAULT_DISPLAY
@@ -41,6 +42,18 @@
             .setActivityType(ACTIVITY_TYPE_STANDARD)
             .setWindowingMode(WINDOWING_MODE_FREEFORM)
             .setLastActiveTime(100)
+            .setUserId(DEFAULT_USER_ID)
+            .apply { bounds?.let { setBounds(it) } }
+            .build()
+
+    fun createPinnedTask(displayId: Int = DEFAULT_DISPLAY, bounds: Rect? = null): RunningTaskInfo =
+        TestRunningTaskInfoBuilder()
+            .setDisplayId(displayId)
+            .setParentTaskId(displayId)
+            .setToken(MockToken().token())
+            .setActivityType(ACTIVITY_TYPE_STANDARD)
+            .setWindowingMode(WINDOWING_MODE_PINNED)
+            .setLastActiveTime(100)
             .apply { bounds?.let { setBounds(it) } }
             .build()
 
@@ -50,6 +63,7 @@
             .setToken(MockToken().token())
             .setActivityType(ACTIVITY_TYPE_STANDARD)
             .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+            .setUserId(DEFAULT_USER_ID)
             .setLastActiveTime(100)
 
     /** Create a task that has windowing mode set to [WINDOWING_MODE_FULLSCREEN] */
@@ -63,6 +77,7 @@
             .setToken(MockToken().token())
             .setActivityType(ACTIVITY_TYPE_STANDARD)
             .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+            .setUserId(DEFAULT_USER_ID)
             .setLastActiveTime(100)
             .build()
 
@@ -72,6 +87,7 @@
             .setToken(MockToken().token())
             .setActivityType(ACTIVITY_TYPE_HOME)
             .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+            .setUserId(DEFAULT_USER_ID)
             .setLastActiveTime(100)
             .build()
 
@@ -91,4 +107,6 @@
         createSystemModalTask().apply {
             baseActivity = ComponentName("com.test.dummypackage", "TestClass")
         }
+
+    const val DEFAULT_USER_ID = 10
 }
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 0871d38..4e2994c 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
@@ -26,6 +26,7 @@
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.window.flags.Flags
+import com.android.window.flags.Flags.FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -34,6 +35,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.CancelState
 import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
@@ -52,16 +54,19 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyFloat
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.MockitoSession
+import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.argThat
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
 
@@ -82,8 +87,17 @@
     @Mock private lateinit var desktopUserRepositories: DesktopUserRepositories
     @Mock private lateinit var bubbleController: BubbleController
     @Mock private lateinit var visualIndicator: DesktopModeVisualIndicator
+    @Mock private lateinit var dragCancelCallback: Runnable
+    @Mock
+    private lateinit var dragToDesktopStateListener:
+        DragToDesktopTransitionHandler.DragToDesktopStateListener
 
-    private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
+    private val transactionSupplier = Supplier {
+        val transaction = mock<SurfaceControl.Transaction>()
+        whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction)
+        whenever(transaction.setFrameTimeline(anyLong())).thenReturn(transaction)
+        transaction
+    }
 
     private lateinit var defaultHandler: DragToDesktopTransitionHandler
     private lateinit var springHandler: SpringDragToDesktopTransitionHandler
@@ -101,7 +115,11 @@
                     Optional.of(bubbleController),
                     transactionSupplier,
                 )
-                .apply { setSplitScreenController(splitScreenController) }
+                .apply {
+                    setSplitScreenController(splitScreenController)
+                    dragToDesktopStateListener =
+                        this@DragToDesktopTransitionHandlerTest.dragToDesktopStateListener
+                }
         springHandler =
             SpringDragToDesktopTransitionHandler(
                     context,
@@ -112,12 +130,24 @@
                     Optional.of(bubbleController),
                     transactionSupplier,
                 )
-                .apply { setSplitScreenController(splitScreenController) }
+                .apply {
+                    setSplitScreenController(splitScreenController)
+                    dragToDesktopStateListener =
+                        this@DragToDesktopTransitionHandlerTest.dragToDesktopStateListener
+                }
         mockitoSession =
             ExtendedMockito.mockitoSession()
                 .strictness(Strictness.LENIENT)
                 .mockStatic(SystemProperties::class.java)
                 .startMocking()
+        whenever(
+                transitions.startTransition(
+                    eq(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP),
+                    /* wct= */ any(),
+                    eq(defaultHandler),
+                )
+            )
+            .thenReturn(mock<IBinder>())
     }
 
     @After
@@ -427,7 +457,7 @@
         )
 
         // No need to animate the cancel since the start animation couldn't even start.
-        verifyZeroInteractions(dragAnimator)
+        verifyNoMoreInteractions(dragAnimator)
     }
 
     @Test
@@ -478,7 +508,7 @@
         )
 
         // Should NOT have any transaction changes
-        verifyZeroInteractions(mergedStartTransaction)
+        verifyNoMoreInteractions(mergedStartTransaction)
         // Should NOT merge animation
         verify(finishCallback, never()).onTransitionFinished(any())
     }
@@ -679,17 +709,11 @@
         val startTransition = startDrag(defaultHandler, task)
         val endTransition = mock<IBinder>()
         defaultHandler.onTaskResizeAnimationListener = mock()
-        defaultHandler.mergeAnimation(
+        mergeAnimation(
             transition = endTransition,
-            info =
-                createTransitionInfo(
-                    type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
-                    draggedTask = task,
-                ),
-            startT = mock<SurfaceControl.Transaction>(),
-            finishT = mock<SurfaceControl.Transaction>(),
+            type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
+            task = task,
             mergeTarget = startTransition,
-            finishCallback = mock<Transitions.TransitionFinishCallback>(),
         )
 
         defaultHandler.onTransitionConsumed(endTransition, aborted = true, mock())
@@ -701,6 +725,185 @@
     }
 
     @Test
+    @DisableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun mergeOtherTransition_flagDisabled_cancelAndEndNotYetRequested_doesNotInterruptStartDrag() {
+        val finishCallback = mock<Transitions.TransitionFinishCallback>()
+        val task = createTask()
+        defaultHandler.onTaskResizeAnimationListener = mock()
+        val startTransition = startDrag(defaultHandler, task, finishCallback = finishCallback)
+
+        mergeInterruptingTransition(mergeTarget = startTransition)
+
+        verify(finishCallback, never()).onTransitionFinished(anyOrNull())
+        verify(dragAnimator, never()).cancelAnimator()
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun mergeOtherTransition_cancelAndEndNotYetRequested_interruptsStartDrag() {
+        val finishCallback = mock<Transitions.TransitionFinishCallback>()
+        val task = createTask()
+        defaultHandler.onTaskResizeAnimationListener = mock()
+        val startTransition = startDrag(defaultHandler, task, finishCallback = finishCallback)
+
+        mergeInterruptingTransition(mergeTarget = startTransition)
+
+        verify(dragAnimator).cancelAnimator()
+        verify(dragCancelCallback).run()
+        verify(dragToDesktopStateListener).onTransitionInterrupted()
+        assertThat(defaultHandler.inProgress).isTrue()
+        // Doesn't finish start transition yet
+        verify(finishCallback, never()).onTransitionFinished(/* wct= */ anyOrNull())
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun mergeOtherTransition_cancelAndEndNotYetRequested_finishesStartAfterAnimation() {
+        val finishCallback = mock<Transitions.TransitionFinishCallback>()
+        val task = createTask()
+        defaultHandler.onTaskResizeAnimationListener = mock()
+        val startTransition = startDrag(defaultHandler, task, finishCallback = finishCallback)
+
+        mergeInterruptingTransition(mergeTarget = startTransition)
+        mAnimatorTestRule.advanceTimeBy(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+
+        verify(finishCallback).onTransitionFinished(/* wct= */ anyOrNull())
+        assertThat(defaultHandler.inProgress).isFalse()
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun mergeOtherTransition_endDragAlreadyMerged_doesNotInterruptStartDrag() {
+        val startDragFinishCallback = mock<Transitions.TransitionFinishCallback>()
+        val task = createTask()
+        val startTransition =
+            startDrag(defaultHandler, task, finishCallback = startDragFinishCallback)
+        defaultHandler.onTaskResizeAnimationListener = mock()
+        mergeAnimation(
+            type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
+            task = task,
+            mergeTarget = startTransition,
+        )
+
+        mergeInterruptingTransition(mergeTarget = startTransition)
+
+        verify(startDragFinishCallback, never()).onTransitionFinished(anyOrNull())
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun startEndAnimation_otherTransitionInterruptedStartAfterEndRequest_finishImmediately() {
+        val task1 = createTask()
+        val startTransition = startDrag(defaultHandler, task1)
+        val endTransition =
+            defaultHandler.finishDragToDesktopTransition(WindowContainerTransaction())
+        val startTransaction = mock<SurfaceControl.Transaction>()
+        val endDragFinishCallback = mock<Transitions.TransitionFinishCallback>()
+        defaultHandler.onTaskResizeAnimationListener = mock()
+        mergeInterruptingTransition(mergeTarget = startTransition)
+
+        val didAnimate =
+            defaultHandler.startAnimation(
+                transition = requireNotNull(endTransition),
+                info =
+                    createTransitionInfo(
+                        type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
+                        draggedTask = task1,
+                    ),
+                startTransaction = startTransaction,
+                finishTransaction = mock(),
+                finishCallback = endDragFinishCallback,
+            )
+
+        assertThat(didAnimate).isTrue()
+        verify(startTransaction).apply()
+        verify(endDragFinishCallback).onTransitionFinished(anyOrNull())
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun startDrag_otherTransitionInterruptedStartAfterEndRequested_animatesDragWhenReady() {
+        val task1 = createTask()
+        val startTransition = startDrag(defaultHandler, task1)
+        verify(dragAnimator).startAnimation()
+        val endTransition =
+            defaultHandler.finishDragToDesktopTransition(WindowContainerTransaction())
+        defaultHandler.onTaskResizeAnimationListener = mock()
+        mergeInterruptingTransition(mergeTarget = startTransition)
+        defaultHandler.startAnimation(
+            transition = requireNotNull(endTransition),
+            info =
+                createTransitionInfo(
+                    type = TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP,
+                    draggedTask = task1,
+                ),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+            finishCallback = mock(),
+        )
+
+        startDrag(defaultHandler, createTask())
+
+        verify(dragAnimator, times(2)).startAnimation()
+    }
+
+    @Test
+    @EnableFlags(FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX)
+    fun startCancelAnimation_otherTransitionInterruptingAfterCancelRequest_finishImmediately() {
+        val task1 = createTask()
+        val startTransition = startDrag(defaultHandler, task1)
+        val cancelTransition =
+            cancelDragToDesktopTransition(defaultHandler, CancelState.STANDARD_CANCEL)
+        mergeInterruptingTransition(mergeTarget = startTransition)
+        val cancelFinishCallback = mock<Transitions.TransitionFinishCallback>()
+        val startTransaction = mock<SurfaceControl.Transaction>()
+
+        val didAnimate =
+            defaultHandler.startAnimation(
+                transition = requireNotNull(cancelTransition),
+                info =
+                    createTransitionInfo(
+                        type = TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP,
+                        draggedTask = task1,
+                    ),
+                startTransaction = startTransaction,
+                finishTransaction = mock(),
+                finishCallback = cancelFinishCallback,
+            )
+
+        assertThat(didAnimate).isTrue()
+        verify(startTransaction).apply()
+        verify(cancelFinishCallback).onTransitionFinished(/* wct= */ anyOrNull())
+    }
+
+    private fun mergeInterruptingTransition(mergeTarget: IBinder) {
+        defaultHandler.mergeAnimation(
+            transition = mock<IBinder>(),
+            info = createTransitionInfo(type = TRANSIT_OPEN, draggedTask = createTask()),
+            startT = mock(),
+            finishT = mock(),
+            mergeTarget = mergeTarget,
+            finishCallback = mock(),
+        )
+    }
+
+    private fun mergeAnimation(
+        transition: IBinder = mock(),
+        type: Int,
+        mergeTarget: IBinder,
+        task: RunningTaskInfo,
+    ) {
+        defaultHandler.mergeAnimation(
+            transition = transition,
+            info = createTransitionInfo(type = type, draggedTask = task),
+            startT = mock(),
+            finishT = mock(),
+            mergeTarget = mergeTarget,
+            finishCallback = mock(),
+        )
+    }
+
+    @Test
     fun getAnimationFraction_returnsFraction() {
         val fraction =
             SpringDragToDesktopTransitionHandler.getAnimationFraction(
@@ -785,6 +988,7 @@
         finishTransaction: SurfaceControl.Transaction = mock(),
         homeChange: TransitionInfo.Change? = createHomeChange(),
         transitionRootLeash: SurfaceControl = mock(),
+        finishCallback: Transitions.TransitionFinishCallback = mock(),
     ): IBinder {
         whenever(dragAnimator.position).thenReturn(PointF())
         // Simulate transition is started and is ready to animate.
@@ -800,7 +1004,7 @@
                 ),
             startTransaction = startTransaction,
             finishTransaction = finishTransaction,
-            finishCallback = {},
+            finishCallback = finishCallback,
         )
         return transition
     }
@@ -819,7 +1023,12 @@
                 )
             )
             .thenReturn(token)
-        handler.startDragToDesktopTransition(task, dragAnimator, visualIndicator)
+        handler.startDragToDesktopTransition(
+            task,
+            dragAnimator,
+            visualIndicator,
+            dragCancelCallback,
+        )
         return token
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
index c7518d5..3983bfb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
@@ -54,7 +54,7 @@
 import org.mockito.kotlin.never
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 /**
@@ -111,7 +111,7 @@
                 eq(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR),
             )
         // Assert fadeIn, fadeOut, and animateIndicatorType were not called.
-        verifyZeroInteractions(spyViewContainer)
+        verifyNoMoreInteractions(spyViewContainer)
     }
 
     @Test
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 7560945..dc973d0 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
@@ -90,7 +90,7 @@
         whenever(packageManager.getHomeActivities(ArrayList())).thenReturn(componentName)
         desktopModeCompatPolicy = DesktopModeCompatPolicy(spyContext)
         transitionHandler = createTransitionHandler()
-        allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+        allowOverlayPermissionForAllUsers(arrayOf(SYSTEM_ALERT_WINDOW))
     }
 
     private fun createTransitionHandler() =
@@ -200,10 +200,16 @@
             .isTrue()
     }
 
-    fun allowOverlayPermission(permissions: Array<String>) {
+    fun allowOverlayPermissionForAllUsers(permissions: Array<String>) {
         val packageInfo = mock<PackageInfo>()
         packageInfo.requestedPermissions = permissions
-        whenever(packageManager.getPackageInfo(anyString(), eq(PackageManager.GET_PERMISSIONS)))
+        whenever(
+                packageManager.getPackageInfoAsUser(
+                    anyString(),
+                    eq(PackageManager.GET_PERMISSIONS),
+                    anyInt(),
+                )
+            )
             .thenReturn(packageInfo)
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
index 96b85ad..9af5047 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
@@ -15,20 +15,25 @@
  */
 package com.android.wm.shell.desktopmode.multidesks
 
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
 import android.testing.AndroidTestingRunner
 import android.view.Display
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.TransitionInfo
+import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction
 import android.window.WindowContainerTransaction.Change
 import android.window.WindowContainerTransaction.HierarchyOp
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
 import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTaskOrganizer.TaskListener
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.common.LaunchAdjacentController
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
 import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer.DeskMinimizationRoot
 import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer.DeskRoot
@@ -36,14 +41,17 @@
 import com.android.wm.shell.sysui.ShellInit
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertNotNull
+import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mockito
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.argThat
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 /**
  * Tests for [RootTaskDesksOrganizer].
@@ -58,57 +66,32 @@
     private val testShellInit = ShellInit(testExecutor)
     private val mockShellCommandHandler = mock<ShellCommandHandler>()
     private val mockShellTaskOrganizer = mock<ShellTaskOrganizer>()
+    private val launchAdjacentController = LaunchAdjacentController(mock())
 
     private lateinit var organizer: RootTaskDesksOrganizer
 
     @Before
     fun setUp() {
         organizer =
-            RootTaskDesksOrganizer(testShellInit, mockShellCommandHandler, mockShellTaskOrganizer)
+            RootTaskDesksOrganizer(
+                testShellInit,
+                mockShellCommandHandler,
+                mockShellTaskOrganizer,
+                launchAdjacentController,
+            )
     }
 
-    @Test
-    fun testCreateDesk_callsBack() {
-        val callback = FakeOnCreateCallback()
-        organizer.createDesk(Display.DEFAULT_DISPLAY, callback)
-
-        val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
-        organizer.onTaskAppeared(freeformRoot, SurfaceControl())
-
-        assertThat(callback.created).isTrue()
-        assertEquals(freeformRoot.taskId, callback.deskId)
-    }
+    @Test fun testCreateDesk_createsDeskAndMinimizationRoots() = runTest { createDesk() }
 
     @Test
-    fun testCreateDesk_createsMinimizationRoot() {
-        val callback = FakeOnCreateCallback()
-        organizer.createDesk(Display.DEFAULT_DISPLAY, callback)
-        val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
-        organizer.onTaskAppeared(freeformRoot, SurfaceControl())
-
-        val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
-        organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
-
-        val minimizationRoot = organizer.deskMinimizationRootsByDeskId[freeformRoot.taskId]
-        assertNotNull(minimizationRoot)
-        assertThat(minimizationRoot.deskId).isEqualTo(freeformRoot.taskId)
-        assertThat(minimizationRoot.rootId).isEqualTo(minimizationRootTask.taskId)
-    }
-
-    @Test
-    fun testCreateMinimizationRoot_marksHidden() {
-        organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
-        val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
-        organizer.onTaskAppeared(freeformRoot, SurfaceControl())
-
-        val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
-        organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+    fun testCreateMinimizationRoot_marksHidden() = runTest {
+        val desk = createDesk()
 
         verify(mockShellTaskOrganizer)
             .applyTransaction(
                 argThat { wct ->
                     wct.changes.any { change ->
-                        change.key == minimizationRootTask.token.asBinder() &&
+                        change.key == desk.minimizationRoot.token.asBinder() &&
                             (change.value.changeMask and Change.CHANGE_HIDDEN != 0) &&
                             change.value.hidden
                     }
@@ -117,7 +100,7 @@
     }
 
     @Test
-    fun testOnTaskAppeared_withoutRequest_throws() {
+    fun testOnTaskAppeared_withoutRequest_throws() = runTest {
         val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
 
         assertThrows(Exception::class.java) {
@@ -126,41 +109,25 @@
     }
 
     @Test
-    fun testOnTaskAppeared_withRequestOnlyInAnotherDisplay_throws() {
-        organizer.createDesk(displayId = 2, FakeOnCreateCallback())
-        val freeformRoot = createFreeformTask(Display.DEFAULT_DISPLAY).apply { parentTaskId = -1 }
+    fun testOnTaskAppeared_duplicateRoot_throws() = runTest {
+        val desk = createDesk()
 
         assertThrows(Exception::class.java) {
-            organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+            organizer.onTaskAppeared(desk.deskRoot.taskInfo, SurfaceControl())
         }
     }
 
     @Test
-    fun testOnTaskAppeared_duplicateRoot_throws() {
-        organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
-        val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
-        organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+    fun testOnTaskAppeared_duplicateMinimizedRoot_throws() = runTest {
+        val desk = createDesk()
 
         assertThrows(Exception::class.java) {
-            organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+            organizer.onTaskAppeared(desk.minimizationRoot.taskInfo, SurfaceControl())
         }
     }
 
     @Test
-    fun testOnTaskAppeared_duplicateMinimizedRoot_throws() {
-        organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
-        val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
-        val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
-        organizer.onTaskAppeared(freeformRoot, SurfaceControl())
-        organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
-
-        assertThrows(Exception::class.java) {
-            organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
-        }
-    }
-
-    @Test
-    fun testOnTaskVanished_removesRoot() {
+    fun testOnTaskVanished_removesRoot() = runTest {
         val desk = createDesk()
 
         organizer.onTaskVanished(desk.deskRoot.taskInfo)
@@ -169,7 +136,7 @@
     }
 
     @Test
-    fun testOnTaskVanished_removesMinimizedRoot() {
+    fun testOnTaskVanished_removesMinimizedRoot() = runTest {
         val desk = createDesk()
 
         organizer.onTaskVanished(desk.deskRoot.taskInfo)
@@ -179,7 +146,7 @@
     }
 
     @Test
-    fun testDesktopWindowAppearsInDesk() {
+    fun testDesktopWindowAppearsInDesk() = runTest {
         val desk = createDesk()
         val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
 
@@ -189,7 +156,7 @@
     }
 
     @Test
-    fun testDesktopWindowAppearsInDeskMinimizationRoot() {
+    fun testDesktopWindowAppearsInDeskMinimizationRoot() = runTest {
         val desk = createDesk()
         val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
 
@@ -199,7 +166,7 @@
     }
 
     @Test
-    fun testDesktopWindowMovesToMinimizationRoot() {
+    fun testDesktopWindowMovesToMinimizationRoot() = runTest {
         val desk = createDesk()
         val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
         organizer.onTaskAppeared(child, SurfaceControl())
@@ -212,7 +179,7 @@
     }
 
     @Test
-    fun testDesktopWindowDisappearsFromDesk() {
+    fun testDesktopWindowDisappearsFromDesk() = runTest {
         val desk = createDesk()
         val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
 
@@ -223,7 +190,7 @@
     }
 
     @Test
-    fun testDesktopWindowDisappearsFromDeskMinimizationRoot() {
+    fun testDesktopWindowDisappearsFromDeskMinimizationRoot() = runTest {
         val desk = createDesk()
         val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
 
@@ -234,7 +201,7 @@
     }
 
     @Test
-    fun testRemoveDesk_removesDeskRoot() {
+    fun testRemoveDesk_removesDeskRoot() = runTest {
         val desk = createDesk()
 
         val wct = WindowContainerTransaction()
@@ -250,7 +217,7 @@
     }
 
     @Test
-    fun testRemoveDesk_removesMinimizationRoot() {
+    fun testRemoveDesk_removesMinimizationRoot() = runTest {
         val desk = createDesk()
 
         val wct = WindowContainerTransaction()
@@ -266,7 +233,7 @@
     }
 
     @Test
-    fun testActivateDesk() {
+    fun testActivateDesk() = runTest {
         val desk = createDesk()
 
         val wct = WindowContainerTransaction()
@@ -290,7 +257,7 @@
     }
 
     @Test
-    fun testActivateDesk_didNotExist_throws() {
+    fun testActivateDesk_didNotExist_throws() = runTest {
         val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
 
         val wct = WindowContainerTransaction()
@@ -298,7 +265,7 @@
     }
 
     @Test
-    fun testMoveTaskToDesk() {
+    fun testMoveTaskToDesk() = runTest {
         val desk = createDesk()
 
         val desktopTask = createFreeformTask().apply { parentTaskId = -1 }
@@ -324,7 +291,7 @@
     }
 
     @Test
-    fun testMoveTaskToDesk_didNotExist_throws() {
+    fun testMoveTaskToDesk_didNotExist_throws() = runTest {
         val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
 
         val desktopTask = createFreeformTask().apply { parentTaskId = -1 }
@@ -335,7 +302,7 @@
     }
 
     @Test
-    fun testGetDeskAtEnd() {
+    fun testGetDeskAtEnd() = runTest {
         val desk = createDesk()
 
         val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
@@ -348,7 +315,7 @@
     }
 
     @Test
-    fun testGetDeskAtEnd_inMinimizationRoot() {
+    fun testGetDeskAtEnd_inMinimizationRoot() = runTest {
         val desk = createDesk()
 
         val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
@@ -361,27 +328,24 @@
     }
 
     @Test
-    fun testIsDeskActiveAtEnd() {
-        organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
-        val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
-        freeformRoot.isVisibleRequested = true
-        organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+    fun testIsDeskActiveAtEnd() = runTest {
+        val desk = createDesk()
 
         val isActive =
             organizer.isDeskActiveAtEnd(
                 change =
-                    TransitionInfo.Change(freeformRoot.token, SurfaceControl()).apply {
-                        taskInfo = freeformRoot
+                    TransitionInfo.Change(desk.deskRoot.token, SurfaceControl()).apply {
+                        taskInfo = desk.deskRoot.taskInfo
                         mode = TRANSIT_TO_FRONT
                     },
-                deskId = freeformRoot.taskId,
+                deskId = desk.deskRoot.deskId,
             )
 
         assertThat(isActive).isTrue()
     }
 
     @Test
-    fun deactivateDesk_clearsLaunchRoot() {
+    fun deactivateDesk_clearsLaunchRoot() = runTest {
         val wct = WindowContainerTransaction()
         val desk = createDesk()
         organizer.activateDesk(wct, desk.deskRoot.deskId)
@@ -400,7 +364,7 @@
     }
 
     @Test
-    fun isDeskChange_forDeskId() {
+    fun isDeskChange_forDeskId() = runTest {
         val desk = createDesk()
 
         assertThat(
@@ -415,7 +379,7 @@
     }
 
     @Test
-    fun isDeskChange_forDeskId_inMinimizationRoot() {
+    fun isDeskChange_forDeskId_inMinimizationRoot() = runTest {
         val desk = createDesk()
 
         assertThat(
@@ -433,7 +397,7 @@
     }
 
     @Test
-    fun isDeskChange_anyDesk() {
+    fun isDeskChange_anyDesk() = runTest {
         val desk = createDesk()
 
         assertThat(
@@ -447,7 +411,7 @@
     }
 
     @Test
-    fun isDeskChange_anyDesk_inMinimizationRoot() {
+    fun isDeskChange_anyDesk_inMinimizationRoot() = runTest {
         val desk = createDesk()
 
         assertThat(
@@ -464,7 +428,7 @@
     }
 
     @Test
-    fun minimizeTask() {
+    fun minimizeTask() = runTest {
         val desk = createDesk()
         val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
         val wct = WindowContainerTransaction()
@@ -473,18 +437,11 @@
 
         organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
 
-        assertThat(
-                wct.hierarchyOps.any { hop ->
-                    hop.isReparent &&
-                        hop.container == task.token.asBinder() &&
-                        hop.newParent == desk.minimizationRoot.token.asBinder()
-                }
-            )
-            .isTrue()
+        assertThat(wct.hasMinimizationHops(desk, task.token)).isTrue()
     }
 
     @Test
-    fun minimizeTask_alreadyMinimized_noOp() {
+    fun minimizeTask_alreadyMinimized_noOp() = runTest {
         val desk = createDesk()
         val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
         val wct = WindowContainerTransaction()
@@ -496,7 +453,7 @@
     }
 
     @Test
-    fun minimizeTask_inDifferentDesk_noOp() {
+    fun minimizeTask_inDifferentDesk_noOp() = runTest {
         val desk = createDesk()
         val otherDesk = createDesk()
         val task = createFreeformTask().apply { parentTaskId = otherDesk.deskRoot.deskId }
@@ -508,30 +465,251 @@
         assertThat(wct.isEmpty).isTrue()
     }
 
+    @Test
+    fun unminimizeTask() = runTest {
+        val desk = createDesk()
+        val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+        val wct = WindowContainerTransaction()
+        organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, task)
+        organizer.onTaskAppeared(task, SurfaceControl())
+        organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+        task.parentTaskId = desk.minimizationRoot.rootId
+        organizer.onTaskInfoChanged(task)
+
+        wct.clear()
+        organizer.unminimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+        assertThat(wct.hasUnminimizationHops(desk, task.token)).isTrue()
+    }
+
+    @Test
+    fun unminimizeTask_alreadyUnminimized_noOp() = runTest {
+        val desk = createDesk()
+        val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+        val wct = WindowContainerTransaction()
+        organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, task)
+        organizer.onTaskAppeared(task, SurfaceControl())
+
+        wct.clear()
+        organizer.unminimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+        assertThat(wct.hasUnminimizationHops(desk, task.token)).isFalse()
+    }
+
+    @Test
+    fun unminimizeTask_notInDesk_noOp() = runTest {
+        val desk = createDesk()
+        val task = createFreeformTask()
+        val wct = WindowContainerTransaction()
+
+        organizer.unminimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+        assertThat(wct.hasUnminimizationHops(desk, task.token)).isFalse()
+    }
+
+    @Test
+    fun reorderTaskToFront() = runTest {
+        val desk = createDesk()
+        val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+        val wct = WindowContainerTransaction()
+        organizer.onTaskAppeared(task, SurfaceControl())
+
+        organizer.reorderTaskToFront(wct, desk.deskRoot.deskId, task)
+
+        assertThat(
+                wct.hierarchyOps.singleOrNull { hop ->
+                    hop.container == task.token.asBinder() &&
+                        hop.type == HIERARCHY_OP_TYPE_REORDER &&
+                        hop.toTop &&
+                        hop.includingParents()
+                }
+            )
+            .isNotNull()
+    }
+
+    @Test
+    fun reorderTaskToFront_notInDesk_noOp() = runTest {
+        val desk = createDesk()
+        val task = createFreeformTask()
+        val wct = WindowContainerTransaction()
+
+        organizer.reorderTaskToFront(wct, desk.deskRoot.deskId, task)
+
+        assertThat(
+                wct.hierarchyOps.singleOrNull { hop ->
+                    hop.container == task.token.asBinder() &&
+                        hop.type == HIERARCHY_OP_TYPE_REORDER &&
+                        hop.toTop &&
+                        hop.includingParents()
+                }
+            )
+            .isNull()
+    }
+
+    @Test
+    fun reorderTaskToFront_minimized_unminimizesAndReorders() = runTest {
+        val desk = createDesk()
+        val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+        val wct = WindowContainerTransaction()
+        organizer.onTaskAppeared(task, SurfaceControl())
+        task.parentTaskId = desk.minimizationRoot.rootId
+        organizer.onTaskInfoChanged(task)
+
+        organizer.reorderTaskToFront(wct, desk.deskRoot.deskId, task)
+
+        assertThat(wct.hasUnminimizationHops(desk, task.token)).isTrue()
+        assertThat(
+                wct.hierarchyOps.singleOrNull { hop ->
+                    hop.container == task.token.asBinder() &&
+                        hop.type == HIERARCHY_OP_TYPE_REORDER &&
+                        hop.toTop &&
+                        hop.includingParents()
+                }
+            )
+            .isNotNull()
+    }
+
+    @Test
+    fun onTaskAppeared_visibleDesk_onlyDesk_disablesLaunchAdjacent() = runTest {
+        launchAdjacentController.launchAdjacentEnabled = true
+
+        createDesk(visible = true)
+
+        assertThat(launchAdjacentController.launchAdjacentEnabled).isFalse()
+    }
+
+    @Test
+    fun onTaskAppeared_invisibleDesk_onlyDesk_enablesLaunchAdjacent() = runTest {
+        launchAdjacentController.launchAdjacentEnabled = false
+
+        createDesk(visible = false)
+
+        assertThat(launchAdjacentController.launchAdjacentEnabled).isTrue()
+    }
+
+    @Test
+    fun onTaskAppeared_invisibleDesk_otherVisibleDesk_disablesLaunchAdjacent() = runTest {
+        launchAdjacentController.launchAdjacentEnabled = true
+
+        createDesk(visible = true)
+        createDesk(visible = false)
+
+        assertThat(launchAdjacentController.launchAdjacentEnabled).isFalse()
+    }
+
+    @Test
+    fun onTaskInfoChanged_deskBecomesVisible_onlyDesk_disablesLaunchAdjacent() = runTest {
+        launchAdjacentController.launchAdjacentEnabled = true
+
+        val desk = createDesk(visible = false)
+        desk.deskRoot.taskInfo.isVisible = true
+        organizer.onTaskInfoChanged(desk.deskRoot.taskInfo)
+
+        assertThat(launchAdjacentController.launchAdjacentEnabled).isFalse()
+    }
+
+    @Test
+    fun onTaskInfoChanged_deskBecomesInvisible_onlyDesk_enablesLaunchAdjacent() = runTest {
+        launchAdjacentController.launchAdjacentEnabled = false
+
+        val desk = createDesk(visible = true)
+        desk.deskRoot.taskInfo.isVisible = false
+        organizer.onTaskInfoChanged(desk.deskRoot.taskInfo)
+
+        assertThat(launchAdjacentController.launchAdjacentEnabled).isTrue()
+    }
+
+    @Test
+    fun onTaskInfoChanged_deskBecomesInvisible_otherVisibleDesk_disablesLaunchAdjacent() = runTest {
+        launchAdjacentController.launchAdjacentEnabled = true
+
+        createDesk(visible = true)
+        val desk = createDesk(visible = true)
+        desk.deskRoot.taskInfo.isVisible = false
+        organizer.onTaskInfoChanged(desk.deskRoot.taskInfo)
+
+        assertThat(launchAdjacentController.launchAdjacentEnabled).isFalse()
+    }
+
+    @Test
+    fun onTaskVanished_visibleDeskDisappears_onlyDesk_enablesLaunchAdjacent() = runTest {
+        launchAdjacentController.launchAdjacentEnabled = false
+
+        val desk = createDesk(visible = true)
+        organizer.onTaskVanished(desk.deskRoot.taskInfo)
+
+        assertThat(launchAdjacentController.launchAdjacentEnabled).isTrue()
+    }
+
+    @Test
+    fun onTaskVanished_visibleDeskDisappears_otherDeskVisible_disablesLaunchAdjacent() = runTest {
+        launchAdjacentController.launchAdjacentEnabled = true
+
+        createDesk(visible = true)
+        val desk = createDesk(visible = true)
+        organizer.onTaskVanished(desk.deskRoot.taskInfo)
+
+        assertThat(launchAdjacentController.launchAdjacentEnabled).isFalse()
+    }
+
     private data class DeskRoots(
         val deskRoot: DeskRoot,
         val minimizationRoot: DeskMinimizationRoot,
     )
 
-    private fun createDesk(): DeskRoots {
-        organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
-        val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
-        organizer.onTaskAppeared(freeformRoot, SurfaceControl())
-        val minimizationRoot = createFreeformTask().apply { parentTaskId = -1 }
-        organizer.onTaskAppeared(minimizationRoot, SurfaceControl())
-        return DeskRoots(
-            organizer.deskRootsByDeskId[freeformRoot.taskId],
-            checkNotNull(organizer.deskMinimizationRootsByDeskId[freeformRoot.taskId]),
-        )
+    private suspend fun createDesk(visible: Boolean = true): DeskRoots {
+        val freeformRootTask =
+            createFreeformTask().apply {
+                parentTaskId = -1
+                isVisible = visible
+                isVisibleRequested = visible
+            }
+        val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
+        Mockito.reset(mockShellTaskOrganizer)
+        whenever(
+                mockShellTaskOrganizer.createRootTask(
+                    Display.DEFAULT_DISPLAY,
+                    WINDOWING_MODE_FREEFORM,
+                    organizer,
+                    true,
+                )
+            )
+            .thenAnswer { invocation ->
+                val listener = (invocation.arguments[2] as TaskListener)
+                listener.onTaskAppeared(freeformRootTask, SurfaceControl())
+            }
+            .thenAnswer { invocation ->
+                val listener = (invocation.arguments[2] as TaskListener)
+                listener.onTaskAppeared(minimizationRootTask, SurfaceControl())
+            }
+        val deskId = organizer.createDesk(Display.DEFAULT_DISPLAY)
+        assertEquals(freeformRootTask.taskId, deskId)
+        val deskRoot = assertNotNull(organizer.deskRootsByDeskId.get(freeformRootTask.taskId))
+        val minimizationRoot =
+            assertNotNull(organizer.deskMinimizationRootsByDeskId[freeformRootTask.taskId])
+        assertThat(minimizationRoot.deskId).isEqualTo(freeformRootTask.taskId)
+        assertThat(minimizationRoot.rootId).isEqualTo(minimizationRootTask.taskId)
+        return DeskRoots(deskRoot, minimizationRoot)
     }
 
-    private class FakeOnCreateCallback : DesksOrganizer.OnCreateCallback {
-        var deskId: Int? = null
-        val created: Boolean
-            get() = deskId != null
-
-        override fun onCreated(deskId: Int) {
-            this.deskId = deskId
+    private fun WindowContainerTransaction.hasMinimizationHops(
+        desk: DeskRoots,
+        task: WindowContainerToken,
+    ): Boolean =
+        hierarchyOps.any { hop ->
+            hop.isReparent &&
+                hop.container == task.asBinder() &&
+                hop.newParent == desk.minimizationRoot.token.asBinder()
         }
-    }
+
+    private fun WindowContainerTransaction.hasUnminimizationHops(
+        desk: DeskRoots,
+        task: WindowContainerToken,
+    ): Boolean =
+        hierarchyOps.any { hop ->
+            hop.isReparent &&
+                hop.container == task.asBinder() &&
+                hop.newParent == desk.deskRoot.token.asBinder() &&
+                hop.toTop
+        }
 }
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 5f92326..6039b8f 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
@@ -115,6 +115,8 @@
                 minimizedTasks = minimizedTasks,
                 freeformTasksInZOrder = freeformTasksInZOrder,
                 userId = DEFAULT_USER_ID,
+                leftTiledTask = null,
+                rightTiledTask = null,
             )
 
             val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
@@ -124,6 +126,50 @@
     }
 
     @Test
+    fun addTiledTasks_addsTiledTasksToDesktop() {
+        runTest(StandardTestDispatcher()) {
+            // Create a basic repository state
+            val task = createDesktopTask(1)
+            val desktopPersistentRepositories = createRepositoryWithOneDesk(task)
+            testDatastore.updateData { desktopPersistentRepositories }
+            // Create a new state to be initialized
+            val visibleTasks = ArraySet(listOf(1, 2))
+
+            // Update with new state
+            datastoreRepository.addOrUpdateDesktop(
+                visibleTasks = visibleTasks,
+                minimizedTasks = ArraySet(),
+                freeformTasksInZOrder = ArrayList(),
+                userId = DEFAULT_USER_ID,
+                leftTiledTask = 1,
+                rightTiledTask = null,
+            )
+
+            var actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
+            assertThat(actualDesktop?.tasksByTaskIdMap?.get(1)?.desktopTaskTilingState)
+                .isEqualTo(DesktopTaskTilingState.LEFT)
+            assertThat(actualDesktop?.tasksByTaskIdMap?.get(2)?.desktopTaskTilingState)
+                .isEqualTo(DesktopTaskTilingState.NONE)
+
+            // Update with new state
+            datastoreRepository.addOrUpdateDesktop(
+                visibleTasks = visibleTasks,
+                minimizedTasks = ArraySet(),
+                freeformTasksInZOrder = ArrayList(),
+                userId = DEFAULT_USER_ID,
+                leftTiledTask = null,
+                rightTiledTask = 2,
+            )
+
+            actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
+            assertThat(actualDesktop?.tasksByTaskIdMap?.get(1)?.desktopTaskTilingState)
+                .isEqualTo(DesktopTaskTilingState.NONE)
+            assertThat(actualDesktop?.tasksByTaskIdMap?.get(2)?.desktopTaskTilingState)
+                .isEqualTo(DesktopTaskTilingState.RIGHT)
+        }
+    }
+
+    @Test
     fun removeUsers_removesUsersData() {
         runTest(StandardTestDispatcher()) {
             val task = createDesktopTask(1)
@@ -138,12 +184,16 @@
                 minimizedTasks = minimizedTasks,
                 freeformTasksInZOrder = freeformTasksInZOrder,
                 userId = DEFAULT_USER_ID,
+                leftTiledTask = null,
+                rightTiledTask = null,
             )
             datastoreRepository.addOrUpdateDesktop(
                 visibleTasks = visibleTasks,
                 minimizedTasks = minimizedTasks,
                 freeformTasksInZOrder = freeformTasksInZOrder,
                 userId = USER_ID_2,
+                leftTiledTask = null,
+                rightTiledTask = null,
             )
 
             datastoreRepository.removeUsers(mutableListOf(USER_ID_2))
@@ -175,6 +225,8 @@
                 minimizedTasks = minimizedTasks,
                 freeformTasksInZOrder = freeformTasksInZOrder,
                 userId = DEFAULT_USER_ID,
+                leftTiledTask = null,
+                rightTiledTask = null,
             )
 
             val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
@@ -200,6 +252,8 @@
                 minimizedTasks = minimizedTasks,
                 freeformTasksInZOrder = freeformTasksInZOrder,
                 userId = DEFAULT_USER_ID,
+                leftTiledTask = null,
+                rightTiledTask = null,
             )
 
             val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
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 dd9e6ca..4440d4e 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
@@ -17,7 +17,6 @@
 package com.android.wm.shell.desktopmode.persistence
 
 import android.os.UserManager
-import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import android.view.Display.DEFAULT_DISPLAY
@@ -82,10 +81,27 @@
     }
 
     @Test
-    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE, FLAG_ENABLE_DESKTOP_WINDOWING_HSUM)
-    /** TODO: b/362720497 - add multi-desk version when implemented. */
-    @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
-    fun initWithPersistence_multipleUsers_addedCorrectly_multiDesksDisabled() =
+    fun init_updatesFlow() =
+        runTest(StandardTestDispatcher()) {
+            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)
+
+            repositoryInitializer.initialize(desktopUserRepositories)
+
+            assertThat(repositoryInitializer.isInitialized.value).isTrue()
+        }
+
+    @Test
+    @EnableFlags(
+        FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE,
+        FLAG_ENABLE_DESKTOP_WINDOWING_HSUM,
+        FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+    )
+    fun initWithPersistence_multipleUsers_addedCorrectly() =
         runTest(StandardTestDispatcher()) {
             whenever(persistentRepository.getUserDesktopRepositoryMap())
                 .thenReturn(
@@ -104,50 +120,74 @@
 
             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
-            // once the repository handles multiple desktops.
             assertThat(
-                    desktopUserRepositories.getProfile(USER_ID_1).getActiveTasks(DEFAULT_DISPLAY)
+                    desktopUserRepositories
+                        .getProfile(USER_ID_1)
+                        .getActiveTaskIdsInDesk(DESKTOP_ID_1)
                 )
-                .containsExactly(1, 3, 4, 5)
+                .containsExactly(1, 3)
                 .inOrder()
             assertThat(
                     desktopUserRepositories
                         .getProfile(USER_ID_1)
-                        .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+                        .getActiveTaskIdsInDesk(DESKTOP_ID_2)
                 )
-                .containsExactly(5, 1)
+                .containsExactly(4, 5)
                 .inOrder()
             assertThat(
-                    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)
+                        .getActiveTaskIdsInDesk(DESKTOP_ID_3)
                 )
                 .containsExactly(7, 8)
                 .inOrder()
             assertThat(
                     desktopUserRepositories
-                        .getProfile(USER_ID_2)
-                        .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+                        .getProfile(USER_ID_1)
+                        .getExpandedTasksIdsInDeskOrdered(DESKTOP_ID_1)
                 )
-                .contains(7)
+                .containsExactly(1)
+                .inOrder()
             assertThat(
-                    desktopUserRepositories.getProfile(USER_ID_2).getMinimizedTasks(DEFAULT_DISPLAY)
+                    desktopUserRepositories
+                        .getProfile(USER_ID_1)
+                        .getExpandedTasksIdsInDeskOrdered(DESKTOP_ID_2)
+                )
+                .containsExactly(5)
+                .inOrder()
+            assertThat(
+                    desktopUserRepositories
+                        .getProfile(USER_ID_2)
+                        .getExpandedTasksIdsInDeskOrdered(DESKTOP_ID_3)
+                )
+                .containsExactly(7)
+                .inOrder()
+            assertThat(
+                    desktopUserRepositories
+                        .getProfile(USER_ID_1)
+                        .getMinimizedTaskIdsInDesk(DESKTOP_ID_1)
+                )
+                .containsExactly(3)
+                .inOrder()
+            assertThat(
+                    desktopUserRepositories
+                        .getProfile(USER_ID_1)
+                        .getMinimizedTaskIdsInDesk(DESKTOP_ID_2)
+                )
+                .containsExactly(4)
+                .inOrder()
+            assertThat(
+                    desktopUserRepositories
+                        .getProfile(USER_ID_2)
+                        .getMinimizedTaskIdsInDesk(DESKTOP_ID_3)
                 )
                 .containsExactly(8)
+                .inOrder()
         }
 
     @Test
-    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
-    /** TODO: b/362720497 - add multi-desk version when implemented. */
-    @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
-    fun initWithPersistence_singleUser_addedCorrectly_multiDesksDisabled() =
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE, FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    fun initWithPersistence_singleUser_addedCorrectly() =
         runTest(StandardTestDispatcher()) {
             whenever(persistentRepository.getUserDesktopRepositoryMap())
                 .thenReturn(mapOf(USER_ID_1 to desktopRepositoryState1))
@@ -161,23 +201,44 @@
             assertThat(
                     desktopUserRepositories
                         .getProfile(USER_ID_1)
-                        .getActiveTaskIdsInDesk(deskId = DEFAULT_DISPLAY)
+                        .getActiveTaskIdsInDesk(DESKTOP_ID_1)
                 )
-                .containsExactly(1, 3, 4, 5)
+                .containsExactly(1, 3)
                 .inOrder()
             assertThat(
                     desktopUserRepositories
                         .getProfile(USER_ID_1)
-                        .getExpandedTasksIdsInDeskOrdered(deskId = DEFAULT_DISPLAY)
+                        .getActiveTaskIdsInDesk(DESKTOP_ID_2)
                 )
-                .containsExactly(5, 1)
+                .containsExactly(4, 5)
                 .inOrder()
             assertThat(
                     desktopUserRepositories
                         .getProfile(USER_ID_1)
-                        .getMinimizedTaskIdsInDesk(deskId = DEFAULT_DISPLAY)
+                        .getExpandedTasksIdsInDeskOrdered(DESKTOP_ID_1)
                 )
-                .containsExactly(3, 4)
+                .containsExactly(1)
+                .inOrder()
+            assertThat(
+                    desktopUserRepositories
+                        .getProfile(USER_ID_1)
+                        .getExpandedTasksIdsInDeskOrdered(DESKTOP_ID_2)
+                )
+                .containsExactly(5)
+                .inOrder()
+            assertThat(
+                    desktopUserRepositories
+                        .getProfile(USER_ID_1)
+                        .getMinimizedTaskIdsInDesk(DESKTOP_ID_1)
+                )
+                .containsExactly(3)
+                .inOrder()
+            assertThat(
+                    desktopUserRepositories
+                        .getProfile(USER_ID_1)
+                        .getMinimizedTaskIdsInDesk(DESKTOP_ID_2)
+                )
+                .containsExactly(4)
                 .inOrder()
         }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 9509aaf..52c5ad1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -41,6 +41,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
@@ -198,6 +199,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     public void visibilityTaskChanged_visible_setLaunchAdjacentDisabled() {
         ActivityManager.RunningTaskInfo task =
                 new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
@@ -209,6 +211,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     public void visibilityTaskChanged_notVisible_setLaunchAdjacentEnabled() {
         ActivityManager.RunningTaskInfo task =
                 new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index bc91845..714e5f4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -44,10 +44,12 @@
 import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.desktopmode.DesktopImmersiveController;
+import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.TransitionInfoBuilder;
 import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.StubTransaction;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 
 import org.junit.Before;
@@ -68,6 +70,7 @@
     @Mock private WindowDecorViewModel mWindowDecorViewModel;
     @Mock private TaskChangeListener mTaskChangeListener;
     @Mock private FocusTransitionObserver mFocusTransitionObserver;
+    @Mock private DesksTransitionObserver mDesksTransitionObserver;
 
     private FreeformTaskTransitionObserver mTransitionObserver;
 
@@ -88,7 +91,8 @@
                         Optional.of(mDesktopImmersiveController),
                         mWindowDecorViewModel,
                         Optional.of(mTaskChangeListener),
-                        mFocusTransitionObserver);
+                        mFocusTransitionObserver,
+                        Optional.of(mDesksTransitionObserver));
 
         final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
         verify(mShellInit).addInitCallback(initRunnableCaptor.capture(), same(mTransitionObserver));
@@ -357,6 +361,18 @@
         verify(mDesktopImmersiveController).onTransitionFinished(transition, /* aborted= */ false);
     }
 
+    @Test
+    public void onTransitionReady_forwardsToDesksTransitionObserver() {
+        final IBinder transition = mock(IBinder.class);
+        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, /* flags= */ 0)
+                .build();
+
+        mTransitionObserver.onTransitionReady(transition, info, new StubTransaction(),
+                new StubTransaction());
+
+        verify(mDesksTransitionObserver).onTransitionReady(transition, info);
+    }
+
     private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) {
         final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
         taskInfo.taskId = taskId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
index 14f9ffc..2bd9afc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -107,7 +107,7 @@
         });
 
         verify(mMockStartCallback).run();
-        verifyZeroInteractions(mMockEndCallback);
+        verifyNoMoreInteractions(mMockEndCallback);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
index 72c4666..fa7ab952 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -117,7 +117,7 @@
         });
 
         verify(mMockStartCallback).run();
-        verifyZeroInteractions(mMockEndCallback);
+        verifyNoMoreInteractions(mMockEndCallback);
 
         // Check corner and shadow radii were set
         verify(mMockAnimateTransaction, atLeastOnce())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
index b816f0e..97133be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
@@ -21,7 +21,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -143,7 +143,7 @@
         });
 
         verify(mMockStartCallback).run();
-        verifyZeroInteractions(mMockEndCallback);
+        verifyNoMoreInteractions(mMockEndCallback);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
index 23fbad0..c99ca6d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
@@ -22,7 +22,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.mockito.kotlin.MatchersKt.eq;
 
@@ -118,7 +118,7 @@
         });
 
         verify(mMockStartCallback).run();
-        verifyZeroInteractions(mMockEndCallback);
+        verifyNoMoreInteractions(mMockEndCallback);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
index 333569a..a760890 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -24,18 +24,21 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.mockito.kotlin.MatchersKt.eq;
 import static org.mockito.kotlin.VerificationKt.clearInvocations;
 import static org.mockito.kotlin.VerificationKt.times;
 import static org.mockito.kotlin.VerificationKt.verify;
-import static org.mockito.kotlin.VerificationKt.verifyZeroInteractions;
+import static org.mockito.kotlin.VerificationKt.verifyNoMoreInteractions;
 
 import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.app.RemoteAction;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -48,8 +51,10 @@
 
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
 import com.android.wm.shell.pip2.animation.PipResizeAnimator;
 
 import org.junit.Before;
@@ -107,6 +112,16 @@
     }
 
     @Test
+    public void constructor_addOnPipComponentChangedListener() {
+        mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+                mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+                mMockPipBoundsAlgorithm, mMockShellExecutor);
+
+        verify(mMockPipBoundsState).addOnPipComponentChangedListener(
+                any(PipBoundsState.OnPipComponentChangedListener.class));
+    }
+
+    @Test
     public void setPictureInPictureParams_updatePictureInPictureParams() {
         mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
                 mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
@@ -161,7 +176,7 @@
         mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
                 aspectRatio, action1));
 
-        verifyZeroInteractions(mMockPipParamsChangedCallback);
+        verifyNoMoreInteractions(mMockPipParamsChangedCallback);
     }
 
     @Test
@@ -178,9 +193,10 @@
         clearInvocations(mMockPipParamsChangedCallback);
         mPipTaskListener.onTaskInfoChanged(new ActivityManager.RunningTaskInfo());
 
-        verifyZeroInteractions(mMockPipParamsChangedCallback);
+        verifyNoMoreInteractions(mMockPipParamsChangedCallback);
         verify(mMockPipTransitionState, times(0))
                 .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+        assertTrue(mPipTaskListener.getPictureInPictureParams().empty());
     }
 
     @Test
@@ -230,7 +246,7 @@
         mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
 
         verify(mMockPipTransitionState).setOnIdlePipTransitionStateRunnable(any(Runnable.class));
-        verifyZeroInteractions(mMockPipParamsChangedCallback);
+        verifyNoMoreInteractions(mMockPipParamsChangedCallback);
     }
 
     @Test
@@ -247,7 +263,7 @@
         clearInvocations(mMockPipParamsChangedCallback);
         mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
 
-        verifyZeroInteractions(mMockPipParamsChangedCallback);
+        verifyNoMoreInteractions(mMockPipParamsChangedCallback);
         verify(mMockPipTransitionState, times(0))
                 .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
     }
@@ -304,7 +320,7 @@
                 PipTransitionState.SCHEDULED_BOUNDS_CHANGE,
                 extras);
 
-        verifyZeroInteractions(mMockPipScheduler);
+        verifyNoMoreInteractions(mMockPipScheduler);
     }
 
     @Test
@@ -359,6 +375,26 @@
         verify(mMockPipResizeAnimator, times(0)).start();
     }
 
+    @Test
+    public void onPipComponentChanged_clearPictureInPictureParams() {
+        when(mMockContext.getResources()).thenReturn(mock(Resources.class));
+        PipBoundsState pipBoundsState = new PipBoundsState(mMockContext,
+                mock(PhoneSizeSpecSource.class), mock(PipDisplayLayoutState.class));
+        pipBoundsState.setLastPipComponentName(new ComponentName("org.test", "test1"));
+
+        mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+                mMockPipTransitionState, mMockPipScheduler, pipBoundsState,
+                mMockPipBoundsAlgorithm, mMockShellExecutor);
+        Rational aspectRatio = new Rational(4, 3);
+        String action1 = "action1";
+        mPipTaskListener.setPictureInPictureParams(getPictureInPictureParams(
+                aspectRatio, action1));
+
+        pipBoundsState.setLastPipComponentName(new ComponentName("org.test", "test2"));
+
+        assertTrue(mPipTaskListener.getPictureInPictureParams().empty());
+    }
+
     private PictureInPictureParams getPictureInPictureParams(Rational aspectRatio,
             String... actions) {
         final PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
index 82cdfd5..51de50d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.Flags;
@@ -82,7 +82,7 @@
         mPipUiStateChangeController.onPipTransitionStateChanged(
                 PipTransitionState.SWIPING_TO_PIP, PipTransitionState.ENTERING_PIP, Bundle.EMPTY);
 
-        verifyZeroInteractions(mPictureInPictureUiStateConsumer);
+        verifyNoMoreInteractions(mPictureInPictureUiStateConsumer);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
index 6ecebd7..75f6bda 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
@@ -203,7 +203,7 @@
         assertThat(taskInfoFromParcel.taskInfoList).hasSize(3)
         // Only compare task ids
         val taskIdComparator = Correspondence.transforming<TaskInfo, Int>(
-            { it?.taskId }, "has taskId of"
+            { it.taskId }, "has taskId of"
         )
         assertThat(taskInfoFromParcel.taskInfoList).comparingElementsUsing(taskIdComparator)
             .containsExactly(1, 2, 3).inOrder()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryTest.kt
index fd22a84..2b39262 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DragZoneFactoryTest.kt
@@ -53,7 +53,8 @@
         tabletPortrait.copy(windowBounds = Rect(0, 0, 800, 900), isSmallTablet = true)
     private val foldableLandscape =
         foldablePortrait.copy(windowBounds = Rect(0, 0, 900, 800), isLandscape = true)
-    private val splitScreenModeChecker = SplitScreenModeChecker { SplitScreenMode.NONE }
+    private var splitScreenMode = SplitScreenMode.NONE
+    private val splitScreenModeChecker = SplitScreenModeChecker { splitScreenMode }
     private var isDesktopWindowModeSupported = true
     private val desktopWindowModeChecker = DesktopWindowModeChecker { isDesktopWindowModeSupported }
 
@@ -283,7 +284,7 @@
     }
 
     @Test
-    fun dragZonesForBubble_tablet_desktopModeDisabled() {
+    fun dragZonesForBubble_desktopModeDisabled() {
         isDesktopWindowModeSupported = false
         dragZoneFactory =
             DragZoneFactory(
@@ -298,7 +299,7 @@
     }
 
     @Test
-    fun dragZonesForExpandedView_tablet_desktopModeDisabled() {
+    fun dragZonesForExpandedView_desktopModeDisabled() {
         isDesktopWindowModeSupported = false
         dragZoneFactory =
             DragZoneFactory(
@@ -314,6 +315,38 @@
         assertThat(dragZones.filterIsInstance<DragZone.DesktopWindow>()).isEmpty()
     }
 
+    @Test
+    fun dragZonesForBubble_splitScreenModeUnsupported() {
+        splitScreenMode = SplitScreenMode.UNSUPPORTED
+        dragZoneFactory =
+            DragZoneFactory(
+                context,
+                foldableLandscape,
+                splitScreenModeChecker,
+                desktopWindowModeChecker
+            )
+        val dragZones =
+            dragZoneFactory.createSortedDragZones(DraggedObject.Bubble(BubbleBarLocation.LEFT))
+        assertThat(dragZones.filterIsInstance<DragZone.Split>()).isEmpty()
+    }
+
+    @Test
+    fun dragZonesForExpandedView_splitScreenModeUnsupported() {
+        splitScreenMode = SplitScreenMode.UNSUPPORTED
+        dragZoneFactory =
+            DragZoneFactory(
+                context,
+                foldableLandscape,
+                splitScreenModeChecker,
+                desktopWindowModeChecker
+            )
+        val dragZones =
+            dragZoneFactory.createSortedDragZones(
+                DraggedObject.ExpandedView(BubbleBarLocation.LEFT)
+            )
+        assertThat(dragZones.filterIsInstance<DragZone.Split>()).isEmpty()
+    }
+
     private inline fun <reified T> verifyInstance(): DragZoneVerifier = { dragZone ->
         assertThat(dragZone).isInstanceOf(T::class.java)
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DropTargetManagerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DropTargetManagerTest.kt
index 95498cb..3b21e36 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DropTargetManagerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/DropTargetManagerTest.kt
@@ -352,7 +352,7 @@
             initialDragZone = dragZone
         }
 
-        override fun onDragZoneChanged(from: DragZone, to: DragZone) {
+        override fun onDragZoneChanged(draggedObject: DraggedObject, from: DragZone, to: DragZone) {
             fromDragZone = from
             toDragZone = to
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
index 5ac6800..12785c0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
@@ -42,6 +42,7 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.kotlin.any
 import org.mockito.kotlin.eq
@@ -87,7 +88,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun testIsTopActivityExemptWithPermission_onlyTransparentActivitiesInStack() {
-        allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+        allowOverlayPermissionForAllUsers(arrayOf(SYSTEM_ALERT_WINDOW))
         assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
             createFreeformTask(/* displayId */ 0)
                 .apply {
@@ -101,7 +102,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun testIsTopActivityExemptWithNoPermission_onlyTransparentActivitiesInStack() {
-        allowOverlayPermission(arrayOf())
+        allowOverlayPermissionForAllUsers(arrayOf())
         assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
             createFreeformTask(/* displayId */ 0)
                 .apply {
@@ -115,7 +116,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
     fun testIsTopActivityExemptCachedPermissionCheckIsUsed() {
-        allowOverlayPermission(arrayOf())
+        allowOverlayPermissionForAllUsers(arrayOf())
         assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
             createFreeformTask(/* displayId */ 0)
                 .apply {
@@ -123,6 +124,7 @@
                     isTopActivityNoDisplay = false
                     numActivities = 1
                     baseActivity = baseActivityTest
+                    userId = 10
                 }))
         assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
             createFreeformTask(/* displayId */ 0)
@@ -131,10 +133,26 @@
                     isTopActivityNoDisplay = false
                     numActivities = 1
                     baseActivity = baseActivityTest
+                    userId = 10
                 }))
-        verify(packageManager, times(1)).getPackageInfo(
+        assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+            createFreeformTask(/* displayId */ 0)
+                .apply {
+                    isActivityStackTransparent = true
+                    isTopActivityNoDisplay = false
+                    numActivities = 1
+                    baseActivity = baseActivityTest
+                    userId = 0
+                }))
+        verify(packageManager, times(1)).getPackageInfoAsUser(
             eq("com.test.dummypackage"),
-            eq(PackageManager.GET_PERMISSIONS)
+            eq(PackageManager.GET_PERMISSIONS),
+            eq(10)
+        )
+        verify(packageManager, times(1)).getPackageInfoAsUser(
+            eq("com.test.dummypackage"),
+            eq(PackageManager.GET_PERMISSIONS),
+            eq(0)
         )
     }
 
@@ -284,13 +302,14 @@
             }
         }
 
-    fun allowOverlayPermission(permissions: Array<String>) {
+    fun allowOverlayPermissionForAllUsers(permissions: Array<String>) {
         val packageInfo = mock<PackageInfo>()
         packageInfo.requestedPermissions = permissions
         whenever(
-            packageManager.getPackageInfo(
+            packageManager.getPackageInfoAsUser(
                 anyString(),
-                eq(PackageManager.GET_PERMISSIONS)
+                eq(PackageManager.GET_PERMISSIONS),
+                anyInt(),
             )
         ).thenReturn(packageInfo)
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
index fb62ba7..edf91fe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
@@ -234,14 +234,25 @@
         assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse()
     }
 
+    @DisableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE)
     @Test
     fun isDeviceEligibleForDesktopMode_configDEModeOnAndIntDispHostsDesktopOff_returnsFalse() {
         doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
-        doReturn(false).whenever(mockResources).getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops))
+        doReturn(false).whenever(mockResources)
+            .getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops))
 
         assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse()
     }
 
+    @EnableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE)
+    @Test
+    fun isPDDeviceEligibleForDesktopMode_configDEModeOnAndIntDispHostsDesktopOff_returnsTrue() {
+        doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+        doReturn(false).whenever(mockResources).getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops))
+
+        assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isTrue()
+    }
+
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitMultiDisplayHelperTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitMultiDisplayHelperTests.kt
new file mode 100644
index 0000000..23751b6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitMultiDisplayHelperTests.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.splitscreen
+
+import android.app.ActivityManager
+import android.hardware.display.DisplayManager
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.split.SplitLayout
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+/**
+ * Unit tests for [SplitMultiDisplayHelper].
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SplitMultiDisplayHelperTests : ShellTestCase() {
+
+    private lateinit var splitMultiDisplayHelper: SplitMultiDisplayHelper
+
+    @Mock
+    private lateinit var mockDisplayManager: DisplayManager
+    @Mock
+    private lateinit var mockSplitLayout: SplitLayout
+    @Mock
+    private lateinit var mockDisplay1: Display
+    @Mock
+    private lateinit var mockDisplay2: Display
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mockDisplay1 = mockDisplayManager.getDisplay(Display.DEFAULT_DISPLAY) ?: mock(Display::class.java)
+        mockDisplay2 = mockDisplayManager.getDisplay(Display.DEFAULT_DISPLAY + 1) ?: mock(Display::class.java)
+
+        `when`(mockDisplay1.displayId).thenReturn(Display.DEFAULT_DISPLAY)
+        `when`(mockDisplay2.displayId).thenReturn(Display.DEFAULT_DISPLAY + 1)
+
+        splitMultiDisplayHelper = SplitMultiDisplayHelper(mockDisplayManager)
+    }
+
+    @Test
+    fun getDisplayIds_noDisplays_returnsEmptyList() {
+        `when`(mockDisplayManager.displays).thenReturn(emptyArray())
+
+        val displayIds = splitMultiDisplayHelper.getDisplayIds()
+
+        assertThat(displayIds).isEmpty()
+    }
+
+    @Test
+    fun getDisplayIds_singleDisplay_returnsCorrectId() {
+        `when`(mockDisplayManager.displays).thenReturn(arrayOf(mockDisplay1))
+
+        val displayIds = splitMultiDisplayHelper.getDisplayIds()
+
+        assertThat(displayIds).containsExactly(Display.DEFAULT_DISPLAY)
+    }
+
+    @Test
+    fun getDisplayIds_multiDisplays_returnsCorrectIds() {
+        `when`(mockDisplayManager.displays).thenReturn(arrayOf(mockDisplay1, mockDisplay2))
+
+        val displayIds = splitMultiDisplayHelper.getDisplayIds()
+
+        assertThat(displayIds).containsExactly(Display.DEFAULT_DISPLAY, Display.DEFAULT_DISPLAY + 1)
+    }
+
+    @Test
+    fun swapDisplayTaskHierarchy_validDisplays_swapsHierarchies() {
+        val rootTaskInfo1 = ActivityManager.RunningTaskInfo().apply { taskId = 1 }
+        val rootTaskInfo2 = ActivityManager.RunningTaskInfo().apply { taskId = 2 }
+
+        splitMultiDisplayHelper.setDisplayRootTaskInfo(Display.DEFAULT_DISPLAY, rootTaskInfo1)
+        splitMultiDisplayHelper.setDisplayRootTaskInfo(Display.DEFAULT_DISPLAY + 1, rootTaskInfo2)
+
+        splitMultiDisplayHelper.swapDisplayTaskHierarchy(Display.DEFAULT_DISPLAY, Display.DEFAULT_DISPLAY + 1)
+
+        assertThat(splitMultiDisplayHelper.getDisplayRootTaskInfo(Display.DEFAULT_DISPLAY)).isEqualTo(rootTaskInfo2)
+        assertThat(splitMultiDisplayHelper.getDisplayRootTaskInfo(Display.DEFAULT_DISPLAY + 1)).isEqualTo(rootTaskInfo1)
+    }
+
+    @Test
+    fun swapDisplayTaskHierarchy_invalidFirstDisplayId_doesNothing() {
+        val rootTaskInfo2 = ActivityManager.RunningTaskInfo().apply { taskId = 2 }
+
+        splitMultiDisplayHelper.setDisplayRootTaskInfo(Display.DEFAULT_DISPLAY + 1, rootTaskInfo2)
+
+        splitMultiDisplayHelper.swapDisplayTaskHierarchy(Display.INVALID_DISPLAY, Display.DEFAULT_DISPLAY + 1)
+
+        assertThat(splitMultiDisplayHelper.getDisplayRootTaskInfo(Display.INVALID_DISPLAY)).isNull()
+        assertThat(splitMultiDisplayHelper.getDisplayRootTaskInfo(Display.DEFAULT_DISPLAY + 1)).isEqualTo(rootTaskInfo2)
+    }
+
+    @Test
+    fun swapDisplayTaskHierarchy_invalidSecondDisplayId_doesNothing() {
+        val rootTaskInfo1 = ActivityManager.RunningTaskInfo().apply { taskId = 1 }
+
+        splitMultiDisplayHelper.setDisplayRootTaskInfo(Display.DEFAULT_DISPLAY, rootTaskInfo1)
+
+        splitMultiDisplayHelper.swapDisplayTaskHierarchy(Display.DEFAULT_DISPLAY, Display.INVALID_DISPLAY)
+
+        assertThat(splitMultiDisplayHelper.getDisplayRootTaskInfo(Display.DEFAULT_DISPLAY)).isEqualTo(rootTaskInfo1)
+        assertThat(splitMultiDisplayHelper.getDisplayRootTaskInfo(Display.INVALID_DISPLAY)).isNull()
+    }
+
+    @Test
+    fun swapDisplayTaskHierarchy_sameDisplayId_doesNothing() {
+        val rootTaskInfo1 = ActivityManager.RunningTaskInfo().apply { taskId = 1 }
+
+        splitMultiDisplayHelper.setDisplayRootTaskInfo(Display.DEFAULT_DISPLAY, rootTaskInfo1)
+
+        splitMultiDisplayHelper.swapDisplayTaskHierarchy(Display.DEFAULT_DISPLAY, Display.DEFAULT_DISPLAY)
+
+        assertThat(splitMultiDisplayHelper.getDisplayRootTaskInfo(Display.DEFAULT_DISPLAY)).isEqualTo(rootTaskInfo1)
+    }
+
+    @Test
+    fun getDisplayRootTaskInfo_validDisplayId_returnsRootTaskInfo() {
+        val rootTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = 123 }
+
+        splitMultiDisplayHelper.setDisplayRootTaskInfo(Display.DEFAULT_DISPLAY, rootTaskInfo)
+
+        val retrievedRootTaskInfo = splitMultiDisplayHelper.getDisplayRootTaskInfo(Display.DEFAULT_DISPLAY)
+
+        assertThat(retrievedRootTaskInfo).isEqualTo(rootTaskInfo)
+    }
+
+    @Test
+    fun getDisplayRootTaskInfo_invalidDisplayId_returnsNull() {
+        val retrievedRootTaskInfo = splitMultiDisplayHelper.getDisplayRootTaskInfo(Display.INVALID_DISPLAY)
+
+        assertThat(retrievedRootTaskInfo).isNull()
+    }
+
+    @Test
+    fun setDisplayRootTaskInfo_setsRootTaskInfo() {
+        val rootTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = 456 }
+
+        splitMultiDisplayHelper.setDisplayRootTaskInfo(Display.DEFAULT_DISPLAY, rootTaskInfo)
+        val retrievedRootTaskInfo = splitMultiDisplayHelper.getDisplayRootTaskInfo(Display.DEFAULT_DISPLAY)
+
+        assertThat(retrievedRootTaskInfo).isEqualTo(rootTaskInfo)
+    }
+}
\ No newline at end of file
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 e246329..077b355 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
@@ -211,11 +211,19 @@
         when(mSplitLayout.getDividerLeash()).thenReturn(dividerLeash);
 
         mRootTask = new TestRunningTaskInfoBuilder().build();
-        SurfaceControl rootLeash = new SurfaceControl.Builder().setName("test").build();
+        SurfaceControl rootLeash = new SurfaceControl.Builder().setName("splitRoot").build();
         mStageCoordinator.onTaskAppeared(mRootTask, rootLeash);
 
         mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
         mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
+        SurfaceControl mainRootLeash = new SurfaceControl.Builder().setName("mainRoot").build();
+        SurfaceControl sideRootLeash = new SurfaceControl.Builder().setName("sideRoot").build();
+        mMainStage.mRootLeash = mainRootLeash;
+        mSideStage.mRootLeash = sideRootLeash;
+        SurfaceControl mainDimLayer = new SurfaceControl.Builder().setName("mainDim").build();
+        SurfaceControl sideDimLayer = new SurfaceControl.Builder().setName("sideDim").build();
+        mMainStage.mDimLayer = mainDimLayer;
+        mSideStage.mDimLayer = sideDimLayer;
         doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager();
         doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager();
 
@@ -563,7 +571,8 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    @DisableFlags({Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+            Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX})
     public void testRequestEnterSplit_didNotEnterSplitSelect_doesNotApplyTransaction() {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         mStageCoordinator.registerSplitSelectListener(
@@ -577,7 +586,8 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+    @DisableFlags({Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+            Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX})
     public void testRequestEnterSplit_enteredSplitSelect_appliesTransaction() {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         mStageCoordinator.registerSplitSelectListener(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 3099b0f..a122c38 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -27,6 +27,7 @@
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.window.flags.Flags.FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX;
 import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
 
@@ -44,6 +45,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
@@ -196,6 +198,73 @@
     }
 
     @Test
+    @DisableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+    public void startDragToDesktopFinished_flagDisabled_doesNotTriggerCallback()
+            throws RemoteException {
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+        when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+        IBinder transition = mock(IBinder.class);
+        mHomeTransitionObserver.onTransitionReady(
+                transition,
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ false);
+
+        verify(mListener, never()).onHomeVisibilityChanged(/* isVisible= */ anyBoolean());
+    }
+
+    @Test
+    @EnableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+    public void startDragToDesktopAborted_doesNotTriggerCallback() throws RemoteException {
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+        when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+        IBinder transition = mock(IBinder.class);
+        mHomeTransitionObserver.onTransitionReady(
+                transition,
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ true);
+
+        verify(mListener, never()).onHomeVisibilityChanged(/* isVisible= */ anyBoolean());
+    }
+
+    @Test
+    @EnableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
+    public void startDragToDesktopFinished_triggersCallback() throws RemoteException {
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+        when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+        IBinder transition = mock(IBinder.class);
+        mHomeTransitionObserver.onTransitionReady(
+                transition,
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ false);
+
+        verify(mListener).onHomeVisibilityChanged(/* isVisible= */ true);
+    }
+
+    @Test
     @EnableFlags({Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE})
     public void testDragTaskToBubbleOverHome_notifiesHomeIsVisible() throws RemoteException {
         ActivityManager.RunningTaskInfo homeTask = createTaskInfo(1, ACTIVITY_TYPE_HOME);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/RemoteTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/RemoteTransitionHandlerTest.kt
new file mode 100644
index 0000000..048981d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/RemoteTransitionHandlerTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.transition
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.WindowManager
+import android.window.RemoteTransition
+import android.window.TransitionFilter
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestSyncExecutor
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+/**
+ * Test class for [RemoteTransitionHandler].
+ *
+ * atest WMShellUnitTests:RemoteTransitionHandlerTest
+ */
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class RemoteTransitionHandlerTest : ShellTestCase() {
+
+    private val testExecutor: TestSyncExecutor = TestSyncExecutor()
+
+    private val testRemoteTransition = RemoteTransition(TestRemoteTransition())
+    private lateinit var handler: RemoteTransitionHandler
+
+    @Before
+    fun setUp() {
+        handler = RemoteTransitionHandler(testExecutor)
+    }
+
+    @Test
+    fun handleRequest_noRemoteTransition_returnsNull() {
+        val request = TransitionRequestInfo(WindowManager.TRANSIT_OPEN, null, null)
+
+        assertNull(handler.handleRequest(mock(), request))
+    }
+
+    @Test
+    fun handleRequest_testRemoteTransition_returnsWindowContainerTransaction() {
+        val request = TransitionRequestInfo(WindowManager.TRANSIT_OPEN, null, testRemoteTransition)
+
+        assertTrue(handler.handleRequest(mock(), request) is WindowContainerTransaction)
+    }
+
+    @Test
+    fun startAnimation_noRemoteTransition_returnsFalse() {
+        val request = TransitionRequestInfo(WindowManager.TRANSIT_OPEN, null, null)
+        handler.handleRequest(mock(), request)
+
+        val isHandled = handler.startAnimation(
+            /* transition= */ mock(),
+            /* info= */ createTransitionInfo(),
+            /* startTransaction= */ mock(),
+            /* finishTransaction= */ mock(),
+            /* finishCallback= */ {},
+        )
+
+        assertFalse(isHandled)
+    }
+
+    @Test
+    fun startAnimation_remoteTransition_returnsTrue() {
+        val request = TransitionRequestInfo(WindowManager.TRANSIT_OPEN, null, testRemoteTransition)
+        handler.addFiltered(TransitionFilter(), testRemoteTransition)
+        handler.handleRequest(mock(), request)
+
+        val isHandled = handler.startAnimation(
+            /* transition= */ testRemoteTransition.remoteTransition.asBinder(),
+            /* info= */ createTransitionInfo(),
+            /* startTransaction= */ mock(),
+            /* finishTransaction= */ mock(),
+            /* finishCallback= */ {},
+        )
+
+        assertTrue(isHandled)
+    }
+
+    private fun createTransitionInfo(
+        type: Int = WindowManager.TRANSIT_OPEN,
+        changeMode: Int = WindowManager.TRANSIT_CLOSE,
+    ): TransitionInfo =
+        TransitionInfo(type, /* flags= */ 0).apply {
+            addChange(
+                TransitionInfo.Change(mock(), mock()).apply {
+                    mode = changeMode
+                    parent = null
+                }
+            )
+        }
+}
\ No newline at end of file
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 257bbb5..b07b6c1 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
@@ -22,6 +22,7 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.SurfaceControl
+import android.window.TaskSnapshot
 import androidx.test.filters.SmallTest
 import com.android.window.flags.Flags
 import com.android.wm.shell.MockToken
@@ -33,6 +34,7 @@
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
+import org.junit.Assert.fail
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -84,7 +86,29 @@
         assertThat(menu.menuViewContainer).isInstanceOf(AdditionalSystemViewContainer::class.java)
     }
 
-    private fun createMenu(task: RunningTaskInfo) = DesktopHeaderManageWindowsMenu(
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun testShow_nullSnapshotDoesNotCauseNPE() {
+        val task = createFreeformTask()
+        val snapshotList = listOf(Pair(/* index = */ 1, /* snapshot = */ null))
+        // Set as immersive so that menu is created as system view container (simpler of the
+        // options)
+        userRepositories.getProfile(DEFAULT_USER_ID).setTaskInFullImmersiveState(
+            displayId = task.displayId,
+            taskId = task.taskId,
+            immersive = true
+        )
+        try {
+            menu = createMenu(task, snapshotList)
+        } catch (e: NullPointerException) {
+            fail("Null snapshot should not have thrown null pointer exception")
+        }
+    }
+
+    private fun createMenu(
+        task: RunningTaskInfo,
+        snapshotList: List<Pair<Int, TaskSnapshot?>> = emptyList()
+    ) = DesktopHeaderManageWindowsMenu(
         callerTaskInfo = task,
         x = 0,
         y = 0,
@@ -94,7 +118,7 @@
         desktopUserRepositories = userRepositories,
         surfaceControlBuilderSupplier = { SurfaceControl.Builder() },
         surfaceControlTransactionSupplier = { SurfaceControl.Transaction() },
-        snapshotList = emptyList(),
+        snapshotList = snapshotList,
         onIconClickListener = {},
         onOutsideClickListener = {},
     )
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
index 067dcec..b1f9241 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
@@ -28,6 +28,7 @@
 import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -109,7 +110,7 @@
         onTaskOpening(task, taskSurface)
         assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
         task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
-        onTaskChanging(task, taskSurface)
+        onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
 
         assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
         verify(decoration).close()
@@ -165,7 +166,7 @@
 
         setLargeScreen(false)
         setUpMockDecorationForTask(task)
-        onTaskChanging(task, taskSurface)
+        onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
         assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
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 e89a122..ad3426e 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
@@ -51,6 +51,7 @@
 import android.view.View
 import android.view.ViewRootImpl
 import android.view.WindowInsets.Type.statusBars
+import android.view.WindowManager.TRANSIT_CHANGE
 import android.window.WindowContainerTransaction
 import android.window.WindowContainerTransaction.HierarchyOp
 import androidx.test.filters.SmallTest
@@ -115,7 +116,8 @@
                 .spyStatic(DragPositioningCallbackUtility::class.java)
                 .startMocking()
 
-        doReturn(true).`when` { DesktopModeStatus.canInternalDisplayHostDesktops(Mockito.any()) }
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupportedOnDisplay(Mockito.any(),
+            Mockito.any()) }
         doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(Mockito.any()) }
         doReturn(false).`when` { DesktopModeStatus.overridesShowAppHandle(Mockito.any()) }
 
@@ -133,7 +135,7 @@
 
         task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
         task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
-        onTaskChanging(task, taskSurface)
+        onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
 
         assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
         verify(decoration).close()
@@ -148,12 +150,12 @@
         val taskSurface = SurfaceControl()
         setUpMockDecorationForTask(task)
 
-        onTaskChanging(task, taskSurface)
+        onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
         assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
 
         task.setWindowingMode(WINDOWING_MODE_FREEFORM)
         task.setActivityType(ACTIVITY_TYPE_STANDARD)
-        onTaskChanging(task, taskSurface)
+        onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
         assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
     }
 
@@ -394,7 +396,7 @@
         whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
 
         val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN)
-        doReturn(true).`when` { DesktopModeStatus.canInternalDisplayHostDesktops(any()) }
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupportedOnDisplay(any(), any()) }
         setUpMockDecorationsForTasks(task)
 
         onTaskOpening(task)
@@ -757,20 +759,6 @@
     }
 
     @Test
-    fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
-        val toSplitScreenListenerCaptor = forClass(Function0::class.java)
-                as ArgumentCaptor<Function0<Unit>>
-        val decor = createOpenTaskDecoration(
-            windowingMode = WINDOWING_MODE_MULTI_WINDOW,
-            onToSplitScreenClickListenerCaptor = toSplitScreenListenerCaptor
-        )
-
-        toSplitScreenListenerCaptor.value.invoke()
-
-        verify(decor).disposeStatusBarInputLayer()
-    }
-
-    @Test
     fun testDecor_onClickToOpenBrowser_closeMenus() {
         val openInBrowserListenerCaptor = forClass(Consumer::class.java)
                 as ArgumentCaptor<Consumer<Intent>>
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 81dfaed..4c9c2f1 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
@@ -79,10 +79,12 @@
 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.AppHandleAndHeaderVisibilityHelper
 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.tiling.DesktopTilingDecorViewModel
+import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
 import org.junit.After
 import org.mockito.Mockito
@@ -124,6 +126,7 @@
     protected val mockShellController = mock<ShellController>()
     protected val testShellExecutor = TestShellExecutor()
     protected val mockAppHeaderViewHolderFactory = mock<AppHeaderViewHolder.Factory>()
+    protected val mockAppHandleViewHolderFactory = mock<AppHandleViewHolder.Factory>()
     protected val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
     protected val mockShellCommandHandler = mock<ShellCommandHandler>()
     protected val mockWindowManager = mock<IWindowManager>()
@@ -174,6 +177,7 @@
     internal lateinit var desktopModeOnKeyguardChangedListener: DesktopModeKeyguardChangeListener
     protected lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel
     protected lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
+    protected lateinit var appHandleAndHeaderVisibilityHelper: AppHandleAndHeaderVisibilityHelper
 
     fun setUpCommon() {
         spyContext = spy(mContext)
@@ -185,9 +189,13 @@
         whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
         whenever(mockDisplayController.getDisplayContext(any())).thenReturn(spyContext)
         whenever(mockDisplayController.getDisplay(any())).thenReturn(display)
+        whenever(display.type).thenReturn(Display.TYPE_INTERNAL)
         whenever(mockDesktopUserRepositories.getProfile(anyInt()))
             .thenReturn(mockDesktopRepository)
         desktopModeCompatPolicy = DesktopModeCompatPolicy(spyContext)
+        appHandleAndHeaderVisibilityHelper =
+            AppHandleAndHeaderVisibilityHelper(spyContext, mockDisplayController,
+                desktopModeCompatPolicy)
         desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
             spyContext,
             testShellExecutor,
@@ -216,12 +224,14 @@
             mockInputMonitorFactory,
             transactionFactory,
             mockAppHeaderViewHolderFactory,
+            mockAppHandleViewHolderFactory,
             mockRootTaskDisplayAreaOrganizer,
             windowDecorByTaskIdSpy,
             mockInteractionJankMonitor,
             Optional.of(mockTasksLimiter),
             mockAppHandleEducationController,
             mockAppToWebEducationController,
+            appHandleAndHeaderVisibilityHelper,
             mockCaptionHandleRepository,
             Optional.of(mockActivityOrientationChangeHandler),
             mockTaskPositionerFactory,
@@ -324,7 +334,7 @@
             mockDesktopModeWindowDecorFactory.create(
                 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(), any())
+                any(), any(), any(), any())
         ).thenReturn(decoration)
         decoration.mTaskInfo = task
         whenever(decoration.user).thenReturn(mockUserHandle)
@@ -346,12 +356,17 @@
         )
     }
 
-    protected fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
+    protected fun onTaskChanging(
+        task: RunningTaskInfo,
+        leash: SurfaceControl = SurfaceControl(),
+        changeMode: Int
+    ) {
         desktopModeWindowDecorViewModel.onTaskChanging(
             task,
             leash,
             StubTransaction(),
-            StubTransaction()
+            StubTransaction(),
+            changeMode
         )
     }
 
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 c4f70ac2..f37f2fb 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
@@ -60,7 +60,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.TypedArray;
-import android.graphics.PointF;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.net.Uri;
@@ -115,6 +115,7 @@
 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.AppHandleViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
 
 import kotlin.Unit;
@@ -167,9 +168,13 @@
     private static final boolean DEFAULT_IS_STATUSBAR_VISIBLE = true;
     private static final boolean DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED = false;
     private static final boolean DEFAULT_IS_IN_FULL_IMMERSIVE_MODE = false;
+    private static final boolean DEFAULT_IS_DRAGGING = false;
     private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
     private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
     private static final boolean DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS = false;
+    private static final boolean DEFAULT_IS_RECENTS_TRANSITION_RUNNING = false;
+    private static final boolean DEFAULT_IS_MOVING_TO_BACK = false;
+
 
     @Mock
     private DisplayController mMockDisplayController;
@@ -190,8 +195,12 @@
     @Mock
     private AppHeaderViewHolder.Factory mMockAppHeaderViewHolderFactory;
     @Mock
+    private AppHandleViewHolder.Factory mMockAppHandleViewHolderFactory;
+    @Mock
     private AppHeaderViewHolder mMockAppHeaderViewHolder;
     @Mock
+    private AppHandleViewHolder mMockAppHandleViewHolder;
+    @Mock
     private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
     @Mock
     private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
@@ -300,6 +309,9 @@
         when(mMockAppHeaderViewHolderFactory
                 .create(any(), any(), any(), any(), any(), any(), any(), any(), any()))
                 .thenReturn(mMockAppHeaderViewHolder);
+        when(mMockAppHandleViewHolderFactory
+                .create(any(), any(), any(), any(), any()))
+                .thenReturn(mMockAppHandleViewHolder);
         when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
         when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
         when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay)))
@@ -415,11 +427,14 @@
                 DEFAULT_IS_STATUSBAR_VISIBLE,
                 DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 /* shouldIgnoreCornerRadius= */ true,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
     }
@@ -616,11 +631,14 @@
                 DEFAULT_IS_STATUSBAR_VISIBLE,
                 DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                /* shouldExcludeCaptionFromAppBounds */ true);
+                /* shouldExcludeCaptionFromAppBounds */ true,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         // Force consuming flags are disabled.
         assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
@@ -650,11 +668,14 @@
                 DEFAULT_IS_STATUSBAR_VISIBLE,
                 DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
         assertThat(
@@ -728,11 +749,14 @@
                 DEFAULT_IS_STATUSBAR_VISIBLE,
                 DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
                 /* inFullImmersiveMode */ true,
+                DEFAULT_IS_DRAGGING,
                 insetsState,
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         // Takes status bar inset as padding, ignores caption bar inset.
         assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -755,11 +779,14 @@
                 DEFAULT_IS_STATUSBAR_VISIBLE,
                 DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
                 /* inFullImmersiveMode */ true,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsInsetSource).isFalse();
     }
@@ -781,11 +808,14 @@
                 /* isStatusBarVisible */ false,
                 DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         // Header is always shown because it's assumed the status bar is always visible.
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -807,11 +837,14 @@
                 /* isStatusBarVisible */ true,
                 /* isKeyguardVisibleAndOccluded */ false,
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
     }
@@ -832,11 +865,14 @@
                 /* isStatusBarVisible */ false,
                 DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -857,11 +893,14 @@
                 DEFAULT_IS_STATUSBAR_VISIBLE,
                 /* isKeyguardVisibleAndOccluded */ true,
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -883,11 +922,14 @@
                 /* isStatusBarVisible */ true,
                 DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
                 /* inFullImmersiveMode */ true,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isTrue();
 
@@ -901,16 +943,48 @@
                 /* isStatusBarVisible */ false,
                 DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
                 /* inFullImmersiveMode */ true,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX)
+    public void updateRelayoutParams_header_fullyImmersive_captionVisDuringDrag() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                mMockSplitScreenController,
+                DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+                DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+                /* isStatusBarVisible */ false,
+                DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+                /* inFullImmersiveMode */ true,
+                /* isDragging */ true,
+                new InsetsState(),
+                DEFAULT_HAS_GLOBAL_FOCUS,
+                mExclusionRegion,
+                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
+
+        assertThat(relayoutParams.mIsCaptionVisible).isTrue();
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
     public void updateRelayoutParams_header_fullyImmersive_overKeyguard_captionNotVisible() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -927,11 +1001,14 @@
                 DEFAULT_IS_STATUSBAR_VISIBLE,
                 /* isKeyguardVisibleAndOccluded */ true,
                 /* inFullImmersiveMode */ true,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
 
         assertThat(relayoutParams.mIsCaptionVisible).isFalse();
     }
@@ -962,6 +1039,65 @@
         assertThat(relayoutParams.mAsyncViewHost).isFalse();
     }
 
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
+    public void updateRelayoutParams_handle_movingToBack_captionNotVisible() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                mMockSplitScreenController,
+                DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+                DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+                DEFAULT_IS_STATUSBAR_VISIBLE,
+                DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+                DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
+                new InsetsState(),
+                DEFAULT_HAS_GLOBAL_FOCUS,
+                mExclusionRegion,
+                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                /* isMovingToBack= */ true);
+
+        assertThat(relayoutParams.mIsCaptionVisible).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
+    public void updateRelayoutParams_handle_inRecentsTransition_captionNotVisible() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        final RelayoutParams relayoutParams = new RelayoutParams();
+
+        DesktopModeWindowDecoration.updateRelayoutParams(
+                relayoutParams,
+                mTestableContext,
+                taskInfo,
+                mMockSplitScreenController,
+                DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+                DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+                DEFAULT_IS_STATUSBAR_VISIBLE,
+                DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+                DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
+                new InsetsState(),
+                DEFAULT_HAS_GLOBAL_FOCUS,
+                mExclusionRegion,
+                DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                /* isRecentsTransitionRunning= */ true,
+                DEFAULT_IS_MOVING_TO_BACK);
+
+        assertThat(relayoutParams.mIsCaptionVisible).isFalse();
+    }
+
     @Test
     public void relayout_fullscreenTask_appliesTransactionImmediately() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -1588,11 +1724,14 @@
                 DEFAULT_IS_STATUSBAR_VISIBLE,
                 DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
                 DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+                DEFAULT_IS_DRAGGING,
                 new InsetsState(),
                 DEFAULT_HAS_GLOBAL_FOCUS,
                 mExclusionRegion,
                 DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
-                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+                DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+                DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+                DEFAULT_IS_MOVING_TO_BACK);
     }
 
     private DesktopModeWindowDecoration createWindowDecoration(
@@ -1635,9 +1774,9 @@
                 taskInfo, mMockSurfaceControl, mMockHandler, mMainExecutor,
                 mMockMainCoroutineDispatcher, mMockBgCoroutineScope, mBgExecutor,
                 mMockChoreographer, mMockSyncQueue, mMockAppHeaderViewHolderFactory,
-                mMockRootTaskDisplayAreaOrganizer, mMockGenericLinksParser,
-                mMockAssistContentRequester, SurfaceControl.Builder::new, mMockTransactionSupplier,
-                WindowContainerTransaction::new, SurfaceControl::new,
+                mMockAppHandleViewHolderFactory, mMockRootTaskDisplayAreaOrganizer,
+                mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
+                mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
                 new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
                 mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
                 mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger,
@@ -1764,7 +1903,7 @@
                 @NonNull DisplayController displayController,
                 @NonNull ActivityManager.RunningTaskInfo taskInfo,
                 @NonNull Context decorWindowContext,
-                @NonNull Function2<? super Integer,? super Integer,? extends PointF>
+                @NonNull Function2<? super Integer,? super Integer,? extends Point>
                     positionSupplier,
                 @NonNull Supplier<SurfaceControl.Transaction> transactionSupplier) {
             return mMaximizeMenu;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
index 7341e09..3600997 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
@@ -40,11 +40,13 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.function.Consumer
 import java.util.function.Supplier
+import org.junit.After
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argThat
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.verify
@@ -63,6 +65,15 @@
     private val testBgExecutor = TestShellExecutor()
     private val mockWindowSession = mock<IWindowSession>()
     private val mockInputEventReceiver = mock<TaskResizeInputEventReceiver>()
+    private val inputChannel = mock<InputChannel>()
+    private val sinkInputChannel = mock<InputChannel>()
+    private val decorationSurface = SurfaceControl.Builder().setName("decoration surface").build()
+    private val createdSurfaces = ArrayList<SurfaceControl>()
+
+    @After
+    fun tearDown() {
+        decorationSurface.release()
+    }
 
     @Test
     fun testGrantInputChannelOffMainThread() {
@@ -73,6 +84,35 @@
     }
 
     @Test
+    fun testGrantInputChannelAfterDecorSurfaceReleased() {
+        // Keep tracking the underlying surface that the decorationSurface points to.
+        val forVerification = SurfaceControl(decorationSurface, "forVerification")
+        try {
+            create()
+            decorationSurface.release()
+            testBgExecutor.flushAll()
+
+            verify(mockWindowSession)
+                .grantInputChannel(
+                    anyInt(),
+                    argThat<SurfaceControl> { isValid && isSameSurface(forVerification) },
+                    any(),
+                    anyOrNull(),
+                    anyInt(),
+                    anyInt(),
+                    anyInt(),
+                    anyInt(),
+                    anyOrNull(),
+                    any(),
+                    any(),
+                    any(),
+                )
+        } finally {
+            forVerification.release()
+        }
+    }
+
+    @Test
     fun testInitializationCallback_waitsForBgSetup() {
         val inputListener = create()
 
@@ -143,6 +183,40 @@
         verify(mockWindowSession).remove(inputListener.mSinkClientToken)
     }
 
+    @Test
+    fun testClose_afterBgSetup_disposesOfInputChannels() {
+        val inputListener = create()
+        testBgExecutor.flushAll()
+        inputListener.close()
+        testMainExecutor.flushAll()
+        verify(inputChannel).dispose()
+        verify(sinkInputChannel).dispose()
+    }
+
+    @Test
+    fun testClose_beforeBgSetup_releaseSurfaces() {
+        val inputListener = create()
+        inputListener.close()
+        testBgExecutor.flushAll()
+        testMainExecutor.flushAll()
+
+        assertThat(createdSurfaces).hasSize(1)
+        assertThat(createdSurfaces[0].isValid).isFalse()
+    }
+
+    @Test
+    fun testClose_afterBgSetup_releaseSurfaces() {
+        val inputListener = create()
+        testBgExecutor.flushAll()
+        inputListener.close()
+        testMainExecutor.flushAll()
+        testBgExecutor.flushAll()
+
+        assertThat(createdSurfaces).hasSize(2)
+        assertThat(createdSurfaces[0].isValid).isFalse()
+        assertThat(createdSurfaces[1].isValid).isFalse()
+    }
+
     private fun verifyNoInputChannelGrantRequests() {
         verify(mockWindowSession, never())
             .grantInputChannel(
@@ -172,12 +246,26 @@
             TestHandler(Looper.getMainLooper()),
             mock<Choreographer>(),
             Display.DEFAULT_DISPLAY,
-            mock<SurfaceControl>(),
+            decorationSurface,
             mock<DragPositioningCallback>(),
-            { SurfaceControl.Builder() },
-            { StubTransaction() },
+            {
+                object : SurfaceControl.Builder() {
+                    override fun build(): SurfaceControl {
+                        return super.build().also { createdSurfaces.add(it) }
+                    }
+                }
+            },
+            {
+                object : StubTransaction() {
+                    override fun remove(sc: SurfaceControl): SurfaceControl.Transaction {
+                        return super.remove(sc).also { sc.release() }
+                    }
+                }
+            },
             mock<DisplayController>(),
             mock<DesktopModeEventLogger>(),
+            inputChannel,
+            sinkInputChannel,
         )
 
     private class TestInitializationCallback : Runnable {
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 f984f6d..2e46f63 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
@@ -98,8 +98,6 @@
 
     private lateinit var handleMenu: HandleMenu
 
-    private val menuWidthWithElevation = MENU_WIDTH + MENU_PILL_ELEVATION
-
     @Before
     fun setUp() {
         val mockAdditionalViewHostViewContainer = AdditionalViewHostViewContainer(
@@ -126,7 +124,6 @@
             addOverride(R.dimen.desktop_mode_handle_menu_height, MENU_HEIGHT)
             addOverride(R.dimen.desktop_mode_handle_menu_margin_top, MENU_TOP_MARGIN)
             addOverride(R.dimen.desktop_mode_handle_menu_margin_start, MENU_START_MARGIN)
-            addOverride(R.dimen.desktop_mode_handle_menu_pill_elevation, MENU_PILL_ELEVATION)
             addOverride(
                 R.dimen.desktop_mode_handle_menu_pill_spacing_margin, MENU_PILL_SPACING_MARGIN)
         }
@@ -141,7 +138,7 @@
         assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
         // Verify menu is created at coordinates that, when added to WindowManager,
         // show at the top-center of display.
-        val expected = Point(DISPLAY_BOUNDS.centerX() - menuWidthWithElevation / 2, MENU_TOP_MARGIN)
+        val expected = Point(DISPLAY_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
         assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
     }
 
@@ -165,7 +162,7 @@
         // Verify menu is created at coordinates that, when added to WindowManager,
         // show at the top-center of split left task.
         val expected = Point(
-            SPLIT_LEFT_BOUNDS.centerX() - menuWidthWithElevation / 2,
+            SPLIT_LEFT_BOUNDS.centerX() - MENU_WIDTH / 2,
             MENU_TOP_MARGIN
         )
         assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
@@ -180,7 +177,7 @@
         // Verify menu is created at coordinates that, when added to WindowManager,
         // show at the top-center of split right task.
         val expected = Point(
-            SPLIT_RIGHT_BOUNDS.centerX() - menuWidthWithElevation / 2,
+            SPLIT_RIGHT_BOUNDS.centerX() - MENU_WIDTH / 2,
             MENU_TOP_MARGIN
         )
         assertEquals(expected.toPointF(), handleMenu.handleMenuPosition)
@@ -323,7 +320,6 @@
         private const val MENU_HEIGHT = 400
         private const val MENU_TOP_MARGIN = 10
         private const val MENU_START_MARGIN = 20
-        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/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index a6b0770..0798613 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -559,6 +559,17 @@
     }
 
     @Test
+    fun testClose() = runOnUiThread {
+        verify(mockDisplayController, times(1))
+            .addDisplayWindowListener(eq(taskPositioner))
+
+        taskPositioner.close()
+
+        verify(mockDisplayController, times(1))
+            .removeDisplayWindowListener(eq(taskPositioner))
+    }
+
+    @Test
     fun testIsResizingOrAnimatingResizeSet() = runOnUiThread {
         Assert.assertFalse(taskPositioner.isResizingOrAnimating)
 
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 fa3d3e4..011c8f0 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
@@ -52,7 +52,7 @@
 import org.mockito.kotlin.never
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 
@@ -216,7 +216,7 @@
 
         veil.hideVeil()
 
-        verifyZeroInteractions(mockTransaction)
+        verifyNoMoreInteractions(mockTransaction)
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index a2927fa..2e95a97 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -60,6 +60,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Handler;
+import android.os.LocaleList;
 import android.testing.AndroidTestingRunner;
 import android.util.DisplayMetrics;
 import android.view.AttachedSurfaceControl;
@@ -97,6 +98,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 import java.util.function.Supplier;
 
 /**
@@ -475,6 +477,50 @@
     }
 
     @Test
+    public void testReinflateViewsOnLocaleListChange() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setVisible(true)
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .build();
+        taskInfo.configuration.setLocales(new LocaleList(Locale.FRANCE, Locale.US));
+        final TestWindowDecoration windowDecor = spy(createWindowDecoration(taskInfo));
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */, Region.obtain());
+        clearInvocations(windowDecor);
+
+        final ActivityManager.RunningTaskInfo taskInfo2 = new TestRunningTaskInfoBuilder()
+                .setVisible(true)
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .build();
+        taskInfo2.configuration.setLocales(new LocaleList(Locale.US, Locale.FRANCE));
+        windowDecor.relayout(taskInfo2, true /* hasGlobalFocus */, Region.obtain());
+        // WindowDecoration#releaseViews should be called since the locale list has changed.
+        verify(windowDecor, times(1)).releaseViews(any());
+    }
+
+    @Test
+    public void testViewNotReinflatedWhenLocaleListNotChanged() {
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController)
+                .getDisplay(Display.DEFAULT_DISPLAY);
+
+        final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+                .setVisible(true)
+                .setDisplayId(Display.DEFAULT_DISPLAY)
+                .build();
+        taskInfo.configuration.setLocales(new LocaleList(Locale.FRANCE, Locale.US));
+        final TestWindowDecoration windowDecor = spy(createWindowDecoration(taskInfo));
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */, Region.obtain());
+        clearInvocations(windowDecor);
+        windowDecor.relayout(taskInfo, true /* hasGlobalFocus */, Region.obtain());
+        // WindowDecoration#releaseViews should not be called since nothing has changed.
+        verify(windowDecor, never()).releaseViews(any());
+    }
+
+    @Test
     public void testLayoutResultCalculation_fullWidthCaption() {
         final Display defaultDisplay = mock(Display.class);
         doReturn(defaultDisplay).when(mMockDisplayController)
@@ -780,6 +826,18 @@
     }
 
     @Test
+    public void testClose_withTaskDragResizerSet_callResizerClose() {
+        final TestWindowDecoration windowDecor = createWindowDecoration(
+                new TestRunningTaskInfoBuilder().build());
+        final TaskDragResizer taskDragResizer = mock(TaskDragResizer.class);
+        windowDecor.setTaskDragResizer(taskDragResizer);
+
+        windowDecor.close();
+
+        verify(taskDragResizer).close();
+    }
+
+    @Test
     public void testRelayout_captionFrameChanged_insetsReapplied() {
         final Display defaultDisplay = mock(Display.class);
         doReturn(defaultDisplay).when(mMockDisplayController)
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
index c61e0eb..714d062 100644
--- 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
@@ -23,6 +23,7 @@
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.graphics.drawable.Drawable
+import android.os.LocaleList
 import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import android.testing.TestableContext
@@ -39,6 +40,7 @@
 import com.android.wm.shell.sysui.UserChangeListener
 import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader.AppResources
 import com.google.common.truth.Truth.assertThat
+import java.util.Locale
 import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Test
@@ -52,7 +54,7 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 /**
@@ -116,12 +118,14 @@
     @Test
     fun testGetName_cached_returnsFromCache() {
         val task = createTaskInfo(context.userId)
+        task.configuration.setLocales(LocaleList(Locale.US))
         loader.onWindowDecorCreated(task)
         loader.taskToResourceCache[task.taskId] = AppResources("App Name", mock(), mock())
+        loader.localeListOnCache[task.taskId] = LocaleList(Locale.US)
 
         loader.getName(task)
 
-        verifyZeroInteractions(
+        verifyNoMoreInteractions(
             mockPackageManager,
             mockIconProvider,
             mockHeaderIconFactory,
@@ -130,6 +134,19 @@
     }
 
     @Test
+    fun testGetName_cached_localesChanged_loadsResourceAndCaches() {
+        val task = createTaskInfo(context.userId)
+        loader.onWindowDecorCreated(task)
+        loader.taskToResourceCache[task.taskId] = AppResources("App Name", mock(), mock())
+        loader.localeListOnCache[task.taskId] = LocaleList(Locale.US, Locale.FRANCE)
+        task.configuration.setLocales(LocaleList(Locale.FRANCE, Locale.US))
+        doReturn("App Name but in French").whenever(mockPackageManager).getApplicationLabel(any())
+
+        assertThat(loader.getName(task)).isEqualTo("App Name but in French")
+        assertThat(loader.taskToResourceCache[task.taskId]?.appName).isEqualTo("App Name but in French")
+    }
+
+    @Test
     fun testGetHeaderIcon_notCached_loadsResourceAndCaches() {
         val task = createTaskInfo(context.userId)
         loader.onWindowDecorCreated(task)
@@ -148,7 +165,7 @@
 
         loader.getHeaderIcon(task)
 
-        verifyZeroInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
+        verifyNoMoreInteractions(mockPackageManager, mockIconProvider, mockHeaderIconFactory)
     }
 
     @Test
@@ -170,7 +187,7 @@
 
         loader.getVeilIcon(task)
 
-        verifyZeroInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
+        verifyNoMoreInteractions(mockPackageManager, mockIconProvider, mockVeilIconFactory)
     }
 
     @Test
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 646ec21..9e148e5 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
@@ -28,6 +28,7 @@
 import com.android.wm.shell.common.ShellExecutor
 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.DesktopTestHelpers.createFreeformTask
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
@@ -63,6 +64,7 @@
     private val transitionsMock: Transitions = mock()
     private val shellTaskOrganizerMock: ShellTaskOrganizer = mock()
     private val userRepositories: DesktopUserRepositories = mock()
+    private val desktopRepository: DesktopRepository = mock()
     private val desktopModeEventLogger: DesktopModeEventLogger = mock()
     private val toggleResizeDesktopTaskTransitionHandlerMock:
         ToggleResizeDesktopTaskTransitionHandler =
@@ -105,6 +107,7 @@
         whenever(contextMock.createContextAsUser(any(), any())).thenReturn(context)
         whenever(contextMock.resources).thenReturn(resourcesMock)
         whenever(resourcesMock.getDimensionPixelSize(any())).thenReturn(10)
+        whenever(userRepositories.current).thenReturn(desktopRepository)
     }
 
     @Test
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 bc8faed..e5f8d7d 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
@@ -16,6 +16,7 @@
 package com.android.wm.shell.windowdecor.tiling
 
 import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
 import android.content.res.Resources
 import android.graphics.Rect
@@ -24,8 +25,10 @@
 import android.view.MotionEvent
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_PIP
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
 import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -40,6 +43,7 @@
 import com.android.wm.shell.desktopmode.DesktopRepository
 import com.android.wm.shell.desktopmode.DesktopTasksController
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.createPinnedTask
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
 import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
@@ -552,6 +556,37 @@
     }
 
     @Test
+    fun taskTiled_shouldBeRemoved_whenEnteringPip() {
+        val task1 = createPipTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+        whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+        whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+        tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+        val changeInfo = createPipChangeTransition(task1)
+        tilingDecoration.onTransitionReady(
+            transition = mock(),
+            info = changeInfo,
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        assertThat(tilingDecoration.leftTaskResizingHelper).isNull()
+        verify(tiledTaskHelper, times(1)).dispose()
+    }
+
+    @Test
     fun taskNotTiled_shouldNotBeRemoved_whenNotTiled() {
         val task1 = createVisibleTask()
         val task2 = createVisibleTask()
@@ -618,6 +653,64 @@
         verify(context, never()).getApplicationContext()
     }
 
+    @Test
+    fun addLeftTiledTask_updatesTaskRepository_whenLeftTileInitializedOrBroken() {
+        val task1 = createVisibleTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+        whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+        whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+
+        verify(desktopRepository, times(1)).addLeftTiledTask(displayId, task1.taskId)
+        verify(desktopRepository, never()).addRightTiledTask(displayId, task1.taskId)
+
+        tilingDecoration.removeTaskIfTiled(task1.taskId)
+
+        verify(desktopRepository, times(1)).removeLeftTiledTask(displayId)
+        verify(desktopRepository, never()).removeRightTiledTask(displayId)
+    }
+
+    @Test
+    fun addRightTiledTask_updatesTaskRepository_whenRightTileInitializedOrBroken() {
+        val task1 = createVisibleTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+        whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+        whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.RIGHT,
+            BOUNDS,
+        )
+
+        verify(desktopRepository, times(1)).addRightTiledTask(displayId, task1.taskId)
+        verify(desktopRepository, never()).addLeftTiledTask(displayId, task1.taskId)
+
+        tilingDecoration.removeTaskIfTiled(task1.taskId)
+
+        verify(desktopRepository, times(1)).removeRightTiledTask(displayId)
+        verify(desktopRepository, never()).removeLeftTiledTask(displayId)
+    }
+
     private fun initTiledTaskHelperMock(taskInfo: ActivityManager.RunningTaskInfo) {
         whenever(tiledTaskHelper.bounds).thenReturn(BOUNDS)
         whenever(tiledTaskHelper.taskInfo).thenReturn(taskInfo)
@@ -652,6 +745,23 @@
             whenever(userRepositories.current.isVisibleTask(eq(it.taskId))).thenReturn(true)
         }
 
+    private fun createPipTask() =
+        createPinnedTask().also {
+            whenever(userRepositories.current.isVisibleTask(eq(it.taskId))).thenReturn(true)
+        }
+
+    private fun createPipChangeTransition(task: RunningTaskInfo?, type: Int = TRANSIT_PIP) =
+        TransitionInfo(type, /* flags= */ 0).apply {
+            addChange(
+                Change(mock(), mock()).apply {
+                    mode = TRANSIT_PIP
+                    parent = null
+                    taskInfo = task
+                    flags = flags
+                }
+            )
+        }
+
     companion object {
         private val NON_STABLE_BOUNDS_MOCK = Rect(50, 55, 100, 100)
         private val STABLE_BOUNDS_MOCK = Rect(0, 0, 100, 100)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
new file mode 100644
index 0000000..bc4865a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.viewholder
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.graphics.Point
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.view.View
+import android.widget.ImageButton
+import androidx.test.filters.SmallTest
+import com.android.internal.policy.SystemBarUtils
+import com.android.wm.shell.R
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.WindowManagerWrapper
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import kotlin.test.Test
+
+/**
+ * Tests for [AppHandleViewHolder].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:AppHandleViewHolderTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class AppHandleViewHolderTest : ShellTestCase() {
+
+    companion object {
+        private const val CAPTION_WIDTH = 500
+        private const val CAPTION_HEIGHT = 100
+    }
+
+    private val mockView = mock<View>()
+    private val mockImageButton = mock<ImageButton>()
+    private val mockOnTouchListener = mock<View.OnTouchListener>()
+    private val mockOnClickListener = mock<View.OnClickListener>()
+    private val mockWindowManagerWrapper = mock<WindowManagerWrapper>()
+    private val mockHandler = mock<Handler>()
+    private val mockTaskInfo = mock<RunningTaskInfo>()
+
+    @Before
+    fun setup() {
+        whenever(mockView.context).thenReturn(mContext)
+        whenever(mockView.requireViewById<View>(R.id.desktop_mode_caption))
+            .thenReturn(mockView)
+        whenever(mockView.requireViewById<ImageButton>(R.id.caption_handle))
+            .thenReturn(mockImageButton)
+    }
+
+    @Test
+    fun statusBarInputLayer_disposedWhenCaptionBelowStatusBar() {
+        val appHandleViewHolder: AppHandleViewHolder = spy(createAppHandleViewHolder())
+        val captionPosition = Point(0, SystemBarUtils.getStatusBarHeight(mContext) + 10)
+
+        appHandleViewHolder.bindData(
+            AppHandleViewHolder.HandleData(
+                taskInfo = mockTaskInfo,
+                position = captionPosition,
+                width = CAPTION_WIDTH,
+                height = CAPTION_HEIGHT,
+                showInputLayer = false
+            )
+        )
+
+        verify(appHandleViewHolder).disposeStatusBarInputLayer()
+    }
+
+    private fun createAppHandleViewHolder(): AppHandleViewHolder {
+        return AppHandleViewHolder(
+            mockView,
+            mockOnTouchListener,
+            mockOnClickListener,
+            mockWindowManagerWrapper,
+            mockHandler
+        )
+    }
+}
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 5a4cff0..4e820b6 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -453,7 +453,7 @@
 {
     assert(mFp == NULL);    // no reopen
     assert(!mMap.has_value());
-    assert(dataMap != NULL);
+    assert(dataMap.data());
 
     mMap = std::move(dataMap);
     mStart = -1;            // not used
@@ -798,7 +798,7 @@
 {
     assert(mFd < 0);        // no re-open
     assert(!mMap.has_value());
-    assert(dataMap != NULL);
+    assert(dataMap.data());
 
     mMap = std::move(dataMap);
     mStart = -1;        // not used
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index a18c5f5..8ecd6ba 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -6520,41 +6520,79 @@
 }
 
 bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const {
-    if (mError != NO_ERROR) {
-        return false;
-    }
+  if (mError != NO_ERROR) {
+    return false;
+  }
 
-    const ssize_t p = getResourcePackageIndex(resID);
-    const int t = Res_GETTYPE(resID);
-    const int e = Res_GETENTRY(resID);
+  const ssize_t p = getResourcePackageIndex(resID);
+  const int t = Res_GETTYPE(resID);
+  const int e = Res_GETENTRY(resID);
 
-    if (p < 0) {
-        if (Res_GETPACKAGE(resID)+1 == 0) {
-            ALOGW("No package identifier when getting flags for resource number 0x%08x", resID);
-        } else {
-            ALOGW("No known package when getting flags for resource number 0x%08x", resID);
-        }
-        return false;
+  if (p < 0) {
+    if (Res_GETPACKAGE(resID)+1 == 0) {
+      ALOGW("No package identifier when getting flags for resource number 0x%08x", resID);
+    } else {
+      ALOGW("No known package when getting flags for resource number 0x%08x", resID);
     }
-    if (t < 0) {
-        ALOGW("No type identifier when getting flags for resource number 0x%08x", resID);
-        return false;
-    }
+    return false;
+  }
+  if (t < 0) {
+    ALOGW("No type identifier when getting flags for resource number 0x%08x", resID);
+    return false;
+  }
 
-    const PackageGroup* const grp = mPackageGroups[p];
-    if (grp == NULL) {
-        ALOGW("Bad identifier when getting flags for resource number 0x%08x", resID);
-        return false;
-    }
+  const PackageGroup* const grp = mPackageGroups[p];
+  if (grp == NULL) {
+    ALOGW("Bad identifier when getting flags for resource number 0x%08x", resID);
+    return false;
+  }
 
-    Entry entry;
-    status_t err = getEntry(grp, t, e, NULL, &entry);
-    if (err != NO_ERROR) {
-        return false;
-    }
+  Entry entry;
+  status_t err = getEntry(grp, t, e, NULL, &entry);
+  if (err != NO_ERROR) {
+    return false;
+  }
 
-    *outFlags = entry.specFlags;
-    return true;
+  *outFlags = entry.specFlags;
+  return true;
+}
+
+bool ResTable::getResourceEntryFlags(uint32_t resID, uint32_t* outFlags) const {
+  if (mError != NO_ERROR) {
+    return false;
+  }
+
+  const ssize_t p = getResourcePackageIndex(resID);
+  const int t = Res_GETTYPE(resID);
+  const int e = Res_GETENTRY(resID);
+
+  if (p < 0) {
+    if (Res_GETPACKAGE(resID)+1 == 0) {
+      ALOGW("No package identifier when getting flags for resource number 0x%08x", resID);
+    } else {
+      ALOGW("No known package when getting flags for resource number 0x%08x", resID);
+    }
+    return false;
+  }
+  if (t < 0) {
+    ALOGW("No type identifier when getting flags for resource number 0x%08x", resID);
+    return false;
+  }
+
+  const PackageGroup* const grp = mPackageGroups[p];
+  if (grp == NULL) {
+    ALOGW("Bad identifier when getting flags for resource number 0x%08x", resID);
+    return false;
+  }
+
+  Entry entry;
+  status_t err = getEntry(grp, t, e, NULL, &entry);
+  if (err != NO_ERROR) {
+    return false;
+  }
+
+  *outFlags = entry.entry->flags();
+  return true;
 }
 
 bool ResTable::isPackageDynamic(uint8_t packageID) const {
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 30594dc..63b28da 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1265,6 +1265,9 @@
     // Varies in length from 3 to 8 chars. Zero-filled value.
     char localeNumberingSystem[8];
 
+    // Mark all padding explicitly so it's clear how much we can expand it.
+    char endPadding[3];
+
     void copyFromDeviceNoSwap(const ResTable_config& o) {
       const auto o_size = dtohl(o.size);
       if (o_size >= sizeof(ResTable_config)) [[likely]] {
@@ -1422,6 +1425,13 @@
     void swapHtoD_slow();
 };
 
+// Fix the struct size for backward compatibility
+static_assert(sizeof(ResTable_config) == 64);
+
+// Make sure there's no unaccounted padding in the structure.
+static_assert(offsetof(ResTable_config, endPadding) +
+                  sizeof(ResTable_config::endPadding) == sizeof(ResTable_config));
+
 /**
  * A specification of the resources defined by a particular type.
  *
@@ -1583,6 +1593,8 @@
         // If set, this is a compact entry with data type and value directly
         // encoded in the this entry, see ResTable_entry::compact
         FLAG_COMPACT = 0x0008,
+        // If set, this entry relies on read write android feature flags
+        FLAG_USES_FEATURE_FLAGS = 0x0010,
     };
 
     struct Full {
@@ -1612,6 +1624,7 @@
     uint16_t flags()  const { return dtohs(full.flags); };
     bool is_compact() const { return flags() & FLAG_COMPACT; }
     bool is_complex() const { return flags() & FLAG_COMPLEX; }
+    bool uses_feature_flags() const { return flags() & FLAG_USES_FEATURE_FLAGS; }
 
     size_t size() const {
         return is_compact() ? sizeof(ResTable_entry) : dtohs(this->full.size);
@@ -2029,6 +2042,8 @@
 
     bool getResourceFlags(uint32_t resID, uint32_t* outFlags) const;
 
+    bool getResourceEntryFlags(uint32_t resID, uint32_t* outFlags) const;
+
     /**
      * Returns whether or not the package for the given resource has been dynamically assigned.
      * If the resource can't be found, returns 'false'.
diff --git a/libs/hostgraphics/HostBufferQueue.cpp b/libs/hostgraphics/HostBufferQueue.cpp
index 7e14b88..ef54062 100644
--- a/libs/hostgraphics/HostBufferQueue.cpp
+++ b/libs/hostgraphics/HostBufferQueue.cpp
@@ -29,6 +29,7 @@
     }
 
     virtual status_t detachBuffer(int slot) {
+        mBuffer.clear();
         return OK;
     }
 
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index a210ddf..7d227f7 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -722,10 +722,15 @@
 
             auto canvas = SkCanvas(recycledPixels->getSkBitmap());
             SkRect destination = SkRect::Make(recycledPixels->info().bounds());
-            destination.intersect(SkRect::Make(mSkiaBitmap->info().bounds()));
-            canvas.drawImageRect(mSkiaBitmap->asImage(), *mDesiredSubset, destination,
-                                 SkSamplingOptions(SkFilterMode::kLinear), nullptr,
-                                 SkCanvas::kFast_SrcRectConstraint);
+            if (destination.intersect(SkRect::Make(mSkiaBitmap->info().bounds()))) {
+                canvas.drawImageRect(mSkiaBitmap->asImage(), *mDesiredSubset, destination,
+                                     SkSamplingOptions(SkFilterMode::kLinear), nullptr,
+                                     SkCanvas::kFast_SrcRectConstraint);
+            } else {
+                // The canvas would have discarded the draw operation automatically, but
+                // this case should have been detected before getting to this point.
+                ALOGE("Copy destination does not intersect image bounds");
+            }
         } else {
             void* dst = recycledPixels->pixels();
             const size_t dstRowBytes = mRecycledBitmap->rowBytes();
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index dc669a5..aa8cbd1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -175,6 +175,8 @@
     if (stream->isValid()) {
         mOpenMultiPicStream = std::move(stream);
         mSerialContext.reset(new SkSharingSerialContext());
+        // passing the GrDirectContext to the SerialContext allows us to raster/serialize GPU images
+        mSerialContext->setDirectContext(mRenderThread.getGrContext());
         SkSerialProcs procs;
         procs.fImageProc = SkSharingSerialContext::serializeImage;
         procs.fImageCtx = mSerialContext.get();
diff --git a/location/java/android/location/GnssClock.java b/location/java/android/location/GnssClock.java
index 62f50b5..6930f36 100644
--- a/location/java/android/location/GnssClock.java
+++ b/location/java/android/location/GnssClock.java
@@ -349,7 +349,7 @@
      * Gets the clock's Drift in nanoseconds per second.
      *
      * <p>This value is the instantaneous time-derivative of the value provided by
-     * {@link #getBiasNanos()}.
+     * the sum of {@link #getFullBiasNanos()} and {@link #getBiasNanos()}.
      *
      * <p>A positive value indicates that the frequency is higher than the nominal (e.g. GPS master
      * clock) frequency. The error estimate for this reported drift is
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 83b1778..f26e72f 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -167,6 +167,7 @@
     namespace: "location"
     description: "Flag for GNSS assistance interface"
     bug: "209078566"
+    is_exported: true
 }
 
 flag {
@@ -179,3 +180,28 @@
     }
 }
 
+flag {
+    name: "gnss_location_provider_overlay_2025_devices"
+    namespace: "location"
+    description: "Flag for GNSS location provider overlay for 2025 devices"
+    bug: "398254728"
+    is_fixed_read_only: true
+}
+
+flag {
+    name: "limit_fused_gps"
+    namespace: "location"
+    description: "Limits when GPS can be used for fused location requests"
+    bug: "401885179"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "gnss_assistance_interface_jni"
+    namespace: "location"
+    description: "Flag for GNSS assistance interface JNI"
+    bug: "209078566"
+}
+
diff --git a/media/java/Android.bp b/media/java/Android.bp
index 6878f9d..28b9d3b 100644
--- a/media/java/Android.bp
+++ b/media/java/Android.bp
@@ -15,6 +15,7 @@
     ],
     exclude_srcs: [
         ":framework-media-tv-tunerresourcemanager-sources-aidl",
+        ":framework-media-quality-sources-aidl",
     ],
     visibility: [
         "//frameworks/base",
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d082c73..32af7c6 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4857,7 +4857,7 @@
             focusReceiver = addClientIdToFocusReceiverLocked(clientFakeId);
         }
 
-        return handleExternalAudioPolicyWaitIfNeeded(clientFakeId, focusReceiver);
+        return handleExternalAudioPolicyWaitIfNeeded(clientFakeId, focusReceiver, afr);
     }
 
     /**
@@ -5070,7 +5070,7 @@
             focusReceiver = addClientIdToFocusReceiverLocked(clientId);
         }
 
-        return handleExternalAudioPolicyWaitIfNeeded(clientId, focusReceiver);
+        return handleExternalAudioPolicyWaitIfNeeded(clientId, focusReceiver, afr);
     }
 
     @GuardedBy("mFocusRequestsLock")
@@ -5086,11 +5086,20 @@
     }
 
     private @FocusRequestResult int handleExternalAudioPolicyWaitIfNeeded(String clientId,
-            BlockingFocusResultReceiver focusReceiver) {
+            BlockingFocusResultReceiver focusReceiver, @NonNull AudioFocusRequest afr) {
         focusReceiver.waitForResult(EXT_FOCUS_POLICY_TIMEOUT_MS);
-        if (DEBUG && !focusReceiver.receivedResult()) {
-            Log.e(TAG, "handleExternalAudioPolicyWaitIfNeeded"
-                    + " response from ext policy timed out, denying request");
+        if (!focusReceiver.receivedResult()) {
+            if (DEBUG) {
+                Log.e(TAG, "handleExternalAudioPolicyWaitIfNeeded"
+                        + " response from ext policy timed out, denying request");
+            }
+            try {
+                // To prevent from orphan focus holder, cleanup
+                abandonAudioFocus(afr.getOnAudioFocusChangeListener());
+            } catch (Exception e) {
+                Log.e(TAG, "handleExternalAudioPolicyWaitIfNeeded failed to abandon audio"
+                        +" focus after time out, error: " + e.getMessage());
+            }
         }
 
         synchronized (mFocusRequestsLock) {
diff --git a/media/java/android/media/IMediaRouter2.aidl b/media/java/android/media/IMediaRouter2.aidl
index 85bc8ef..e9590d5 100644
--- a/media/java/android/media/IMediaRouter2.aidl
+++ b/media/java/android/media/IMediaRouter2.aidl
@@ -18,6 +18,7 @@
 
 import android.media.MediaRoute2Info;
 import android.media.RoutingSessionInfo;
+import android.media.SuggestedDeviceInfo;
 import android.os.Bundle;
 import android.os.UserHandle;
 
@@ -37,4 +38,6 @@
      */
     void requestCreateSessionByManager(long uniqueRequestId, in RoutingSessionInfo oldSession,
         in MediaRoute2Info route);
+    void notifyDeviceSuggestionsUpdated(String suggestingPackageName,
+        in List<SuggestedDeviceInfo> suggestions);
 }
diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl
index 21908b2..1c399d6 100644
--- a/media/java/android/media/IMediaRouter2Manager.aidl
+++ b/media/java/android/media/IMediaRouter2Manager.aidl
@@ -21,6 +21,7 @@
 import android.media.RouteDiscoveryPreference;
 import android.media.RouteListingPreference;
 import android.media.RoutingSessionInfo;
+import android.media.SuggestedDeviceInfo;
 
 /**
  * {@hide}
@@ -33,6 +34,8 @@
             in RouteDiscoveryPreference discoveryPreference);
     void notifyRouteListingPreferenceChange(String packageName,
             in @nullable RouteListingPreference routeListingPreference);
+    void notifyDeviceSuggestionsUpdated(String packageName, String suggestingPackageName,
+            in @nullable List<SuggestedDeviceInfo> suggestedDeviceInfo);
     void notifyRoutesUpdated(in List<MediaRoute2Info> routes);
     void notifyRequestFailed(int requestId, int reason);
     void invalidateInstance();
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 961962f..60881f4 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -25,6 +25,7 @@
 import android.media.RouteDiscoveryPreference;
 import android.media.RouteListingPreference;
 import android.media.RoutingSessionInfo;
+import android.media.SuggestedDeviceInfo;
 import android.os.Bundle;
 import android.os.UserHandle;
 /**
@@ -72,6 +73,10 @@
             in MediaRoute2Info route);
     void setSessionVolumeWithRouter2(IMediaRouter2 router, String sessionId, int volume);
     void releaseSessionWithRouter2(IMediaRouter2 router, String sessionId);
+    void setDeviceSuggestionsWithRouter2(IMediaRouter2 router,
+            in @nullable List<SuggestedDeviceInfo> suggestedDeviceInfo);
+    @nullable Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestionsWithRouter2(
+            IMediaRouter2 router);
 
     // Methods for MediaRouter2Manager
     List<RoutingSessionInfo> getRemoteSessions(IMediaRouter2Manager manager);
@@ -98,4 +103,8 @@
             String sessionId, int volume);
     void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId);
     boolean showMediaOutputSwitcherWithProxyRouter(IMediaRouter2Manager manager);
+    void setDeviceSuggestionsWithManager(IMediaRouter2Manager manager,
+            in @nullable List<SuggestedDeviceInfo> suggestedDeviceInfo);
+    @nullable Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestionsWithManager(
+            IMediaRouter2Manager manager);
 }
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 4e86eac..f3b21bf 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -3836,6 +3836,151 @@
                         maxBlocks, maxBlocksPerSecond,
                         blockSize, blockSize,
                         1 /* widthAlignment */, 1 /* heightAlignment */);
+            } else if (GetFlag(() -> android.media.codec.Flags.apvSupport())
+                        && mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_APV)) {
+                maxBlocksPerSecond = 11880;
+                maxBps = 7000000;
+
+                // Sample rate, and Bit rate for APV Codec,
+                // corresponding to the definitions in
+                // "10.1.4. Levels and bands"
+                // found at https://www.ietf.org/archive/id/draft-lim-apv-03.html
+                for (CodecProfileLevel profileLevel: profileLevels) {
+                    long SR = 0; // luma sample rate
+                    int BR = 0;  // bit rate bps
+                    switch (profileLevel.level) {
+                        case CodecProfileLevel.APVLevel1Band0:
+                            SR =      3041280; BR =    7000000; break;
+                        case CodecProfileLevel.APVLevel1Band1:
+                            SR =      3041280; BR =   11000000; break;
+                        case CodecProfileLevel.APVLevel1Band2:
+                            SR =      3041280; BR =   14000000; break;
+                        case CodecProfileLevel.APVLevel1Band3:
+                            SR =      3041280; BR =   21000000; break;
+                        case CodecProfileLevel.APVLevel11Band0:
+                            SR =      6082560; BR =   14000000; break;
+                        case CodecProfileLevel.APVLevel11Band1:
+                            SR =      6082560; BR =   21000000; break;
+                        case CodecProfileLevel.APVLevel11Band2:
+                            SR =      6082560; BR =   28000000; break;
+                        case CodecProfileLevel.APVLevel11Band3:
+                            SR =      6082560; BR =   42000000; break;
+                        case CodecProfileLevel.APVLevel2Band0:
+                            SR =     15667200; BR =   36000000; break;
+                        case CodecProfileLevel.APVLevel2Band1:
+                            SR =     15667200; BR =   53000000; break;
+                        case CodecProfileLevel.APVLevel2Band2:
+                            SR =     15667200; BR =   71000000; break;
+                        case CodecProfileLevel.APVLevel2Band3:
+                            SR =     15667200; BR =  106000000; break;
+                        case CodecProfileLevel.APVLevel21Band0:
+                            SR =     31334400; BR =   71000000; break;
+                        case CodecProfileLevel.APVLevel21Band1:
+                            SR =     31334400; BR =  106000000; break;
+                        case CodecProfileLevel.APVLevel21Band2:
+                            SR =     31334400; BR =  141000000; break;
+                        case CodecProfileLevel.APVLevel21Band3:
+                            SR =     31334400; BR =  212000000; break;
+                        case CodecProfileLevel.APVLevel3Band0:
+                            SR =     66846720; BR =  101000000; break;
+                        case CodecProfileLevel.APVLevel3Band1:
+                            SR =     66846720; BR =  151000000; break;
+                        case CodecProfileLevel.APVLevel3Band2:
+                            SR =     66846720; BR =  201000000; break;
+                        case CodecProfileLevel.APVLevel3Band3:
+                            SR =     66846720; BR =  301000000; break;
+                        case CodecProfileLevel.APVLevel31Band0:
+                            SR =    133693440; BR =  201000000; break;
+                        case CodecProfileLevel.APVLevel31Band1:
+                            SR =    133693440; BR =  301000000; break;
+                        case CodecProfileLevel.APVLevel31Band2:
+                            SR =    133693440; BR =  401000000; break;
+                        case CodecProfileLevel.APVLevel31Band3:
+                            SR =    133693440; BR =  602000000; break;
+                        case CodecProfileLevel.APVLevel4Band0:
+                            SR =    265420800; BR =  401000000; break;
+                        case CodecProfileLevel.APVLevel4Band1:
+                            SR =    265420800; BR =  602000000; break;
+                        case CodecProfileLevel.APVLevel4Band2:
+                            SR =    265420800; BR =  780000000; break;
+                        case CodecProfileLevel.APVLevel4Band3:
+                            SR =    265420800; BR = 1170000000; break;
+                        case CodecProfileLevel.APVLevel41Band0:
+                            SR =    530841600; BR =  780000000; break;
+                        case CodecProfileLevel.APVLevel41Band1:
+                            SR =    530841600; BR = 1170000000; break;
+                        case CodecProfileLevel.APVLevel41Band2:
+                            SR =    530841600; BR = 1560000000; break;
+                        case CodecProfileLevel.APVLevel41Band3:
+                            // Current API allows bitrates only up to Max Integer
+                            // Hence we are limiting internal limits to Integer.MAX_VALUE
+                            // even when actual Level/Band limits are higher
+                            SR =    530841600; BR = Integer.MAX_VALUE; break;
+                        case CodecProfileLevel.APVLevel5Band0:
+                            SR =   1061683200; BR = 1560000000; break;
+                        case CodecProfileLevel.APVLevel5Band1:
+                            SR =   1061683200; BR = Integer.MAX_VALUE; break;
+                        case CodecProfileLevel.APVLevel5Band2:
+                            SR =   1061683200; BR = Integer.MAX_VALUE; break;
+                        case CodecProfileLevel.APVLevel5Band3:
+                            SR =   1061683200; BR = Integer.MAX_VALUE; break;
+                        case CodecProfileLevel.APVLevel51Band0:
+                        case CodecProfileLevel.APVLevel51Band1:
+                        case CodecProfileLevel.APVLevel51Band2:
+                        case CodecProfileLevel.APVLevel51Band3:
+                            SR =   2123366400; BR = Integer.MAX_VALUE; break;
+                        case CodecProfileLevel.APVLevel6Band0:
+                        case CodecProfileLevel.APVLevel6Band1:
+                        case CodecProfileLevel.APVLevel6Band2:
+                        case CodecProfileLevel.APVLevel6Band3:
+                            SR =  4777574400L; BR = Integer.MAX_VALUE; break;
+                        case CodecProfileLevel.APVLevel61Band0:
+                        case CodecProfileLevel.APVLevel61Band1:
+                        case CodecProfileLevel.APVLevel61Band2:
+                        case CodecProfileLevel.APVLevel61Band3:
+                            SR =  8493465600L; BR = Integer.MAX_VALUE; break;
+                        case CodecProfileLevel.APVLevel7Band0:
+                        case CodecProfileLevel.APVLevel7Band1:
+                        case CodecProfileLevel.APVLevel7Band2:
+                        case CodecProfileLevel.APVLevel7Band3:
+                            SR = 16986931200L; BR = Integer.MAX_VALUE; break;
+                        case CodecProfileLevel.APVLevel71Band0:
+                        case CodecProfileLevel.APVLevel71Band1:
+                        case CodecProfileLevel.APVLevel71Band2:
+                        case CodecProfileLevel.APVLevel71Band3:
+                            SR = 33973862400L; BR = Integer.MAX_VALUE; break;
+                        default:
+                            Log.w(TAG, "Unrecognized level "
+                                    + profileLevel.level + " for " + mime);
+                            errors |= ERROR_UNRECOGNIZED;
+                    }
+                    switch (profileLevel.profile) {
+                        case CodecProfileLevel.APVProfile422_10:
+                        case CodecProfileLevel.APVProfile422_10HDR10:
+                        case CodecProfileLevel.APVProfile422_10HDR10Plus:
+                            break;
+                        default:
+                            Log.w(TAG, "Unrecognized profile "
+                                    + profileLevel.profile + " for " + mime);
+                            errors |= ERROR_UNRECOGNIZED;
+                    }
+                    errors &= ~ERROR_NONE_SUPPORTED;
+                    maxBlocksPerSecond = Math.max(SR, maxBlocksPerSecond);
+                    maxBps = Math.max(BR, maxBps);
+                }
+
+                final int blockSize = 16;
+                maxBlocks = Integer.MAX_VALUE;
+                maxBlocksPerSecond = Utils.divUp(maxBlocksPerSecond, blockSize * blockSize);
+                maxBlocks = (int) Math.min((long) maxBlocks, maxBlocksPerSecond);
+                // Max frame size in APV is 2^24
+                int maxLengthInBlocks = Utils.divUp((int) Math.pow(2, 24), blockSize);
+                maxLengthInBlocks = Math.min(maxLengthInBlocks, maxBlocks);
+                applyMacroBlockLimits(
+                        maxLengthInBlocks, maxLengthInBlocks,
+                        maxBlocks, maxBlocksPerSecond,
+                        blockSize, blockSize,
+                        2 /* widthAlignment */, 1 /* heightAlignment */);
             } else {
                 Log.w(TAG, "Unsupported mime " + mime);
                 // using minimal bitrate here.  should be overriden by
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index b57476f..6e0821f 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -183,7 +183,17 @@
             appContext.registerReceiver(new VolumeChangeReceiver(),
                     new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
 
-            mDisplayService.registerDisplayListener(this, mHandler);
+            if (com.android.server.display.feature.flags.Flags
+                    .displayListenerPerformanceImprovements()
+                    && com.android.server.display.feature.flags.Flags
+                    .delayImplicitRrRegistrationUntilRrAccessed()) {
+                mDisplayService.registerDisplayListener(this, mHandler,
+                        DisplayManager.EVENT_TYPE_DISPLAY_ADDED
+                                | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
+                                | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED);
+            } else {
+                mDisplayService.registerDisplayListener(this, mHandler);
+            }
 
             AudioRoutesInfo newAudioRoutes = null;
             try {
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 3af36a4..db305ef 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -22,6 +22,7 @@
 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;
+import static com.android.media.flags.Flags.FLAG_ENABLE_SUGGESTED_DEVICE_API;
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
@@ -159,6 +160,8 @@
             new CopyOnWriteArrayList<>();
     private final CopyOnWriteArrayList<ControllerCallbackRecord> mControllerCallbackRecords =
             new CopyOnWriteArrayList<>();
+    private final CopyOnWriteArrayList<DeviceSuggestionsCallbackRecord>
+            mDeviceSuggestionsCallbackRecords = new CopyOnWriteArrayList<>();
 
     private final CopyOnWriteArrayList<ControllerCreationRequest> mControllerCreationRequests =
             new CopyOnWriteArrayList<>();
@@ -198,6 +201,10 @@
     @Nullable
     private RouteListingPreference mRouteListingPreference;
 
+    @GuardedBy("mLock")
+    @Nullable
+    private Map<String, List<SuggestedDeviceInfo>> mSuggestedDeviceInfo = new HashMap<>();
+
     /**
      * Stores an auxiliary copy of {@link #mFilteredRoutes} at the time of the last route callback
      * dispatch. This is only used to determine what callback a route should be assigned to (added,
@@ -760,6 +767,27 @@
     }
 
     /**
+     * Registers the given callback to be invoked when the {@link SuggestedDeviceInfo} of the target
+     * router changes.
+     *
+     * <p>Calls using a previously registered callback will overwrite the previous executor.
+     *
+     * @hide
+     */
+    public void registerDeviceSuggestionsCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull DeviceSuggestionsCallback deviceSuggestionsCallback) {
+        Objects.requireNonNull(executor, "executor must not be null");
+        Objects.requireNonNull(deviceSuggestionsCallback, "callback must not be null");
+
+        DeviceSuggestionsCallbackRecord record =
+                new DeviceSuggestionsCallbackRecord(executor, deviceSuggestionsCallback);
+
+        mDeviceSuggestionsCallbackRecords.remove(record);
+        mDeviceSuggestionsCallbackRecords.add(record);
+    }
+
+    /**
      * Unregisters the given callback to not receive {@link RouteListingPreference} change events.
      *
      * @see #registerRouteListingPreferenceUpdatedCallback(Executor, Consumer)
@@ -779,6 +807,21 @@
     }
 
     /**
+     * Unregisters the given callback to not receive {@link SuggestedDeviceInfo} change events.
+     *
+     * @see #registerDeviceSuggestionsCallback(Executor, DeviceSuggestionsCallback)
+     * @hide
+     */
+    public void unregisterDeviceSuggestionsCallback(@NonNull DeviceSuggestionsCallback callback) {
+        Objects.requireNonNull(callback, "callback must not be null");
+
+        if (!mDeviceSuggestionsCallbackRecords.remove(
+                new DeviceSuggestionsCallbackRecord(/* executor */ null, callback))) {
+            Log.w(TAG, "unregisterDeviceSuggestionsCallback: Ignoring an unknown" + " callback");
+        }
+    }
+
+    /**
      * Shows the system output switcher dialog.
      *
      * <p>Should only be called when the context of MediaRouter2 is in the foreground and visible on
@@ -832,6 +875,36 @@
     }
 
     /**
+     * Sets the suggested devices.
+     *
+     * <p>Use this method to inform the system UI that this device is suggested in the Output
+     * Switcher and media controls.
+     *
+     * <p>You should pass null to this method to clear a previously set suggestion without setting a
+     * new one.
+     *
+     * @param suggestedDeviceInfo The {@link SuggestedDeviceInfo} the router suggests should be
+     *     provided to the user.
+     * @hide
+     */
+    @FlaggedApi(FLAG_ENABLE_SUGGESTED_DEVICE_API)
+    public void setDeviceSuggestions(@Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+        mImpl.setDeviceSuggestions(suggestedDeviceInfo);
+    }
+
+    /**
+     * Gets the current suggested devices.
+     *
+     * @return the suggested devices, keyed by the package name providing each suggestion list.
+     * @hide
+     */
+    @FlaggedApi(FLAG_ENABLE_SUGGESTED_DEVICE_API)
+    @Nullable
+    public Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestions() {
+        return mImpl.getDeviceSuggestions();
+    }
+
+    /**
      * Returns the current {@link RouteListingPreference} of the target router.
      *
      * <p>If this instance was created using {@code #getInstance(Context, String)}, then it returns
@@ -1518,6 +1591,17 @@
         }
     }
 
+    private void notifyDeviceSuggestionsUpdated(
+            @NonNull String suggestingPackageName,
+            @Nullable List<SuggestedDeviceInfo> deviceSuggestions) {
+        for (DeviceSuggestionsCallbackRecord record : mDeviceSuggestionsCallbackRecords) {
+            record.mExecutor.execute(
+                    () ->
+                            record.mDeviceSuggestionsCallback.onSuggestionUpdated(
+                                    suggestingPackageName, deviceSuggestions));
+        }
+    }
+
     private void notifyTransfer(RoutingController oldController, RoutingController newController) {
         for (TransferCallbackRecord record : mTransferCallbackRecords) {
             record.mExecutor.execute(
@@ -1568,6 +1652,25 @@
                 .build();
     }
 
+    /**
+     * Callback for receiving events about device suggestions
+     *
+     * @hide
+     */
+    public interface DeviceSuggestionsCallback {
+
+        /**
+         * Called when suggestions are updated. Whenever you register a callback, this will be
+         * invoked with the current suggestions.
+         *
+         * @param suggestingPackageName the package that provided the suggestions.
+         * @param suggestedDeviceInfo the suggestions provided by the package.
+         */
+        void onSuggestionUpdated(
+                @NonNull String suggestingPackageName,
+                @Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo);
+    }
+
     /** Callback for receiving events about media route discovery. */
     public abstract static class RouteCallback {
         /**
@@ -2326,6 +2429,35 @@
         }
     }
 
+    private static final class DeviceSuggestionsCallbackRecord {
+        public final Executor mExecutor;
+        public final DeviceSuggestionsCallback mDeviceSuggestionsCallback;
+
+        /* package */ DeviceSuggestionsCallbackRecord(
+                @NonNull Executor executor,
+                @NonNull DeviceSuggestionsCallback deviceSuggestionsCallback) {
+            mExecutor = executor;
+            mDeviceSuggestionsCallback = deviceSuggestionsCallback;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof DeviceSuggestionsCallbackRecord)) {
+                return false;
+            }
+            return mDeviceSuggestionsCallback
+                    == ((DeviceSuggestionsCallbackRecord) obj).mDeviceSuggestionsCallback;
+        }
+
+        @Override
+        public int hashCode() {
+            return mDeviceSuggestionsCallback.hashCode();
+        }
+    }
+
     static final class TransferCallbackRecord {
         public final Executor mExecutor;
         public final TransferCallback mTransferCallback;
@@ -2446,6 +2578,17 @@
         }
 
         @Override
+        public void notifyDeviceSuggestionsUpdated(
+                String suggestingPackageName, List<SuggestedDeviceInfo> suggestions) {
+            mHandler.sendMessage(
+                    obtainMessage(
+                            MediaRouter2::notifyDeviceSuggestionsUpdated,
+                            MediaRouter2.this,
+                            suggestingPackageName,
+                            suggestions));
+        }
+
+        @Override
         public void requestCreateSessionByManager(
                 long managerRequestId, RoutingSessionInfo oldSession, MediaRoute2Info route) {
             mHandler.sendMessage(
@@ -2487,6 +2630,11 @@
 
         void setRouteListingPreference(@Nullable RouteListingPreference preference);
 
+        void setDeviceSuggestions(@Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo);
+
+        @Nullable
+        Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestions();
+
         boolean showSystemOutputSwitcher();
 
         List<MediaRoute2Info> getAllRoutes();
@@ -2687,6 +2835,29 @@
         }
 
         @Override
+        public void setDeviceSuggestions(@Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+            synchronized (mLock) {
+                try {
+                    mMediaRouterService.setDeviceSuggestionsWithManager(
+                            mClient, suggestedDeviceInfo);
+                } catch (RemoteException ex) {
+                    ex.rethrowFromSystemServer();
+                }
+            }
+        }
+
+        @Override
+        public Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestions() {
+            synchronized (mLock) {
+                try {
+                    return mMediaRouterService.getDeviceSuggestionsWithManager(mClient);
+                } catch (RemoteException ex) {
+                    throw ex.rethrowFromSystemServer();
+                }
+            }
+        }
+
+        @Override
         public boolean showSystemOutputSwitcher() {
             try {
                 return mMediaRouterService.showMediaOutputSwitcherWithProxyRouter(mClient);
@@ -3296,6 +3467,23 @@
             notifyRouteListingPreferenceUpdated(routeListingPreference);
         }
 
+        private void onDeviceSuggestionsChangeHandler(
+                @NonNull String packageName,
+                @NonNull String suggestingPackageName,
+                @Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+            if (!TextUtils.equals(getClientPackageName(), packageName)) {
+                return;
+            }
+            synchronized (mLock) {
+                if (Objects.equals(
+                        mSuggestedDeviceInfo.get(suggestingPackageName), suggestedDeviceInfo)) {
+                    return;
+                }
+                mSuggestedDeviceInfo.put(suggestingPackageName, suggestedDeviceInfo);
+            }
+            notifyDeviceSuggestionsUpdated(suggestingPackageName, suggestedDeviceInfo);
+        }
+
         private void onRequestFailedOnHandler(int requestId, int reason) {
             MediaRouter2Manager.TransferRequest matchingRequest = null;
             for (MediaRouter2Manager.TransferRequest request : mTransferRequests) {
@@ -3390,6 +3578,20 @@
             }
 
             @Override
+            public void notifyDeviceSuggestionsUpdated(
+                    String packageName,
+                    String suggestingPackageName,
+                    @Nullable List<SuggestedDeviceInfo> deviceSuggestions) {
+                mHandler.sendMessage(
+                        obtainMessage(
+                                ProxyMediaRouter2Impl::onDeviceSuggestionsChangeHandler,
+                                ProxyMediaRouter2Impl.this,
+                                packageName,
+                                suggestingPackageName,
+                                deviceSuggestions));
+            }
+
+            @Override
             public void notifyRoutesUpdated(List<MediaRoute2Info> routes) {
                 mHandler.sendMessage(
                         obtainMessage(
@@ -3553,6 +3755,30 @@
         }
 
         @Override
+        public void setDeviceSuggestions(@Nullable List<SuggestedDeviceInfo> deviceSuggestions) {
+            synchronized (mLock) {
+                try {
+                    registerRouterStubIfNeededLocked();
+                    mMediaRouterService.setDeviceSuggestionsWithRouter2(mStub, deviceSuggestions);
+                } catch (RemoteException ex) {
+                    ex.rethrowFromSystemServer();
+                }
+            }
+        }
+
+        @Override
+        @Nullable
+        public Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestions() {
+            synchronized (mLock) {
+                try {
+                    return mMediaRouterService.getDeviceSuggestionsWithRouter2(mStub);
+                } catch (RemoteException ex) {
+                    throw ex.rethrowFromSystemServer();
+                }
+            }
+        }
+
+        @Override
         public boolean showSystemOutputSwitcher() {
             synchronized (mLock) {
                 try {
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 3f18eef..bf88709 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -1138,6 +1138,14 @@
         }
 
         @Override
+        public void notifyDeviceSuggestionsUpdated(
+                String packageName,
+                String suggestingPackageName,
+                @Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+            // MediaRouter2Manager doesn't support device suggestions
+        }
+
+        @Override
         public void notifyRoutesUpdated(List<MediaRoute2Info> routes) {
             mHandler.sendMessage(
                     obtainMessage(
diff --git a/media/java/android/media/quality/SoundProfile.aidl b/media/java/android/media/SuggestedDeviceInfo.aidl
similarity index 83%
rename from media/java/android/media/quality/SoundProfile.aidl
rename to media/java/android/media/SuggestedDeviceInfo.aidl
index e79fcaa..eab6425 100644
--- a/media/java/android/media/quality/SoundProfile.aidl
+++ b/media/java/android/media/SuggestedDeviceInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2025 The Android Open 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,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package android.media;
 
-parcelable SoundProfile;
+parcelable SuggestedDeviceInfo;
diff --git a/media/java/android/media/SuggestedDeviceInfo.java b/media/java/android/media/SuggestedDeviceInfo.java
new file mode 100644
index 0000000..2aa139f
--- /dev/null
+++ b/media/java/android/media/SuggestedDeviceInfo.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import static com.android.media.flags.Flags.FLAG_ENABLE_SUGGESTED_DEVICE_API;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Allows applications to suggest routes to the system UI (for example, in the System UI Output
+ * Switcher).
+ *
+ * @see MediaRouter2#setSuggestedDevice
+ * @hide
+ */
+@FlaggedApi(FLAG_ENABLE_SUGGESTED_DEVICE_API)
+public final class SuggestedDeviceInfo implements Parcelable {
+    @NonNull
+    public static final Creator<SuggestedDeviceInfo> CREATOR =
+            new Creator<>() {
+                @Override
+                public SuggestedDeviceInfo createFromParcel(Parcel in) {
+                    return new SuggestedDeviceInfo(in);
+                }
+
+                @Override
+                public SuggestedDeviceInfo[] newArray(int size) {
+                    return new SuggestedDeviceInfo[size];
+                }
+            };
+
+    @NonNull private final String mDeviceDisplayName;
+
+    @NonNull private final String mRouteId;
+
+    private final int mType;
+
+    @NonNull private final Bundle mExtras;
+
+    private SuggestedDeviceInfo(Builder builder) {
+        mDeviceDisplayName = builder.mDeviceDisplayName;
+        mRouteId = builder.mRouteId;
+        mType = builder.mType;
+        mExtras = builder.mExtras;
+    }
+
+    private SuggestedDeviceInfo(Parcel in) {
+        mDeviceDisplayName = in.readString();
+        mRouteId = in.readString();
+        mType = in.readInt();
+        mExtras = in.readBundle();
+    }
+
+    /**
+     * Returns the name to be displayed to the user.
+     *
+     * @return The device display name.
+     */
+    @NonNull
+    public String getDeviceDisplayName() {
+        return mDeviceDisplayName;
+    }
+
+    /**
+     * Returns the route ID associated with the suggestion.
+     *
+     * @return The route ID.
+     */
+    @NonNull
+    public String getRouteId() {
+        return mRouteId;
+    }
+
+    /**
+     * Returns the device type associated with the suggestion.
+     *
+     * @return The device type.
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the extras associated with the suggestion.
+     *
+     * @return The extras.
+     */
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    // SuggestedDeviceInfo Parcelable implementation.
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mDeviceDisplayName);
+        dest.writeString(mRouteId);
+        dest.writeInt(mType);
+        dest.writeBundle(mExtras);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SuggestedDeviceInfo)) {
+            return false;
+        }
+        return Objects.equals(mDeviceDisplayName, ((SuggestedDeviceInfo) obj).mDeviceDisplayName)
+                && Objects.equals(mRouteId, ((SuggestedDeviceInfo) obj).mRouteId)
+                && mType == ((SuggestedDeviceInfo) obj).mType;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDeviceDisplayName, mRouteId, mType);
+    }
+
+    @Override
+    public String toString() {
+        return mDeviceDisplayName + " | " + mRouteId + " | " + mType;
+    }
+
+    /** Builder for {@link SuggestedDeviceInfo}. */
+    public static final class Builder {
+        @NonNull private String mDeviceDisplayName;
+
+        @NonNull private String mRouteId;
+
+        @NonNull private Integer mType;
+
+        private Bundle mExtras = Bundle.EMPTY;
+
+        /**
+         * Creates a new SuggestedDeviceInfo. The device display name, route ID, and type must be
+         * set. The extras cannot be null, but default to an empty {@link Bundle}.
+         *
+         * @throws IllegalArgumentException if the builder has a mandatory unset field.
+         */
+        public SuggestedDeviceInfo build() {
+            if (mDeviceDisplayName == null) {
+                throw new IllegalArgumentException("Device display name cannot be null");
+            }
+
+            if (mRouteId == null) {
+                throw new IllegalArgumentException("Route ID cannot be null.");
+            }
+
+            if (mType == null) {
+                throw new IllegalArgumentException("Device type cannot be null.");
+            }
+
+            if (mExtras == null) {
+                throw new IllegalArgumentException("Extras cannot be null.");
+            }
+
+            return new SuggestedDeviceInfo(this);
+        }
+
+        /**
+         * Sets the {@link #getDeviceDisplayName() device display name}.
+         *
+         * @throws IllegalArgumentException if the name is null or empty.
+         */
+        public Builder setDeviceDisplayName(@NonNull String deviceDisplayName) {
+            if (TextUtils.isEmpty(deviceDisplayName)) {
+                throw new IllegalArgumentException("Device display name cannot be null");
+            }
+            mDeviceDisplayName = deviceDisplayName;
+            return this;
+        }
+
+        /**
+         * Sets the {@link #getRouteId() route id}.
+         *
+         * @throws IllegalArgumentException if the route id is null or empty.
+         */
+        public Builder setRouteId(@NonNull String routeId) {
+            if (TextUtils.isEmpty(routeId)) {
+                throw new IllegalArgumentException("Device display name cannot be null");
+            }
+            mRouteId = routeId;
+            return this;
+        }
+
+        /** Sets the {@link #getType() device type}. */
+        public Builder setType(int type) {
+            mType = type;
+            return this;
+        }
+
+        /**
+         * Sets the {@link #getExtras() extras}.
+         *
+         * <p>The default value is an empty {@link Bundle}.
+         *
+         * <p>Do not mutate the given {@link Bundle} after passing it to this method. You can use
+         * {@link Bundle#deepCopy()} to keep a mutable copy.
+         *
+         * @throws NullPointerException if the extras are null.
+         */
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = Objects.requireNonNull(extras, "extras must not be null");
+            return this;
+        }
+    }
+}
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 0deed39..c9ec31b 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -62,6 +62,14 @@
 }
 
 flag {
+    name: "enable_suggested_device_api"
+    is_exported: true
+    namespace: "media_better_together"
+    description: "Enables the API allowing proxy routers to suggest routes."
+    bug: "393216553"
+}
+
+flag {
     name: "enable_full_scan_with_media_content_control"
     namespace: "media_better_together"
     description: "Allows holders of the MEDIA_CONTENT_CONTROL permission to scan for routes while not in the foreground."
@@ -155,6 +163,13 @@
 }
 
 flag {
+  name: "enable_output_switcher_redesign"
+  namespace: "media_better_together"
+  description: "Enables visual update for the Media Output Switcher"
+  bug: "388296370"
+}
+
+flag {
     name: "enable_prevention_of_keep_alive_route_providers"
     namespace: "media_solutions"
     description: "Enables mechanisms to prevent route providers from keeping malicious apps alive."
@@ -234,3 +249,13 @@
     description: "Fallbacks to the default handling for volume adjustment when media session has fixed volume handling and its app is in the foreground and setting a media controller."
     bug: "293743975"
 }
+
+flag {
+    name: "fix_output_media_item_list_index_out_of_bounds_exception"
+    namespace: "media_better_together"
+    description: "Fixes a bug of causing IndexOutOfBoundsException when building media item list."
+    bug: "398246089"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/media/java/android/media/quality/AmbientBacklightSettings.aidl b/media/java/android/media/quality/AmbientBacklightSettings.aidl
deleted file mode 100644
index e2cdd03..0000000
--- a/media/java/android/media/quality/AmbientBacklightSettings.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.quality;
-
-parcelable AmbientBacklightSettings;
diff --git a/media/java/android/media/quality/Android.bp b/media/java/android/media/quality/Android.bp
new file mode 100644
index 0000000..f620144
--- /dev/null
+++ b/media/java/android/media/quality/Android.bp
@@ -0,0 +1,64 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "framework-media-quality-sources-aidl",
+    srcs: [
+        "aidl/android/media/quality/*.aidl",
+    ],
+    path: "aidl",
+}
+
+cc_library_headers {
+    name: "media_quality_headers",
+    export_include_dirs: ["include"],
+}
+
+cc_library_shared {
+    name: "libmedia_quality_include",
+
+    export_include_dirs: ["include"],
+    cflags: [
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+
+    srcs: [
+        ":framework-media-quality-sources-aidl",
+    ],
+}
+
+aidl_interface {
+    name: "media_quality_aidl_interface",
+    unstable: true,
+    local_include_dir: "aidl",
+    backend: {
+        java: {
+            enabled: true,
+        },
+        cpp: {
+            additional_shared_libraries: ["libmedia_quality_include"],
+            enabled: true,
+        },
+        ndk: {
+            enabled: false,
+        },
+        rust: {
+            enabled: false,
+        },
+    },
+    srcs: [
+        ":framework-media-quality-sources-aidl",
+    ],
+}
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
deleted file mode 100644
index 6e9fa1d..0000000
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.quality;
-
-import android.media.quality.AmbientBacklightSettings;
-import android.media.quality.IAmbientBacklightCallback;
-import android.media.quality.IPictureProfileCallback;
-import android.media.quality.ISoundProfileCallback;
-import android.media.quality.ParameterCapability;
-import android.media.quality.PictureProfileHandle;
-import android.media.quality.PictureProfile;
-import android.media.quality.SoundProfileHandle;
-import android.media.quality.SoundProfile;
-import android.os.Bundle;
-import android.os.UserHandle;
-
-/**
- * Interface for Media Quality Manager
- * @hide
- */
-interface IMediaQualityManager {
-    PictureProfile createPictureProfile(in PictureProfile pp, in UserHandle user);
-    void updatePictureProfile(in String id, in PictureProfile pp, in UserHandle user);
-    void removePictureProfile(in String id, in UserHandle user);
-    boolean setDefaultPictureProfile(in String id, in UserHandle user);
-    PictureProfile getPictureProfile(
-            in int type, in String name, in Bundle options, in UserHandle user);
-    List<PictureProfile> getPictureProfilesByPackage(
-            in String packageName, in Bundle options, in UserHandle user);
-    List<PictureProfile> getAvailablePictureProfiles(in Bundle options, in UserHandle user);
-    List<String> getPictureProfilePackageNames(in UserHandle user);
-    List<String> getPictureProfileAllowList(in UserHandle user);
-    void setPictureProfileAllowList(in List<String> packages, in UserHandle user);
-    List<PictureProfileHandle> getPictureProfileHandle(in String[] id, in UserHandle user);
-
-    SoundProfile createSoundProfile(in SoundProfile pp, in UserHandle user);
-    void updateSoundProfile(in String id, in SoundProfile pp, in UserHandle user);
-    void removeSoundProfile(in String id, in UserHandle user);
-    boolean setDefaultSoundProfile(in String id, in UserHandle user);
-    SoundProfile getSoundProfile(
-            in int type, in String name, in Bundle options, in UserHandle user);
-    List<SoundProfile> getSoundProfilesByPackage(
-            in String packageName, in Bundle options, in UserHandle user);
-    List<SoundProfile> getAvailableSoundProfiles(in Bundle options, in UserHandle user);
-    List<String> getSoundProfilePackageNames(in UserHandle user);
-    List<String> getSoundProfileAllowList(in UserHandle user);
-    void setSoundProfileAllowList(in List<String> packages, in UserHandle user);
-    List<SoundProfileHandle> getSoundProfileHandle(in String[] id, in UserHandle user);
-
-    void registerPictureProfileCallback(in IPictureProfileCallback cb);
-    void registerSoundProfileCallback(in ISoundProfileCallback cb);
-    void registerAmbientBacklightCallback(in IAmbientBacklightCallback cb);
-
-    List<ParameterCapability> getParameterCapabilities(in List<String> names, in UserHandle user);
-
-    boolean isSupported(in UserHandle user);
-    void setAutoPictureQualityEnabled(in boolean enabled, in UserHandle user);
-    boolean isAutoPictureQualityEnabled(in UserHandle user);
-    void setSuperResolutionEnabled(in boolean enabled, in UserHandle user);
-    boolean isSuperResolutionEnabled(in UserHandle user);
-    void setAutoSoundQualityEnabled(in boolean enabled, in UserHandle user);
-    boolean isAutoSoundQualityEnabled(in UserHandle user);
-
-    void setAmbientBacklightSettings(in AmbientBacklightSettings settings, in UserHandle user);
-    void setAmbientBacklightEnabled(in boolean enabled, in UserHandle user);
-    boolean isAmbientBacklightEnabled(in UserHandle user);
-}
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index e4de3e4..fccdba8 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -82,7 +82,7 @@
         String PARAMETER_NAME = "_name";
         String PARAMETER_PACKAGE = "_package";
         String PARAMETER_INPUT_ID = "_input_id";
-
+        String VENDOR_PARAMETERS = "_vendor_parameters";
     }
 
     /**
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 0d6d32a..bfd0138 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -274,9 +274,9 @@
             @NonNull String name,
             @Nullable ProfileQueryParams options) {
         try {
-            Bundle optionsBundle = options == null
-                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
-            return mService.getPictureProfile(type, name, optionsBundle, mUserHandle);
+            boolean includeParams = options == null || options.mParametersIncluded;
+            return mService.getPictureProfile(
+                    type, name, includeParams, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -299,10 +299,9 @@
     public List<PictureProfile> getPictureProfilesByPackage(
             @NonNull String packageName, @Nullable ProfileQueryParams options) {
         try {
-            Bundle optionsBundle = options == null
-                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
+            boolean includeParams = options == null || options.mParametersIncluded;
             return mService.getPictureProfilesByPackage(
-                    packageName, optionsBundle, mUserHandle);
+                    packageName, includeParams, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -321,9 +320,8 @@
     @NonNull
     public List<PictureProfile> getAvailablePictureProfiles(@Nullable ProfileQueryParams options) {
         try {
-            Bundle optionsBundle = options == null
-                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
-            return mService.getAvailablePictureProfiles(optionsBundle, mUserHandle);
+            boolean includeParams = options == null || options.mParametersIncluded;
+            return mService.getAvailablePictureProfiles(includeParams, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -344,7 +342,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
     public boolean setDefaultPictureProfile(@Nullable String pictureProfileId) {
         try {
-            return mService.setDefaultPictureProfile(pictureProfileId, mUserHandle);
+            return mService.setDefaultPictureProfile(pictureProfileId, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -361,7 +359,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
     public List<String> getPictureProfilePackageNames() {
         try {
-            return mService.getPictureProfilePackageNames(mUserHandle);
+            return mService.getPictureProfilePackageNames(mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -373,7 +371,7 @@
      */
     public List<PictureProfileHandle> getPictureProfileHandle(String[] id) {
         try {
-            return mService.getPictureProfileHandle(id, mUserHandle);
+            return mService.getPictureProfileHandle(id, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -385,7 +383,7 @@
      */
     public List<SoundProfileHandle> getSoundProfileHandle(String[] id) {
         try {
-            return mService.getSoundProfileHandle(id, mUserHandle);
+            return mService.getSoundProfileHandle(id, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -401,7 +399,7 @@
      */
     public void createPictureProfile(@NonNull PictureProfile pp) {
         try {
-            mService.createPictureProfile(pp, mUserHandle);
+            mService.createPictureProfile(pp, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -416,7 +414,7 @@
      */
     public void updatePictureProfile(@NonNull String profileId, @NonNull PictureProfile pp) {
         try {
-            mService.updatePictureProfile(profileId, pp, mUserHandle);
+            mService.updatePictureProfile(profileId, pp, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -430,7 +428,7 @@
      */
     public void removePictureProfile(@NonNull String profileId) {
         try {
-            mService.removePictureProfile(profileId, mUserHandle);
+            mService.removePictureProfile(profileId, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -484,9 +482,8 @@
             @NonNull String name,
             @Nullable ProfileQueryParams options) {
         try {
-            Bundle optionsBundle = options == null
-                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
-            return mService.getSoundProfile(type, name, optionsBundle, mUserHandle);
+            boolean includeParams = options == null || options.mParametersIncluded;
+            return mService.getSoundProfile(type, name, includeParams, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -510,9 +507,9 @@
     public List<SoundProfile> getSoundProfilesByPackage(
             @NonNull String packageName, @Nullable ProfileQueryParams options) {
         try {
-            Bundle optionsBundle = options == null
-                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
-            return mService.getSoundProfilesByPackage(packageName, optionsBundle, mUserHandle);
+            boolean includeParams = options == null || options.mParametersIncluded;
+            return mService.getSoundProfilesByPackage(
+                    packageName, includeParams, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -531,9 +528,8 @@
     @NonNull
     public List<SoundProfile> getAvailableSoundProfiles(@Nullable ProfileQueryParams options) {
         try {
-            Bundle optionsBundle = options == null
-                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
-            return mService.getAvailableSoundProfiles(optionsBundle, mUserHandle);
+            boolean includeParams = options == null || options.mParametersIncluded;
+            return mService.getAvailableSoundProfiles(includeParams, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -554,7 +550,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
     public boolean setDefaultSoundProfile(@Nullable String soundProfileId) {
         try {
-            return mService.setDefaultSoundProfile(soundProfileId, mUserHandle);
+            return mService.setDefaultSoundProfile(soundProfileId, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -572,7 +568,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
     public List<String> getSoundProfilePackageNames() {
         try {
-            return mService.getSoundProfilePackageNames(mUserHandle);
+            return mService.getSoundProfilePackageNames(mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -589,7 +585,7 @@
      */
     public void createSoundProfile(@NonNull SoundProfile sp) {
         try {
-            mService.createSoundProfile(sp, mUserHandle);
+            mService.createSoundProfile(sp, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -604,7 +600,7 @@
      */
     public void updateSoundProfile(@NonNull String profileId, @NonNull SoundProfile sp) {
         try {
-            mService.updateSoundProfile(profileId, sp, mUserHandle);
+            mService.updateSoundProfile(profileId, sp, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -618,7 +614,7 @@
      */
     public void removeSoundProfile(@NonNull String profileId) {
         try {
-            mService.removeSoundProfile(profileId, mUserHandle);
+            mService.removeSoundProfile(profileId, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -636,7 +632,7 @@
     @NonNull
     public List<ParameterCapability> getParameterCapabilities(@NonNull List<String> names) {
         try {
-            return mService.getParameterCapabilities(names, mUserHandle);
+            return mService.getParameterCapabilities(names, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -654,7 +650,7 @@
     @NonNull
     public List<String> getPictureProfileAllowList() {
         try {
-            return mService.getPictureProfileAllowList(mUserHandle);
+            return mService.getPictureProfileAllowList(mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -668,7 +664,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
     public void setPictureProfileAllowList(@NonNull List<String> packageNames) {
         try {
-            mService.setPictureProfileAllowList(packageNames, mUserHandle);
+            mService.setPictureProfileAllowList(packageNames, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -686,7 +682,7 @@
     @NonNull
     public List<String> getSoundProfileAllowList() {
         try {
-            return mService.getSoundProfileAllowList(mUserHandle);
+            return mService.getSoundProfileAllowList(mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -700,7 +696,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
     public void setSoundProfileAllowList(@NonNull List<String> packageNames) {
         try {
-            mService.setSoundProfileAllowList(packageNames, mUserHandle);
+            mService.setSoundProfileAllowList(packageNames, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -712,7 +708,7 @@
      */
     public boolean isSupported() {
         try {
-            return mService.isSupported(mUserHandle);
+            return mService.isSupported(mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -730,7 +726,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
     public void setAutoPictureQualityEnabled(boolean enabled) {
         try {
-            mService.setAutoPictureQualityEnabled(enabled, mUserHandle);
+            mService.setAutoPictureQualityEnabled(enabled, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -741,7 +737,7 @@
      */
     public boolean isAutoPictureQualityEnabled() {
         try {
-            return mService.isAutoPictureQualityEnabled(mUserHandle);
+            return mService.isAutoPictureQualityEnabled(mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -758,7 +754,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
     public void setSuperResolutionEnabled(boolean enabled) {
         try {
-            mService.setSuperResolutionEnabled(enabled, mUserHandle);
+            mService.setSuperResolutionEnabled(enabled, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -769,7 +765,7 @@
      */
     public boolean isSuperResolutionEnabled() {
         try {
-            return mService.isSuperResolutionEnabled(mUserHandle);
+            return mService.isSuperResolutionEnabled(mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -787,7 +783,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
     public void setAutoSoundQualityEnabled(boolean enabled) {
         try {
-            mService.setAutoSoundQualityEnabled(enabled, mUserHandle);
+            mService.setAutoSoundQualityEnabled(enabled, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -798,7 +794,7 @@
      */
     public boolean isAutoSoundQualityEnabled() {
         try {
-            return mService.isAutoSoundQualityEnabled(mUserHandle);
+            return mService.isAutoSoundQualityEnabled(mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -847,7 +843,7 @@
             @NonNull AmbientBacklightSettings settings) {
         Preconditions.checkNotNull(settings);
         try {
-            mService.setAmbientBacklightSettings(settings, mUserHandle);
+            mService.setAmbientBacklightSettings(settings, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -858,7 +854,7 @@
      */
     public boolean isAmbientBacklightEnabled() {
         try {
-            return mService.isAmbientBacklightEnabled(mUserHandle);
+            return mService.isAmbientBacklightEnabled(mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -872,7 +868,7 @@
     @RequiresPermission(android.Manifest.permission.READ_COLOR_ZONES)
     public void setAmbientBacklightEnabled(boolean enabled) {
         try {
-            mService.setAmbientBacklightEnabled(enabled, mUserHandle);
+            mService.setAmbientBacklightEnabled(enabled, mUserHandle.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/quality/ParameterCapability.aidl b/media/java/android/media/quality/ParameterCapability.aidl
deleted file mode 100644
index eb2ac97..0000000
--- a/media/java/android/media/quality/ParameterCapability.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.quality;
-
-parcelable ParameterCapability;
diff --git a/media/java/android/media/quality/SoundProfileHandle.aidl b/media/java/android/media/quality/SoundProfileHandle.aidl
deleted file mode 100644
index 6b8161c..0000000
--- a/media/java/android/media/quality/SoundProfileHandle.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.quality;
-
-parcelable SoundProfileHandle;
diff --git a/media/java/android/media/quality/SoundProfileHandle.java b/media/java/android/media/quality/SoundProfileHandle.java
deleted file mode 100644
index edb546e..0000000
--- a/media/java/android/media/quality/SoundProfileHandle.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.quality;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
-  * A type-safe handle to a sound profile.
-  *
-  * @hide
-  */
-public final class SoundProfileHandle implements Parcelable {
-    public static final @NonNull SoundProfileHandle NONE = new SoundProfileHandle(-1000);
-
-    private final long mId;
-
-    /** @hide */
-    public SoundProfileHandle(long id) {
-        mId = id;
-    }
-
-    /** @hide */
-    public long getId() {
-        return mId;
-    }
-
-    /** @hide */
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeLong(mId);
-    }
-
-    /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** @hide */
-    public static final @NonNull Creator<SoundProfileHandle> CREATOR =
-            new Creator<SoundProfileHandle>() {
-                @Override
-                public SoundProfileHandle createFromParcel(Parcel in) {
-                    return new SoundProfileHandle(in);
-                }
-
-                @Override
-                public SoundProfileHandle[] newArray(int size) {
-                    return new SoundProfileHandle[size];
-                }
-            };
-
-    private SoundProfileHandle(@NonNull Parcel in) {
-        mId = in.readLong();
-    }
-}
diff --git a/media/java/android/media/quality/ActiveProcessingPicture.aidl b/media/java/android/media/quality/aidl/android/media/quality/ActiveProcessingPicture.aidl
similarity index 89%
rename from media/java/android/media/quality/ActiveProcessingPicture.aidl
rename to media/java/android/media/quality/aidl/android/media/quality/ActiveProcessingPicture.aidl
index 2851306..d2cf140 100644
--- a/media/java/android/media/quality/ActiveProcessingPicture.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/ActiveProcessingPicture.aidl
@@ -16,4 +16,4 @@
 
 package android.media.quality;
 
-parcelable ActiveProcessingPicture;
\ No newline at end of file
+parcelable ActiveProcessingPicture cpp_header "quality/MediaQualityManager.h";
\ No newline at end of file
diff --git a/media/java/android/media/quality/AmbientBacklightEvent.aidl b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightEvent.aidl
similarity index 89%
rename from media/java/android/media/quality/AmbientBacklightEvent.aidl
rename to media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightEvent.aidl
index 174cd46..d53860f 100644
--- a/media/java/android/media/quality/AmbientBacklightEvent.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightEvent.aidl
@@ -16,4 +16,4 @@
 
 package android.media.quality;
 
-parcelable AmbientBacklightEvent;
+parcelable AmbientBacklightEvent cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/AmbientBacklightMetadata.aidl b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightMetadata.aidl
similarity index 89%
rename from media/java/android/media/quality/AmbientBacklightMetadata.aidl
rename to media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightMetadata.aidl
index b95a474f..a935b49 100644
--- a/media/java/android/media/quality/AmbientBacklightMetadata.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightMetadata.aidl
@@ -16,4 +16,4 @@
 
 package android.media.quality;
 
-parcelable AmbientBacklightMetadata;
\ No newline at end of file
+parcelable AmbientBacklightMetadata cpp_header "quality/MediaQualityManager.h";
\ No newline at end of file
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightSettings.aidl
similarity index 88%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightSettings.aidl
index 5d14631..051aef8 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/AmbientBacklightSettings.aidl
@@ -16,4 +16,4 @@
 
 package android.media.quality;
 
-parcelable PictureProfileHandle;
+parcelable AmbientBacklightSettings cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/IAmbientBacklightCallback.aidl b/media/java/android/media/quality/aidl/android/media/quality/IAmbientBacklightCallback.aidl
similarity index 100%
rename from media/java/android/media/quality/IAmbientBacklightCallback.aidl
rename to media/java/android/media/quality/aidl/android/media/quality/IAmbientBacklightCallback.aidl
diff --git a/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl
new file mode 100644
index 0000000..0191ea7
--- /dev/null
+++ b/media/java/android/media/quality/aidl/android/media/quality/IMediaQualityManager.aidl
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+import android.media.quality.AmbientBacklightSettings;
+import android.media.quality.IAmbientBacklightCallback;
+import android.media.quality.IPictureProfileCallback;
+import android.media.quality.ISoundProfileCallback;
+import android.media.quality.ParameterCapability;
+import android.media.quality.PictureProfileHandle;
+import android.media.quality.PictureProfile;
+import android.media.quality.SoundProfileHandle;
+import android.media.quality.SoundProfile;
+
+/**
+ * Interface for Media Quality Manager
+ * @hide
+ */
+interface IMediaQualityManager {
+    // TODO: use UserHandle
+    PictureProfile createPictureProfile(in PictureProfile pp, int userId);
+    void updatePictureProfile(in String id, in PictureProfile pp, int userId);
+    void removePictureProfile(in String id, int userId);
+    boolean setDefaultPictureProfile(in String id, int userId);
+    // TODO: use Bundle for includeParams
+    PictureProfile getPictureProfile(
+            in int type, in String name, in boolean includeParams, int userId);
+    List<PictureProfile> getPictureProfilesByPackage(
+            in String packageName, in boolean includeParams, int userId);
+    List<PictureProfile> getAvailablePictureProfiles(in boolean includeParams, int userId);
+    List<String> getPictureProfilePackageNames(int userId);
+    List<String> getPictureProfileAllowList(int userId);
+    void setPictureProfileAllowList(in List<String> packages, int userId);
+    List<PictureProfileHandle> getPictureProfileHandle(in String[] id, int userId);
+
+    SoundProfile createSoundProfile(in SoundProfile pp, int userId);
+    void updateSoundProfile(in String id, in SoundProfile pp, int userId);
+    void removeSoundProfile(in String id, int userId);
+    boolean setDefaultSoundProfile(in String id, int userId);
+    SoundProfile getSoundProfile(
+            in int type, in String name, in boolean includeParams, int userId);
+    List<SoundProfile> getSoundProfilesByPackage(
+            in String packageName, in boolean includeParams, int userId);
+    List<SoundProfile> getAvailableSoundProfiles(in boolean includeParams, int userId);
+    List<String> getSoundProfilePackageNames(int userId);
+    List<String> getSoundProfileAllowList(int userId);
+    void setSoundProfileAllowList(in List<String> packages, int userId);
+    List<SoundProfileHandle> getSoundProfileHandle(in String[] id, int userId);
+
+    void registerPictureProfileCallback(in IPictureProfileCallback cb);
+    void registerSoundProfileCallback(in ISoundProfileCallback cb);
+    void registerAmbientBacklightCallback(in IAmbientBacklightCallback cb);
+
+    List<ParameterCapability> getParameterCapabilities(in List<String> names, int userId);
+
+    boolean isSupported(int userId);
+    void setAutoPictureQualityEnabled(in boolean enabled, int userId);
+    boolean isAutoPictureQualityEnabled(int userId);
+    void setSuperResolutionEnabled(in boolean enabled, int userId);
+    boolean isSuperResolutionEnabled(int userId);
+    void setAutoSoundQualityEnabled(in boolean enabled, int userId);
+    boolean isAutoSoundQualityEnabled(int userId);
+
+    void setAmbientBacklightSettings(in AmbientBacklightSettings settings, int userId);
+    void setAmbientBacklightEnabled(in boolean enabled, int userId);
+    boolean isAmbientBacklightEnabled(int userId);
+}
diff --git a/media/java/android/media/quality/IPictureProfileCallback.aidl b/media/java/android/media/quality/aidl/android/media/quality/IPictureProfileCallback.aidl
similarity index 100%
rename from media/java/android/media/quality/IPictureProfileCallback.aidl
rename to media/java/android/media/quality/aidl/android/media/quality/IPictureProfileCallback.aidl
diff --git a/media/java/android/media/quality/ISoundProfileCallback.aidl b/media/java/android/media/quality/aidl/android/media/quality/ISoundProfileCallback.aidl
similarity index 100%
rename from media/java/android/media/quality/ISoundProfileCallback.aidl
rename to media/java/android/media/quality/aidl/android/media/quality/ISoundProfileCallback.aidl
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/media/java/android/media/quality/aidl/android/media/quality/ParameterCapability.aidl
similarity index 89%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to media/java/android/media/quality/aidl/android/media/quality/ParameterCapability.aidl
index 5d14631..ea84857 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/ParameterCapability.aidl
@@ -16,4 +16,4 @@
 
 package android.media.quality;
 
-parcelable PictureProfileHandle;
+parcelable ParameterCapability cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/PictureProfile.aidl b/media/java/android/media/quality/aidl/android/media/quality/PictureProfile.aidl
similarity index 90%
rename from media/java/android/media/quality/PictureProfile.aidl
rename to media/java/android/media/quality/aidl/android/media/quality/PictureProfile.aidl
index 41d018b..b0fe3f5 100644
--- a/media/java/android/media/quality/PictureProfile.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/PictureProfile.aidl
@@ -16,4 +16,4 @@
 
 package android.media.quality;
 
-parcelable PictureProfile;
+parcelable PictureProfile cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/media/java/android/media/quality/aidl/android/media/quality/PictureProfileHandle.aidl
similarity index 89%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to media/java/android/media/quality/aidl/android/media/quality/PictureProfileHandle.aidl
index 5d14631..0582938 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/PictureProfileHandle.aidl
@@ -16,4 +16,4 @@
 
 package android.media.quality;
 
-parcelable PictureProfileHandle;
+parcelable PictureProfileHandle cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/media/java/android/media/quality/aidl/android/media/quality/SoundProfile.aidl
similarity index 90%
rename from media/java/android/media/quality/PictureProfileHandle.aidl
rename to media/java/android/media/quality/aidl/android/media/quality/SoundProfile.aidl
index 5d14631..d93231f 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/SoundProfile.aidl
@@ -16,4 +16,4 @@
 
 package android.media.quality;
 
-parcelable PictureProfileHandle;
+parcelable SoundProfile cpp_header "quality/MediaQualityManager.h";
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/media/java/android/media/quality/aidl/android/media/quality/SoundProfileHandle.aidl
similarity index 88%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to media/java/android/media/quality/aidl/android/media/quality/SoundProfileHandle.aidl
index 5d14631..ea26b19 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/media/java/android/media/quality/aidl/android/media/quality/SoundProfileHandle.aidl
@@ -16,4 +16,7 @@
 
 package android.media.quality;
 
-parcelable PictureProfileHandle;
+// TODO: add SoundProfileHandle.java
+parcelable SoundProfileHandle {
+    long id;
+}
diff --git a/media/java/android/media/quality/include/quality/MediaQualityManager.h b/media/java/android/media/quality/include/quality/MediaQualityManager.h
new file mode 100644
index 0000000..8c31667
--- /dev/null
+++ b/media/java/android/media/quality/include/quality/MediaQualityManager.h
@@ -0,0 +1,127 @@
+#ifndef ANDROID_MEDIA_QUALITY_MANAGER_H
+#define ANDROID_MEDIA_QUALITY_MANAGER_H
+
+
+namespace android {
+namespace media {
+namespace quality {
+
+// TODO: implement writeToParcel and readFromParcel
+
+class PictureProfileHandle : public Parcelable {
+    public:
+        PictureProfileHandle() {}
+        status_t writeToParcel(android::Parcel*) const override {
+            return 0;
+        }
+        status_t readFromParcel(const android::Parcel*) override {
+            return 0;
+        }
+        std::string toString() const {
+            return "";
+        }
+};
+
+class SoundProfile : public Parcelable {
+    public:
+        SoundProfile() {}
+        status_t writeToParcel(android::Parcel*) const override {
+            return 0;
+        }
+        status_t readFromParcel(const android::Parcel*) override {
+            return 0;
+        }
+        std::string toString() const {
+            return "";
+        }
+};
+
+class PictureProfile : public Parcelable {
+    public:
+        PictureProfile() {}
+        status_t writeToParcel(android::Parcel*) const override {
+            return 0;
+        }
+        status_t readFromParcel(const android::Parcel*) override {
+            return 0;
+        }
+        std::string toString() const {
+            return "";
+        }
+};
+
+class ActiveProcessingPicture : public Parcelable {
+    public:
+        ActiveProcessingPicture() {}
+        status_t writeToParcel(android::Parcel*) const override {
+            return 0;
+        }
+        status_t readFromParcel(const android::Parcel*) override {
+            return 0;
+        }
+        std::string toString() const {
+            return "";
+        }
+};
+
+class AmbientBacklightEvent : public Parcelable {
+    public:
+        AmbientBacklightEvent() {}
+        status_t writeToParcel(android::Parcel*) const override {
+            return 0;
+        }
+        status_t readFromParcel(const android::Parcel*) override {
+            return 0;
+        }
+        std::string toString() const {
+            return "";
+        }
+};
+
+class AmbientBacklightMetadata : public Parcelable {
+    public:
+        AmbientBacklightMetadata() {}
+        status_t writeToParcel(android::Parcel*) const override {
+            return 0;
+        }
+        status_t readFromParcel(const android::Parcel*) override {
+            return 0;
+        }
+        std::string toString() const {
+            return "";
+        }
+};
+
+class AmbientBacklightSettings : public Parcelable {
+    public:
+        AmbientBacklightSettings() {}
+        status_t writeToParcel(android::Parcel*) const override {
+            return 0;
+        }
+        status_t readFromParcel(const android::Parcel*) override {
+            return 0;
+        }
+        std::string toString() const {
+            return "";
+        }
+};
+
+class ParameterCapability : public Parcelable {
+    public:
+        ParameterCapability() {}
+        status_t writeToParcel(android::Parcel*) const override {
+            return 0;
+        }
+        status_t readFromParcel(const android::Parcel*) override {
+            return 0;
+        }
+        std::string toString() const {
+            return "";
+        }
+};
+
+} // namespace quality
+} // namespace media
+} // namespace android
+
+#endif
diff --git a/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl b/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl
index cdf6e23..40848fe 100644
--- a/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl
+++ b/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl
@@ -21,5 +21,5 @@
  */
 interface IHDPlusInfo {
     // Specifying a HDPlusInfo and start a network scan.
-    int setHDPlusInfo(String isBlindScanContinue, String isHDMode);
+    int setHDPlusInfo(boolean isBlindScanContinue, boolean isHDMode);
 }
diff --git a/media/java/android/media/tv/extension/scan/IScanListener.aidl b/media/java/android/media/tv/extension/scan/IScanListener.aidl
index 2c4807f..79810a7 100644
--- a/media/java/android/media/tv/extension/scan/IScanListener.aidl
+++ b/media/java/android/media/tv/extension/scan/IScanListener.aidl
@@ -27,7 +27,7 @@
     // notify the scan progress.
     void onScanProgress(String scanProgress, in Bundle scanProgressInfo);
     // notify the scan completion.
-    void onScanCompleted(int scanResult);
+    void onScanCompleted(int scanResult, in Bundle optionScanInfo);
     // notify that the temporaily held channel list is stored.
     void onStoreCompleted(int storeResult);
 }
diff --git a/media/java/android/media/tv/extension/scan/IScanSession.aidl b/media/java/android/media/tv/extension/scan/IScanSession.aidl
index d42eca1..f53b096 100644
--- a/media/java/android/media/tv/extension/scan/IScanSession.aidl
+++ b/media/java/android/media/tv/extension/scan/IScanSession.aidl
@@ -24,7 +24,7 @@
 interface IScanSession {
     // Start a service scan.
     int startScan(int broadcastType, String countryCode, String operator, in int[] frequency,
-        String scanType, String languageCode);
+        String scanType, String languageCode, in Bundle optionalScanParams);
     // Reset the scan information held in TIS.
     int resetScan();
     // Cancel scan.
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl
index 1b1577f..0e9ac5f 100644
--- a/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl
@@ -78,4 +78,11 @@
     int addPredefinedChannelList(String serviceListId, in Bundle[] predefinedListBundle);
     // Add predefined satellite info of Hotbird 13E in scan two satellite scene EU region.
     int addPredefinedSatInfo(String serviceListId, in Bundle predefinedSatInfoBundle);
+
+    // Get the logo URI for a specific service - DVB-I only.
+    String getServiceLogoUri(int serviceRecordId);
+    // Get the installed service list information for a specific channel list id - DVB-I only.
+    Bundle getInstalledServiceListInfo(String channelListId);
+    // Get all installed service list information - DVB-I only.
+    Bundle[] getAllInstalledServiceListInfo();
 }
diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl
index abd8320..fced45b 100644
--- a/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl
+++ b/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl
@@ -16,10 +16,12 @@
 
 package android.media.tv.extension.servicedb;
 
+import android.os.Bundle;
+
 /**
  * @hide
  */
 interface IServiceListImportListener {
     void onImported(int importResult);
-    void onPreloaded(int preloadResult);
+    void onPreloaded(int preloadResult, in Bundle serviceListInfo);
 }
\ No newline at end of file
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
index 6089f42..f65c7ef 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioManagerUnitTest.java
@@ -28,7 +28,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.companion.virtual.VirtualDeviceManager;
@@ -56,7 +56,7 @@
         audioManager.playSoundEffect(FX_KEY_CLICK);
 
         // We expect no interactions with VDM when running on default device.
-        verifyZeroInteractions(mockVdm);
+        verifyNoMoreInteractions(mockVdm);
     }
 
     @Test
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index 65264d3..006b86a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -16,9 +16,9 @@
 
 package com.android.mediaframeworktest.unit;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 1e6a7b7..45b746d 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -971,7 +971,7 @@
                 .timeStampNanos = (i == count - 1) ? now : message.timeStampNanos,
                 .data = HalChannelMessageContents::make<HalChannelMessageContents::workDuration,
                                                         hal::WorkDurationFixedV1>({
-                        .durationNanos = message.cpuDurationNanos,
+                        .durationNanos = message.durationNanos,
                         .workPeriodStartTimestampNanos = message.workPeriodStartTimestampNanos,
                         .cpuDurationNanos = message.cpuDurationNanos,
                         .gpuDurationNanos = message.gpuDurationNanos,
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
index 6229434..7c54ad2 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
@@ -16,7 +16,7 @@
 package com.android.carrierdefaultapp;
 
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index b802d0f..8e5a2b7 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Moenie toelaat nie"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Kanselleer"</string>
     <string name="consent_back" msgid="2560683030046918882">"Terug"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Rollees met die lys af"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Afwaartse pyl"</string>
     <string name="permission_expand" msgid="893185038020887411">"Vou <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> uit"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Vou <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> in"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Gee programme op &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; dieselfde toestemmings as op &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index d4d0b07..40472d2 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"አትፍቀድ"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"ይቅር"</string>
     <string name="consent_back" msgid="2560683030046918882">"ተመለስ"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"በዝርዝሩ ላይ ወደታች ያሸብልሉ"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"የአውርድ ቀስት"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>ን ዘርጋ"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>ን ሰብስብ"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"በ&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ላይ ላሉ መተግበሪያዎች በ&lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ላይ ካሉት ጋር ተመሳሳይ ፈቃዶች ይሰጣቸው?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 322d422..1f3ee73 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"অনুমতি নিদিব"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"বাতিল কৰক"</string>
     <string name="consent_back" msgid="2560683030046918882">"উভতি যাওক"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"সূচীখনত তললৈ স্ক্ৰ’ল কৰক"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"তলমুৱা কাঁড়"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> বিস্তাৰ কৰক"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> সংকোচন কৰক"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"এপ্‌সমূহক &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ত দিয়াৰ দৰে &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;তো একে অনুমতি প্ৰদান কৰিবনে?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index 9f3ab2bd..aa4788a 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"İcazə verməyin"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Ləğv edin"</string>
     <string name="consent_back" msgid="2560683030046918882">"Geriyə"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Siyahını aşağı sürüşdürün"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Aşağı ox"</string>
     <string name="permission_expand" msgid="893185038020887411">"Genişləndirin: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Yığcamlaşdırın: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; cihazındakı tətbiqlərə &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazındakılarla eyni icazələr verilsin?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index d28b9b2..3dcb585 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Otkaži"</string>
     <string name="consent_back" msgid="2560683030046918882">"Nazad"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Skrolujte nadole na listi"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Strelica nadole"</string>
     <string name="permission_expand" msgid="893185038020887411">"Proširi <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Skupi <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Aplikcijama na uređaju &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; dajete sve dozvole kao na uređaju &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index 214dc71..f5cf720 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"অনুমতি দেবেন না"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"বাতিল করুন"</string>
     <string name="consent_back" msgid="2560683030046918882">"ফিরুন"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"তালিকা নিচের দিকে স্ক্রল করুন"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"নিম্নমুখী তীরচিহ্ন"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> বড় করুন"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> আড়াল করুন"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-এ যে অনুমতি দেওয়া আছে &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;-এও সেই একই অনুমতি দিতে চান?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index b23f600..a87ea18 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Nemoj dozvoliti"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Otkaži"</string>
     <string name="consent_back" msgid="2560683030046918882">"Nazad"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Klizanje nadolje na listi"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Strelica nadolje"</string>
     <string name="permission_expand" msgid="893185038020887411">"Proširivanje stavke <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Sužavanje stavke <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Dati aplikacijama na uređaju &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ista odobrenja kao na uređaju &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index 21f3871..7ec2b8b 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"No permetis"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Cancel·la"</string>
     <string name="consent_back" msgid="2560683030046918882">"Enrere"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Desplaça la llista cap avall"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Fletxa avall"</string>
     <string name="permission_expand" msgid="893185038020887411">"Desplega <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Replega <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Vols concedir a les aplicacions del dispositiu &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; els mateixos permisos que tenen a &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index cd89130..a1ea39b 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Nepovolovat"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Zrušit"</string>
     <string name="consent_back" msgid="2560683030046918882">"Zpět"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Přejít v seznamu dolů"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Šipka dolů"</string>
     <string name="permission_expand" msgid="893185038020887411">"Rozbalit sekci <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Sbalit sekci <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Udělit aplikacím v zařízení &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; stejné oprávnění, jako mají v zařízení &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 6dc34e7..97e6fe56 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Tillad ikke"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Annuller"</string>
     <string name="consent_back" msgid="2560683030046918882">"Tilbage"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Rul ned på listen"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Pil ned"</string>
     <string name="permission_expand" msgid="893185038020887411">"Udvid <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Skjul <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Vil du give apps på &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; de samme tilladelser som på &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 4dc2929..fc0231f 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Nicht zulassen"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Abbrechen"</string>
     <string name="consent_back" msgid="2560683030046918882">"Zurück"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"In der Liste nach unten scrollen"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Abwärtspfeil"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> maximieren"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> minimieren"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Apps auf &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; die gleichen Berechtigungen geben wie auf &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index bbd81bf..68c74e0 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Cancel"</string>
     <string name="consent_back" msgid="2560683030046918882">"Back"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Scroll down the list"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Downward arrow"</string>
     <string name="permission_expand" msgid="893185038020887411">"Expand <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Collapse <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Give apps on &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; the same permissions as on &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index bbd81bf..68c74e0 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Cancel"</string>
     <string name="consent_back" msgid="2560683030046918882">"Back"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Scroll down the list"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Downward arrow"</string>
     <string name="permission_expand" msgid="893185038020887411">"Expand <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Collapse <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Give apps on &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; the same permissions as on &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index bbd81bf..68c74e0 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Cancel"</string>
     <string name="consent_back" msgid="2560683030046918882">"Back"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Scroll down the list"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Downward arrow"</string>
     <string name="permission_expand" msgid="893185038020887411">"Expand <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Collapse <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Give apps on &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; the same permissions as on &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index f8e2c74..d74b4f8 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Cancelar"</string>
     <string name="consent_back" msgid="2560683030046918882">"Atrás"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Desplazarse hacia abajo por la lista"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Flecha hacia abajo"</string>
     <string name="permission_expand" msgid="893185038020887411">"Desplegar <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Contraer <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"¿Dar a las aplicaciones de &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; los mismos permisos que &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 1c7f1f3..c133b77 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Utzi"</string>
     <string name="consent_back" msgid="2560683030046918882">"Atzera"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Egin behera zerrendan"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Beheranzko gezia"</string>
     <string name="permission_expand" msgid="893185038020887411">"Zabaldu <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Tolestu <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; gailuan dituzten baimen berberak eman nahi dizkiezu &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; gailuko aplikazioei?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index fb36c46..cf4fe25 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"اجازه ندادن"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"لغو"</string>
     <string name="consent_back" msgid="2560683030046918882">"برگشتن"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"پیمایش به‌پایین فهرست"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"جهت‌نمای پایین"</string>
     <string name="permission_expand" msgid="893185038020887411">"ازهم بازکردن <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"جمع کردن <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"‏به برنامه‌های موجود در &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; همان اجازه‌های &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; داده شود؟"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index ff6cae1..4be6a5a 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Älä salli"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Peruuta"</string>
     <string name="consent_back" msgid="2560683030046918882">"Takaisin"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Vieritä listaa alaspäin"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Alanuoli"</string>
     <string name="permission_expand" msgid="893185038020887411">"Laajenna <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Tiivistä <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Anna laitteen &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; sovelluksille samat luvat kuin laitteella &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index 4d7de76..70beafb 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Annuler"</string>
     <string name="consent_back" msgid="2560683030046918882">"Retour"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Faire défiler la liste en bas"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Flèche vers le bas"</string>
     <string name="permission_expand" msgid="893185038020887411">"Développer <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Réduire <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Accorder aux applis sur &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; les autorisations déjà accordées sur &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index d4c12fb..8714820 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -26,7 +26,7 @@
     <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Sélectionner l\'appareil qui sera géré par &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Sélectionner votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g> à configurer"</string>
-    <string name="single_device_title" msgid="4199861437545438606">"Recherche de l\'appareil suivant : <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+    <string name="single_device_title" msgid="4199861437545438606">"Recherche de votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
     <string name="summary_watch" msgid="8134580124808507407">"Cette appli sera autorisée à synchroniser des infos (comme le nom de l\'appelant) et disposera de ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"appareil"</string>
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Annuler"</string>
     <string name="consent_back" msgid="2560683030046918882">"Retour"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Faire défiler la liste vers le bas"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Flèche vers le bas"</string>
     <string name="permission_expand" msgid="893185038020887411">"Développer <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Réduire <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Accorder les mêmes autorisations aux applis sur &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; que sur &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index 999ab97..ce23be8 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Non permitir"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Cancelar"</string>
     <string name="consent_back" msgid="2560683030046918882">"Atrás"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Desprazarse abaixo na lista"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Frecha cara abaixo"</string>
     <string name="permission_expand" msgid="893185038020887411">"Despregar <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Contraer <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Queres darlles ás aplicacións de &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; os mesmos permisos que teñen as de &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 51d07c9..7956ba6 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"મંજૂરી આપશો નહીં"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"રદ કરો"</string>
     <string name="consent_back" msgid="2560683030046918882">"પાછળ"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"સૂચિ પર નીચે સ્ક્રોલ કરો"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"નીચેની તરફ ઍરો"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>ને મોટું કરો"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>ને નાનું કરો"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; પરની ઍપને &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; પર છે તે જ પરવાનગીઓ આપીએ?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index 04872e5..326564e 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Nemoj dopustiti"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Odustani"</string>
     <string name="consent_back" msgid="2560683030046918882">"Natrag"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Pomicanje po popisu"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Strelica prema dolje"</string>
     <string name="permission_expand" msgid="893185038020887411">"Proširi <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Sažmi <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Dati jednaka dopuštenja aplikacijama na uređaju &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; kao i na uređaju &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index e7c7139..cbf1d8d 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Չթույլատրել"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Չեղարկել"</string>
     <string name="consent_back" msgid="2560683030046918882">"Հետ"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Թերթեք ներքև"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Ներքև սլաք"</string>
     <string name="permission_expand" msgid="893185038020887411">"Ծավալել «<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>» բաժինը"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Ծալել «<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>» բաժինը"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;-ում հավելվածներին տա՞լ նույն թույլտվությունները, ինչ &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-ում"</string>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index f582205..fe3406e 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Batal"</string>
     <string name="consent_back" msgid="2560683030046918882">"Kembali"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Scroll daftar ke bawah"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Panah bawah"</string>
     <string name="permission_expand" msgid="893185038020887411">"Luaskan <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Ciutkan <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Berikan aplikasi di &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; izin yang sama seperti di &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index b66133c..9e366d6 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Hætta við"</string>
     <string name="consent_back" msgid="2560683030046918882">"Til baka"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Fletta niður listann"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Ör niður"</string>
     <string name="permission_expand" msgid="893185038020887411">"Stækka <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Minnka <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Veita forritum í &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; sömu heimildir og í &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 626ae7a..cf65bc9 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -19,7 +19,7 @@
     <string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string>
     <string name="confirmation_title" msgid="2244241995958340998">"‏לאפשר לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לגשת אל &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="message_discovery_soft_timeout" msgid="473346859407859161">"צריך לוודא שה-<xliff:g id="DISCOVERY_METHOD">%2$s</xliff:g> מופעל ב<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>, ושה<xliff:g id="PROFILE_NAME">%3$s</xliff:g> בקרבת מקום."</string>
-    <string name="message_discovery_hard_timeout" msgid="677514663495711424">"לא נמצאו מכשירים. אפשר לנסות שוב מאוחר יותר."</string>
+    <string name="message_discovery_hard_timeout" msgid="677514663495711424">"לא נמצאו מכשירים. כדאי לנסות שוב עוד מעט."</string>
     <string name="discovery_bluetooth" msgid="5693557668470016164">"Bluetooth"</string>
     <string name="discovery_wifi" msgid="1551782459721758773">"Wi-Fi"</string>
     <string name="discovery_mixed" msgid="7071466134150760127">"‏חיבור Wi-Fi ו-Bluetooth"</string>
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"אין אישור"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"ביטול"</string>
     <string name="consent_back" msgid="2560683030046918882">"חזרה"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"גלילה למטה ברשימה"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"חץ למטה"</string>
     <string name="permission_expand" msgid="893185038020887411">"הרחבה של <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"כיווץ של <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"‏האם לתת לאפליקציות ב-‎&lt;strong&gt;‎‏<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>‏‎&lt;/strong&gt;‎‏ את אותן הרשאות כמו ב-‏‎&lt;strong&gt;‎‏<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>‏‎&lt;/strong&gt;‎‏?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index 97f3087..d0214ae 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"არ დაიშვას"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"გაუქმება"</string>
     <string name="consent_back" msgid="2560683030046918882">"უკან"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"გადაადგილება სიის ქვემოთ"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"ქვემოთ მიმართული ისარი"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>-ის გაფართოება"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>-ის ჩაკეცვა"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"გსურთ აპებს &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;-ზე იგივე ნებართვები მიანიჭოთ, როგორიც აქვს &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-ზე?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index 4d77c6c..e794415 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Рұқсат бермеу"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Бас тарту"</string>
     <string name="consent_back" msgid="2560683030046918882">"Артқа"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Тізім соңына дейін айналдыру"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Төмен бағытталған перне"</string>
     <string name="permission_expand" msgid="893185038020887411">"\"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\" панелін жаю"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"\"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\" панелін жию"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; құрылғысындағы қолданбаларға &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; құрылғысындағыдай рұқсаттар берілсін бе?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 751ac1f..a957f48 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"ಅನುಮತಿಸಬೇಡಿ"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"ರದ್ದುಮಾಡಿ"</string>
     <string name="consent_back" msgid="2560683030046918882">"ಹಿಂದೆ"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"ಪಟ್ಟಿಯನ್ನು ಕೆಳಗೆ ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"ಕೆಳಮುಖ ಬಾಣ"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ಅನ್ನು ವಿಸ್ತರಿಸಿ"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;/strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ನಲ್ಲಿನ ಅನುಮತಿಗಳನ್ನೇ &lt;/strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ನಲ್ಲಿನ ಆ್ಯಪ್‌ಗಳಿಗೆ ನೀಡಬೇಕೆ?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index a7a5dc1..d7d489af 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"허용 안함"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"취소"</string>
     <string name="consent_back" msgid="2560683030046918882">"뒤로"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"목록을 아래로 스크롤"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"아래쪽 화살표"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> 펼치기"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> 접기"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;에 설치된 앱에 &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;에 설치된 앱과 동일한 권한을 부여하시겠습니까?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index 991fc9f..3f8c58a 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Уруксат берилбесин"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Жок"</string>
     <string name="consent_back" msgid="2560683030046918882">"Артка"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Тизменин ылдый жагына сыдыруу"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Ылдый караган жебе"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> жайып көрсөтүү"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> жыйыштыруу"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; түзмөгүнө да &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; түзмөгүнө берилген уруксаттар берилсинби?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index 91399b6..457efdb 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"ບໍ່ອະນຸຍາດ"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"ຍົກເລີກ"</string>
     <string name="consent_back" msgid="2560683030046918882">"ກັບຄືນ"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"ເລື່ອນລາຍຊື່ລົງ"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"ລູກສອນຊີ້ລົງ"</string>
     <string name="permission_expand" msgid="893185038020887411">"ຂະຫຍາຍ <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"ຫຍໍ້ <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ລົງ"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"ໃຫ້ການອະນຸຍາດແອັບຢູ່ &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ເປັນການອະນຸຍາດດຽວກັນກັບຢູ່ &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ບໍ?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index 35c7481..01c9ed5 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Neatļaut"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Atcelt"</string>
     <string name="consent_back" msgid="2560683030046918882">"Atpakaļ"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Ritināt sarakstu lejup"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Lejupvērsta bultiņa"</string>
     <string name="permission_expand" msgid="893185038020887411">"Izvērst: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Sakļaut: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Vai lietotnēm ierīcē &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; piešķirt tādas pašas atļaujas kā ierīcē &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index 1eaf2d2..d9c8695 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Не дозволувај"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Откажи"</string>
     <string name="consent_back" msgid="2560683030046918882">"Назад"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Лизгај надолу по списокот"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Стрелка надолу"</string>
     <string name="permission_expand" msgid="893185038020887411">"Прошири <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Собери <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Дасе дадат исти дозволи на апликациите на &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; како на &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index 52fcb1f..828b6cf 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"അനുവദിക്കരുത്"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"റദ്ദാക്കുക"</string>
     <string name="consent_back" msgid="2560683030046918882">"മടങ്ങുക"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"ലിസ്റ്റ് താഴേക്ക് സ്ക്രോൾ ചെയ്യൂ"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"താഴേക്കുള്ള അമ്പടയാളം"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> വികസിപ്പിക്കുക"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ചുരുക്കുക"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; എന്നതിലെ അതേ അനുമതികൾ &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിലെ ആപ്പുകൾക്ക് നൽകണോ?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 5faf241..be6eb8e 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Бүү зөвшөөр"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Цуцлах"</string>
     <string name="consent_back" msgid="2560683030046918882">"Буцах"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Жагсаалтыг доош гүйлгэх"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Доош заасан сум"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>-г дэлгэх"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>-г хураах"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; дээрх аппуудад &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; дээрхтэй адил зөвшөөрөл өгөх үү?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 94e49fe..99f59a6 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"रद्द करा"</string>
     <string name="consent_back" msgid="2560683030046918882">"मागे जा"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"सूचीवर खाली स्क्रोल करा"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"खालच्या दिशेचा अ‍ॅरो"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> चा विस्तार करा"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> कोलॅप्स करा"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; वरील अ‍ॅप्सना &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; प्रमाणेच परवानग्या द्यायच्या आहेत का?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index 8b1170b..9c15c06 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Jangan benarkan"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Batal"</string>
     <string name="consent_back" msgid="2560683030046918882">"Kembali"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Tatal ke bawah senarai"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Anak panah ke bawah"</string>
     <string name="permission_expand" msgid="893185038020887411">"Kembangkan <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Kuncupkan <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Beri apl pada &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; kebenaran yang sama seperti pada &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index faca3e3..6068c43 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"ခွင့်မပြုပါ"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"မလုပ်တော့"</string>
     <string name="consent_back" msgid="2560683030046918882">"နောက်သို့"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"စာရင်းအောက်သို့ လှိမ့်ရန်"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"အောက်ညွှန်မြား"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ကို ပိုပြရန်"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ကို လျှော့ပြရန်"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"အက်ပ်များကို &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; တွင်ပေးထားသည့် ခွင့်ပြုချက်များအတိုင်း &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; တွင် ပေးမလား။"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index 4188951..eb5e2e9 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Ikke tillat"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Avbryt"</string>
     <string name="consent_back" msgid="2560683030046918882">"Tilbake"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Rull nedover i listen"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Nedoverpil"</string>
     <string name="permission_expand" msgid="893185038020887411">"Vis <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Skjul <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Vil du gi apper på &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; de samme tillatelsene som på &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index 2cd4fef..f3b56de 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"अनुमति नदिनुहोस्"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"रद्द गर्नुहोस्"</string>
     <string name="consent_back" msgid="2560683030046918882">"पछाडि"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"सूचीको तलतिर स्क्रोल गर्नुहोस्"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"डाउनवार्ड एरो"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> एक्स्पान्ड गर्नुहोस्"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; मा भएका एपहरूलाई पनि &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; मा दिइएकै अनुमति दिने हो?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index 8298a5b..bd602e4 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"ବାତିଲ କରନ୍ତୁ"</string>
     <string name="consent_back" msgid="2560683030046918882">"ପଛକୁ ଫେରନ୍ତୁ"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"ତାଲିକା ତଳକୁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"ଡାଉନୱାର୍ଡ ତୀର"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>କୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>କୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ପରି &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;ରେ ଥିବା ଆପ୍ସକୁ ସମାନ ଅନୁମତିଗୁଡ଼ିକ ଦେବେ?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index fe13c00..ae0b59d 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"ਆਗਿਆ ਨਾ ਦਿਓ"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"ਰੱਦ ਕਰੋ"</string>
     <string name="consent_back" msgid="2560683030046918882">"ਪਿੱਛੇ"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"ਸੂਚੀ ਨੂੰ ਹੇਠਾਂ ਵੱਲ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"ਹੇਠਾਂ ਤੀਰ"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ਨੂੰ ਸਮੇਟੋ"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"ਕੀ &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; \'ਤੇ ਮੌਜੂਦ ਐਪਾਂ ਨੂੰ &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; \'ਤੇ ਮੌਜੂਦ ਐਪਾਂ ਵਾਂਗ ਇਜਾਜ਼ਤਾਂ ਦੇਣੀਆਂ ਹਨ?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index 1599f3f..bce0a6e 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Anuluj"</string>
     <string name="consent_back" msgid="2560683030046918882">"Wstecz"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Przewiń listę w dół"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Strzałka w dół"</string>
     <string name="permission_expand" msgid="893185038020887411">"Rozwiń sekcję <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Zwiń sekcję <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Czy aplikacjom na urządzeniu &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; przyznać te same uprawnienia co na urządzeniu &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index 85c23ea7..35b653a 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Cancelar"</string>
     <string name="consent_back" msgid="2560683030046918882">"Voltar"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Role para baixo na lista"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Seta para baixo"</string>
     <string name="permission_expand" msgid="893185038020887411">"Abrir <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Fechar <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Dar aos apps no dispositivo &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; as mesmas permissões do dispositivo &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index 85c23ea7..35b653a 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Cancelar"</string>
     <string name="consent_back" msgid="2560683030046918882">"Voltar"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Role para baixo na lista"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Seta para baixo"</string>
     <string name="permission_expand" msgid="893185038020887411">"Abrir <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Fechar <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Dar aos apps no dispositivo &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; as mesmas permissões do dispositivo &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 6820d5b..0fdeb29 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Nu permite"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Anulează"</string>
     <string name="consent_back" msgid="2560683030046918882">"Înapoi"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Derulează în jos lista"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Săgeată în jos"</string>
     <string name="permission_expand" msgid="893185038020887411">"Extinde <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Restrânge <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Acorzi aplicațiilor de pe &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; aceleași permisiuni ca pe &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 3213a2f..872f45e 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Запретить"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Отмена"</string>
     <string name="consent_back" msgid="2560683030046918882">"Назад"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Прокрутить список вниз"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Стрелка вниз"</string>
     <string name="permission_expand" msgid="893185038020887411">"Разворачивать список \"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\"."</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Сворачивать список \"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\"."</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Предоставить приложениям на устройстве &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; те же разрешения, что на устройстве &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index 7fd6611..ec362f2 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"ඉඩ නොදෙන්න"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"අවලංගු කරන්න"</string>
     <string name="consent_back" msgid="2560683030046918882">"ආපසු"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"ලැයිස්තුව පහළට අනුචලනය කරන්න"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"පහළට ඊතලය"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> විදහන්න"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> හකුළන්න"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; the හි යෙදුම්වලට &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; හි අවසරම දෙන්නද?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index 5194f21..cbf5648 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Nepovoliť"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Zrušiť"</string>
     <string name="consent_back" msgid="2560683030046918882">"Späť"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Posunúť zoznam nadol"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Šípka nadol"</string>
     <string name="permission_expand" msgid="893185038020887411">"Rozbaliť sekciu <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Zbaliť sekciu <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Chcete udeliť aplikáciám v zariadení &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; rovnaké povolenia ako v zariadení &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 2200220..20570c30 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Ne dovoli"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Prekliči"</string>
     <string name="consent_back" msgid="2560683030046918882">"Nazaj"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Pomikanje navzdol po seznamu"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Puščica navzdol"</string>
     <string name="permission_expand" msgid="893185038020887411">"Razširi dovoljenje »<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>«"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Strni dovoljenje »<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>«"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Ali želite aplikacijam v napravi &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; odobriti enaka dovoljenja kot v napravi &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index f683dd9..32ede40 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Mos lejo"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Anulo"</string>
     <string name="consent_back" msgid="2560683030046918882">"Pas"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Lëviz poshtë në listë"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Shigjeta poshtë"</string>
     <string name="permission_expand" msgid="893185038020887411">"Zgjero: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Palos: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"T\'i jepen aplikacioneve në &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; të njëjtat leje si në &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index d568009..ac84c34 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Откажи"</string>
     <string name="consent_back" msgid="2560683030046918882">"Назад"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Скролујте надоле на листи"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Стрелица надоле"</string>
     <string name="permission_expand" msgid="893185038020887411">"Прошири <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Скупи <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Апликцијама на уређају &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; дајете све дозволе као на уређају &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index 5c6f5ba..c9364b8 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Tillåt inte"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Avbryt"</string>
     <string name="consent_back" msgid="2560683030046918882">"Tillbaka"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Scrolla nedåt i listan"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Nedåtpil"</string>
     <string name="permission_expand" msgid="893185038020887411">"Utöka <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Komprimera <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Vill du ge apparna på &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; samma behörigheter som de har på &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 7a1cf98..2a9cbee 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Usiruhusu"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Ghairi"</string>
     <string name="consent_back" msgid="2560683030046918882">"Nyuma"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Sogeza chini kwenye orodha"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Kishale kinachoelekeza chini"</string>
     <string name="permission_expand" msgid="893185038020887411">"Panua <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Kunja <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Ungependa kuzipa programu katika &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ruhusa ile ile kama kwenye &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 2313cc3..6697a15 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"கம்பேனியன் சாதன நிர்வாகி"</string>
     <string name="confirmation_title" msgid="2244241995958340998">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; சாதனத்தை அணுக &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
-    <string name="message_discovery_soft_timeout" msgid="473346859407859161">"இந்த <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> சாதனத்தின் <xliff:g id="DISCOVERY_METHOD">%2$s</xliff:g> ஆன் செய்யப்பட்டு, அதன் அருகில் உங்கள் <xliff:g id="PROFILE_NAME">%3$s</xliff:g> இருப்பதை உறுதிசெய்துகொள்ளவும்."</string>
+    <string name="message_discovery_soft_timeout" msgid="473346859407859161">"இந்த <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> <xliff:g id="DISCOVERY_METHOD">%2$s</xliff:g> ஆக இருக்க வேண்டும். உங்கள் <xliff:g id="PROFILE_NAME">%3$s</xliff:g> அருகில் இருக்க வேண்டும்."</string>
     <string name="message_discovery_hard_timeout" msgid="677514663495711424">"சாதனங்கள் எதுவும் கண்டறியப்படவில்லை. பிறகு முயலவும்."</string>
     <string name="discovery_bluetooth" msgid="5693557668470016164">"புளூடூத்"</string>
     <string name="discovery_wifi" msgid="1551782459721758773">"வைஃபை"</string>
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"அனுமதிக்க வேண்டாம்"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"ரத்துசெய்"</string>
     <string name="consent_back" msgid="2560683030046918882">"பின்செல்"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"பட்டியலில் கீழே நகர்த்து"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"கீழ்நோக்கிய அம்புக்குறி"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ஐ விரிவாக்கும்"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ஐச் சுருக்கும்"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; சாதனத்தில் இருக்கும் அதே அனுமதிகளை &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; சாதனத்தில் உள்ள ஆப்ஸுக்கும் வழங்கவா?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index eed0eee..d82c57c 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"అనుమతించవద్దు"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"రద్దు చేయండి"</string>
     <string name="consent_back" msgid="2560683030046918882">"వెనుకకు"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"లిస్ట్‌ను కిందకు స్క్రోల్ చేయి"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"కింది వైపు బాణం గుర్తు"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>‌ను విస్తరించండి"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>‌ను కుదించండి"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;లోని యాప్‌లకు &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;లో ఉన్న అనుమతులను ఇవ్వాలా?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index bf14f4c..26aa9dd 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"ไม่อนุญาต"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"ยกเลิก"</string>
     <string name="consent_back" msgid="2560683030046918882">"กลับ"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"เลื่อนรายการลง"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"ลูกศรชี้ลง"</string>
     <string name="permission_expand" msgid="893185038020887411">"ขยาย <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"ยุบ <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"ให้แอปใน &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; มีสิทธิ์เหมือนกับใน &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ไหม"</string>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index 56997d7..7f2671c 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -45,16 +45,14 @@
     <string name="title_sensor_device_streaming" msgid="2395553261097861497">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasının, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ve &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazları arasında ses ve sistem özelliklerini aktarmasına izin verilsin mi?"</string>
     <string name="summary_sensor_device_streaming" msgid="3413105061195145547">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> uygulaması; <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> cihazınızda oynatılan her şeye erişebilecek.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_2">%1$s</xliff:g> uygulaması bu iznin erişimini kaldırana kadar sesleri <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> cihazına aktarabilecek."</string>
     <string name="helper_summary_sensor_device_streaming" msgid="8860174545653786353">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulaması, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> için cihazlarınız arasında ses ve sistem özelliklerini aktarma izni istiyor."</string>
-    <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"Cihaz"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Bu uygulama, arayan kişinin adı gibi bilgileri telefonunuz ve seçili cihaz arasında senkronize edebilir"</string>
     <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string>
     <string name="consent_no" msgid="2640796915611404382">"İzin verme"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"İptal"</string>
     <string name="consent_back" msgid="2560683030046918882">"Geri"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Listeyi aşağı kaydırın"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Aşağı ok"</string>
     <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> panelini genişlet"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> panelini daralt"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; cihazındaki uygulamalara, &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazındakiyle aynı izinler verilsin mi?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 7d7b9de..73e5d58 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Не дозволяти"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Скасувати"</string>
     <string name="consent_back" msgid="2560683030046918882">"Назад"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Прокрутити список униз"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Стрілка вниз"</string>
     <string name="permission_expand" msgid="893185038020887411">"Розгорнути дозвіл \"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\""</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Згорнути дозвіл \"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\""</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Надати додаткам на пристрої &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; такі самі дозволи, що й на пристрої &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index e1a024c..b4ac45c 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Ruxsat berilmasin"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Bekor qilish"</string>
     <string name="consent_back" msgid="2560683030046918882">"Orqaga"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Roʻyxatni pastga varaqlash"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Pastga strelka"</string>
     <string name="permission_expand" msgid="893185038020887411">"Yoyish: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Yopish: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovalariga &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; qurilmasidagi kabi bir xil ruxsatlar berilsinmi?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index 0293317..11cceaf 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Không cho phép"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Huỷ"</string>
     <string name="consent_back" msgid="2560683030046918882">"Quay lại"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Di chuyển xuống danh sách"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Mũi tên xuống"</string>
     <string name="permission_expand" msgid="893185038020887411">"Mở rộng <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Thu gọn <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Cấp cho các ứng dụng trên &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; các quyền giống như trên &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index ed68dd3..7632af0 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"不允许"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"取消"</string>
     <string name="consent_back" msgid="2560683030046918882">"返回"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"向下滚动列表"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"向下箭头"</string>
     <string name="permission_expand" msgid="893185038020887411">"展开<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"收起<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"要让&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;上的应用享有在&lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;上的同等权限吗?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index 17d3482f..9f988c3 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"不允許"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"取消"</string>
     <string name="consent_back" msgid="2560683030046918882">"返回"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"向下捲動清單"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"向下箭咀"</string>
     <string name="permission_expand" msgid="893185038020887411">"展開<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"收合<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"&lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; 上的應用程式可獲在 &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; 上的相同權限嗎?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index 542a73fc..5bc7bfa 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"不允許"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"取消"</string>
     <string name="consent_back" msgid="2560683030046918882">"返回"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"向下捲動清單"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"向下箭頭"</string>
     <string name="permission_expand" msgid="893185038020887411">"展開<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"收合<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"要讓「<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;的應用程式沿用在「<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;上的權限嗎?"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index b1c7a14..37034fa 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -51,10 +51,8 @@
     <string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string>
     <string name="consent_cancel" msgid="5655005528379285841">"Khansela"</string>
     <string name="consent_back" msgid="2560683030046918882">"Emuva"</string>
-    <!-- no translation found for downward_arrow_action (2327165938832076333) -->
-    <skip />
-    <!-- no translation found for downward_arrow (2292427714411156088) -->
-    <skip />
+    <string name="downward_arrow_action" msgid="2327165938832076333">"Skrola uye ezansi ohlwini"</string>
+    <string name="downward_arrow" msgid="2292427714411156088">"Umcibisholo obheke phansi"</string>
     <string name="permission_expand" msgid="893185038020887411">"Nweba i-<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_collapse" msgid="3320833884220844084">"Goqa i-<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string>
     <string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Nikeza ama-app &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; izimvume ezifanayot &lt;strong&gt;njengaku-<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index b2c1e60..964268e 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -65,6 +65,7 @@
 import android.net.MacAddress;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.text.Spanned;
@@ -621,8 +622,10 @@
             Slog.w(TAG, "Already selected.");
             return;
         }
-        // Notify the adapter to highlight the selected item.
-        mDeviceAdapter.setSelectedPosition(position);
+        // Delay highlighting the selected item by posting to the main thread.
+        // This helps avoid flicker in the user consent dialog after device selection.
+        new Handler(
+                Looper.getMainLooper()).post(() -> mDeviceAdapter.setSelectedPosition(position));
 
         mSelectedDevice = requireNonNull(selectedDevice);
 
diff --git a/packages/ExternalStorageProvider/res/values-fa/strings.xml b/packages/ExternalStorageProvider/res/values-fa/strings.xml
index 9eabfd0..28781f1 100644
--- a/packages/ExternalStorageProvider/res/values-fa/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-fa/strings.xml
@@ -18,6 +18,6 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="748293919008814871">"حافظه خارجی"</string>
     <string name="storage_description" msgid="9176081505553938524">"فضای ذخیره‌سازی محلی"</string>
-    <string name="root_internal_storage" msgid="4980477711224234931">"حافظهٔ داخلی"</string>
+    <string name="root_internal_storage" msgid="4980477711224234931">"فضای ذخیره‌سازی داخلی"</string>
     <string name="root_documents" msgid="5695037589229175941">"اسناد"</string>
 </resources>
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 8e52a00..a0e008c 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -19,6 +19,7 @@
 import static android.content.Intent.ACTION_USER_SWITCHED;
 import static android.location.LocationManager.GPS_PROVIDER;
 import static android.location.LocationManager.NETWORK_PROVIDER;
+import static android.location.LocationRequest.QUALITY_HIGH_ACCURACY;
 import static android.location.LocationRequest.QUALITY_LOW_POWER;
 import static android.location.provider.ProviderProperties.ACCURACY_FINE;
 import static android.location.provider.ProviderProperties.POWER_USAGE_LOW;
@@ -34,6 +35,7 @@
 import android.location.LocationListener;
 import android.location.LocationManager;
 import android.location.LocationRequest;
+import android.location.flags.Flags;
 import android.location.provider.LocationProviderBase;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
@@ -61,6 +63,9 @@
                 .build();
 
     private static final long MAX_LOCATION_COMPARISON_NS = 11 * 1000000000L; // 11 seconds
+    // Maximum request interval at which we will activate GPS (because GPS sometimes consumes
+    // excessive power with large intervals).
+    private static final long MAX_GPS_INTERVAL_MS = 5 * 1000; // 5 seconds
 
     private final Object mLock = new Object();
 
@@ -165,8 +170,13 @@
             mNlpPresent = mLocationManager.hasProvider(NETWORK_PROVIDER);
         }
 
+        boolean requestAllowsGps =
+                Flags.limitFusedGps()
+                    ? mRequest.getQuality() == QUALITY_HIGH_ACCURACY
+                        && mRequest.getIntervalMillis() <= MAX_GPS_INTERVAL_MS
+                    : !mNlpPresent || mRequest.getQuality() < QUALITY_LOW_POWER;
         long gpsInterval =
-                mGpsPresent && (!mNlpPresent || mRequest.getQuality() < QUALITY_LOW_POWER)
+                mGpsPresent && requestAllowsGps
                         ? mRequest.getIntervalMillis() : INTERVAL_DISABLED;
         long networkInterval = mNlpPresent ? mRequest.getIntervalMillis() : INTERVAL_DISABLED;
 
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 5033101..716845c 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -23,6 +23,28 @@
       ]
     },
     {
+      "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+      "options":[
+          {
+            "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+            "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+      "options":[
+          {
+            "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+            "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
       "name": "CtsPackageInstallerCUJMultiUsersTestCases",
       "options":[
         {
@@ -120,6 +142,28 @@
       ]
     },
     {
+      "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+      "options":[
+          {
+            "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+            "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+      "options":[
+          {
+            "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+            "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
       "name": "CtsPackageInstallerCUJMultiUsersTestCases",
       "options":[
         {
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
index 0bc4be3..f799830 100644
--- a/packages/PackageInstaller/res/values-ne/strings.xml
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="7488448184431507488">"प्याकेज स्थापनाकर्ता"</string>
-    <string name="install" msgid="711829760615509273">"स्थापना गर्नु…"</string>
+    <string name="install" msgid="711829760615509273">"इन्स्टल"</string>
     <string name="update" msgid="3932142540719227615">"अपडेट गर्नुहोस्"</string>
     <string name="done" msgid="6632441120016885253">"सम्पन्न भयो"</string>
     <string name="cancel" msgid="1018267193425558088">"रद्द गर्नुहोस्"</string>
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
index 472ffa9..6dec2f9 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
@@ -17,6 +17,7 @@
 package com.android.settingslib.datastore
 
 import android.content.SharedPreferences
+import android.util.Log
 
 /** Interface of key-value store. */
 interface KeyValueStore : KeyedObservable<String> {
@@ -80,6 +81,27 @@
     fun setString(key: String, value: String?) = setValue(key, String::class.javaObjectType, value)
 }
 
+/** Delegation of [KeyValueStore]. */
+interface KeyValueStoreDelegate : KeyValueStore, KeyedObservableDelegate<String> {
+
+    /** [KeyValueStore] to delegate. */
+    val keyValueStoreDelegate: KeyValueStore
+
+    override val keyedObservableDelegate
+        get() = keyValueStoreDelegate
+
+    override fun contains(key: String) = keyValueStoreDelegate.contains(key)
+
+    override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) =
+        keyValueStoreDelegate.getDefaultValue(key, valueType)
+
+    override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+        keyValueStoreDelegate.getValue(key, valueType) ?: getDefaultValue(key, valueType)
+
+    override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) =
+        keyValueStoreDelegate.setValue(key, valueType, value)
+}
+
 /** [SharedPreferences] based [KeyValueStore]. */
 interface SharedPreferencesKeyValueStore : KeyValueStore {
 
@@ -103,11 +125,11 @@
 
     @Suppress("UNCHECKED_CAST")
     override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+        val edit = sharedPreferences.edit()
         if (value == null) {
-            sharedPreferences.edit().remove(key).apply()
+            edit.remove(key).apply()
             return
         }
-        val edit = sharedPreferences.edit()
         when (valueType) {
             Boolean::class.javaObjectType -> edit.putBoolean(key, value as Boolean)
             Float::class.javaObjectType -> edit.putFloat(key, value as Float)
@@ -115,7 +137,7 @@
             Long::class.javaObjectType -> edit.putLong(key, value as Long)
             String::class.javaObjectType -> edit.putString(key, value as String)
             Set::class.javaObjectType -> edit.putStringSet(key, value as Set<String>)
-            else -> {}
+            else -> Log.e(LOG_TAG, "Unsupported $valueType for $key: $value")
         }
         edit.apply()
     }
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index 07b1c9e..ff58bf7 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -116,8 +116,28 @@
 }
 
 /** Delegation of [KeyedObservable]. */
-open class KeyedObservableDelegate<K>(delegate: KeyedObservable<K>) :
-    KeyedObservable<K> by delegate
+interface KeyedObservableDelegate<K> : KeyedObservable<K> {
+
+    /** [KeyedObservable] to delegate. */
+    val keyedObservableDelegate: KeyedObservable<K>
+
+    override fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean =
+        keyedObservableDelegate.addObserver(observer, executor)
+
+    override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean =
+        keyedObservableDelegate.addObserver(key, observer, executor)
+
+    override fun removeObserver(observer: KeyedObserver<K?>): Boolean =
+        keyedObservableDelegate.removeObserver(observer)
+
+    override fun removeObserver(key: K, observer: KeyedObserver<K>): Boolean =
+        keyedObservableDelegate.removeObserver(key, observer)
+
+    override fun notifyChange(reason: Int): Unit = keyedObservableDelegate.notifyChange(reason)
+
+    override fun notifyChange(key: K, reason: Int): Unit =
+        keyedObservableDelegate.notifyChange(key, reason)
+}
 
 /** A thread safe implementation of [KeyedObservable]. */
 open class KeyedDataObservable<K> : KeyedObservable<K> {
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
new file mode 100644
index 0000000..fdde3d3
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate
+
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+
+/**
+ * Interface for managing [DEVICE_STATE_ROTATION_LOCK] setting.
+ *
+ * It provides methods to register/unregister listeners for setting changes, update the setting for
+ * specific device states, retrieve the setting value, and check if rotation is locked for specific
+ * or all device states.
+ */
+interface DeviceStateAutoRotateSettingManager {
+    // TODO: b/397928958 - Rename all terms from rotationLock to autoRotate in all apis.
+
+    /** Listener for changes in device-state based auto rotate setting. */
+    interface DeviceStateAutoRotateSettingListener {
+        /** Called whenever the setting has changed. */
+        fun onSettingsChanged()
+    }
+
+    /** Register listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+    fun registerListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+    /** Unregister listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+    fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+    /**
+     * Write [deviceState]'s setting value as [autoRotate], for [DEVICE_STATE_ROTATION_LOCK] setting.
+     */
+    fun updateSetting(deviceState: Int, autoRotate: Boolean)
+
+    /** Get [DEVICE_STATE_ROTATION_LOCK] setting value for [deviceState]. */
+    fun getRotationLockSetting(deviceState: Int): Int
+
+    /** Returns true if auto-rotate setting is OFF for [deviceState]. */
+    fun isRotationLocked(deviceState: Int): Boolean
+
+    /** Returns true if the auto-rotate setting value for all device states is OFF. */
+    fun isRotationLockedForAllStates(): Boolean
+
+    /** Returns a list of device states and their respective auto rotate setting availability. */
+    fun getSettableDeviceStates(): List<SettableDeviceState>
+}
+
+/** Represents a device state and whether it has an auto-rotation setting. */
+data class SettableDeviceState(
+    /** Returns the device state associated with this object. */
+    val deviceState: Int,
+    /** Returns whether there is an auto-rotation setting for this device state. */
+    val isSettable: Boolean
+)
+
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
new file mode 100644
index 0000000..0b6c6e2
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.devicestate
+
+import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.util.Log
+import android.util.SparseIntArray
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.android.window.flags.Flags
+import java.util.concurrent.Executor
+
+/**
+ * Implementation of [DeviceStateAutoRotateSettingManager]. This implementation is a part of
+ * refactoring, it should be used when [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]
+ * is enabled.
+ */
+class DeviceStateAutoRotateSettingManagerImpl(
+    context: Context,
+    backgroundExecutor: Executor,
+    private val secureSettings: SecureSettings,
+    private val mainHandler: Handler,
+    private val posturesHelper: PosturesHelper,
+) : DeviceStateAutoRotateSettingManager {
+    // TODO: b/397928958 rename the fields and apis from rotationLock to autoRotate.
+
+    private val settingListeners: MutableList<DeviceStateAutoRotateSettingListener> =
+        mutableListOf()
+    private val fallbackPostureMap = SparseIntArray()
+    private val settableDeviceState: MutableList<SettableDeviceState> = mutableListOf()
+
+    private val autoRotateSettingValue: String
+        get() = secureSettings.getStringForUser(DEVICE_STATE_ROTATION_LOCK, UserHandle.USER_CURRENT)
+
+    init {
+        loadAutoRotateDeviceStates(context)
+        val contentObserver =
+            object : ContentObserver(mainHandler) {
+                override fun onChange(selfChange: Boolean) = notifyListeners()
+            }
+        backgroundExecutor.execute {
+            secureSettings.registerContentObserver(
+                DEVICE_STATE_ROTATION_LOCK, false, contentObserver, UserHandle.USER_CURRENT
+            )
+        }
+    }
+
+    override fun registerListener(settingListener: DeviceStateAutoRotateSettingListener) {
+        settingListeners.add(settingListener)
+    }
+
+    override fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener) {
+        if (!settingListeners.remove(settingListener)) {
+            Log.w(TAG, "Attempting to unregister a listener hadn't been registered")
+        }
+    }
+
+    override fun getRotationLockSetting(deviceState: Int): Int {
+        val devicePosture = posturesHelper.deviceStateToPosture(deviceState)
+        val serializedSetting = autoRotateSettingValue
+        val autoRotateSetting = extractSettingForDevicePosture(devicePosture, serializedSetting)
+
+        // If the setting is ignored for this posture, check the fallback posture.
+        if (autoRotateSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+            val fallbackPosture =
+                fallbackPostureMap.get(devicePosture, DEVICE_STATE_ROTATION_LOCK_IGNORED)
+            return extractSettingForDevicePosture(fallbackPosture, serializedSetting)
+        }
+
+        return autoRotateSetting
+    }
+
+    override fun isRotationLocked(deviceState: Int) =
+        getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED
+
+    override fun isRotationLockedForAllStates(): Boolean =
+        convertSerializedSettingToMap(autoRotateSettingValue).all { (_, value) ->
+            value == DEVICE_STATE_ROTATION_LOCK_LOCKED
+        }
+
+    override fun getSettableDeviceStates(): List<SettableDeviceState> = settableDeviceState
+
+    override fun updateSetting(deviceState: Int, autoRotate: Boolean) {
+        // TODO: b/350946537 - Create IPC to update the setting, and call it here.
+        throw UnsupportedOperationException("API updateSetting is not implemented yet")
+    }
+
+    private fun notifyListeners() =
+        settingListeners.forEach { listener -> listener.onSettingsChanged() }
+
+    private fun loadAutoRotateDeviceStates(context: Context) {
+        val perDeviceStateAutoRotateDefaults =
+            context.resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+        for (entry in perDeviceStateAutoRotateDefaults) {
+            entry.parsePostureEntry()?.let { (posture, autoRotate, fallbackPosture) ->
+                if (autoRotate == DEVICE_STATE_ROTATION_LOCK_IGNORED && fallbackPosture != null) {
+                    fallbackPostureMap.put(posture, fallbackPosture)
+                }
+                settableDeviceState.add(
+                    SettableDeviceState(posture, autoRotate != DEVICE_STATE_ROTATION_LOCK_IGNORED)
+                )
+            }
+        }
+    }
+
+    private fun convertSerializedSettingToMap(serializedSetting: String): Map<Int, Int> {
+        if (serializedSetting.isEmpty()) return emptyMap()
+        return try {
+            serializedSetting
+                .split(SEPARATOR_REGEX)
+                .hasEvenSize()
+                .chunked(2)
+                .mapNotNull(::parsePostureSettingPair)
+                .toMap()
+        } catch (e: Exception) {
+            Log.w(
+                TAG,
+                "Invalid format in serializedSetting=$serializedSetting: ${e.message}"
+            )
+            return emptyMap()
+        }
+    }
+
+    private fun List<String>.hasEvenSize(): List<String> {
+        if (this.size % 2 != 0) {
+            throw IllegalStateException("Odd number of elements in the list")
+        }
+        return this
+    }
+
+    private fun parsePostureSettingPair(settingPair: List<String>): Pair<Int, Int>? {
+        return settingPair.let { (keyStr, valueStr) ->
+            val key = keyStr.toIntOrNull()
+            val value = valueStr.toIntOrNull()
+            if (key != null && value != null && value in 0..2) {
+                key to value
+            } else {
+                Log.w(TAG, "Invalid key or value in pair: $keyStr, $valueStr")
+                null // Invalid pair, skip it
+            }
+        }
+    }
+
+    private fun extractSettingForDevicePosture(
+        devicePosture: Int,
+        serializedSetting: String
+    ): Int =
+        convertSerializedSettingToMap(serializedSetting)[devicePosture]
+            ?: DEVICE_STATE_ROTATION_LOCK_IGNORED
+
+    private fun String.parsePostureEntry(): Triple<Int, Int, Int?>? {
+        val values = split(SEPARATOR_REGEX)
+        if (values.size !in 2..3) { // It should contain 2 or 3 values.
+            Log.w(TAG, "Invalid number of values in entry: '$this'")
+            return null
+        }
+        return try {
+            val posture = values[0].toInt()
+            val rotationLockSetting = values[1].toInt()
+            val fallbackPosture = if (values.size == 3) values[2].toIntOrNull() else null
+            Triple(posture, rotationLockSetting, fallbackPosture)
+        } catch (e: NumberFormatException) {
+            Log.w(TAG, "Invalid number format in '$this': ${e.message}")
+            null
+        }
+    }
+
+    companion object {
+        private const val TAG = "DeviceStateAutoRotate"
+        private const val SEPARATOR_REGEX = ":"
+    }
+}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
new file mode 100644
index 0000000..4d1d292
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("DeviceStateAutoRotateSettingUtils")
+
+package com.android.settingslib.devicestate
+
+import android.content.Context
+import com.android.internal.R
+
+/** Returns true if device-state based rotation lock settings are enabled. */
+object DeviceStateAutoRotateSettingUtils {
+    @JvmStatic
+    fun isDeviceStateRotationLockEnabled(context: Context) =
+        context.resources
+            .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+            .isNotEmpty()
+}
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index 635f690..deeba57 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -20,6 +20,8 @@
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
 import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
 
+import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener;
+
 import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -43,7 +45,6 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -58,7 +59,7 @@
     private static DeviceStateRotationLockSettingsManager sSingleton;
 
     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
-    private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
+    private final Set<DeviceStateAutoRotateSettingListener> mListeners = new HashSet<>();
     private final SecureSettings mSecureSettings;
     private final PosturesHelper mPosturesHelper;
     private String[] mPostureRotationLockDefaults;
@@ -127,20 +128,20 @@
     }
 
     /**
-     * Registers a {@link DeviceStateRotationLockSettingsListener} to be notified when the settings
+     * Registers a {@link DeviceStateAutoRotateSettingListener} to be notified when the settings
      * change. Can be called multiple times with different listeners.
      */
-    public void registerListener(DeviceStateRotationLockSettingsListener runnable) {
+    public void registerListener(DeviceStateAutoRotateSettingListener runnable) {
         mListeners.add(runnable);
     }
 
     /**
-     * Unregisters a {@link DeviceStateRotationLockSettingsListener}. No-op if the given instance
+     * Unregisters a {@link DeviceStateAutoRotateSettingListener}. No-op if the given instance
      * was never registered.
      */
     public void unregisterListener(
-            DeviceStateRotationLockSettingsListener deviceStateRotationLockSettingsListener) {
-        if (!mListeners.remove(deviceStateRotationLockSettingsListener)) {
+            DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) {
+        if (!mListeners.remove(deviceStateAutoRotateSettingListener)) {
             Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
         }
     }
@@ -379,56 +380,8 @@
     }
 
     private void notifyListeners() {
-        for (DeviceStateRotationLockSettingsListener r : mListeners) {
+        for (DeviceStateAutoRotateSettingListener r : mListeners) {
             r.onSettingsChanged();
         }
     }
-
-    /** Listener for changes in device-state based rotation lock settings */
-    public interface DeviceStateRotationLockSettingsListener {
-        /** Called whenever the settings have changed. */
-        void onSettingsChanged();
-    }
-
-    /** Represents a device state and whether it has an auto-rotation setting. */
-    public static class SettableDeviceState {
-        private final int mDeviceState;
-        private final boolean mIsSettable;
-
-        SettableDeviceState(int deviceState, boolean isSettable) {
-            mDeviceState = deviceState;
-            mIsSettable = isSettable;
-        }
-
-        /** Returns the device state associated with this object. */
-        public int getDeviceState() {
-            return mDeviceState;
-        }
-
-        /** Returns whether there is an auto-rotation setting for this device state. */
-        public boolean isSettable() {
-            return mIsSettable;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof SettableDeviceState)) return false;
-            SettableDeviceState that = (SettableDeviceState) o;
-            return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mDeviceState, mIsSettable);
-        }
-
-        @Override
-        public String toString() {
-            return "SettableDeviceState{"
-                    + "mDeviceState=" + mDeviceState
-                    + ", mIsSettable=" + mIsSettable
-                    + '}';
-        }
-    }
 }
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
index 1052873..ea40e14 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
@@ -19,7 +19,7 @@
 import android.database.ContentObserver;
 
 /** Minimal wrapper interface around {@link android.provider.Settings.Secure} for easier testing. */
-interface SecureSettings {
+public interface SecureSettings {
 
     void putStringForUser(String name, String value, int userHandle);
 
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 13541b1..009d265 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -58,6 +58,7 @@
 import com.android.settingslib.metadata.ReadWritePermit
 import com.android.settingslib.metadata.SensitivityLevel.Companion.HIGH_SENSITIVITY
 import com.android.settingslib.metadata.SensitivityLevel.Companion.UNKNOWN_SENSITIVITY
+import com.android.settingslib.metadata.getPreferenceIcon
 import com.android.settingslib.preference.PreferenceScreenFactory
 import com.android.settingslib.preference.PreferenceScreenProvider
 import java.util.Locale
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/strings.xml b/packages/SettingsLib/IllustrationPreference/res/values/strings.xml
index 3a8aaf8..03da0dc 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/strings.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/strings.xml
@@ -20,8 +20,6 @@
     <string name="settingslib_action_label_resume">resume</string>
     <!-- Label for an accessibility action that stops an animation [CHAR LIMIT=30] -->
     <string name="settingslib_action_label_pause">pause</string>
-    <!-- Label for an accessibility action that stops an animation [CHAR LIMIT=50] -->
-    <string name="settingslib_state_animation_playing">Animation playing</string>
-    <!-- Label for an accessibility action that stops an animation [CHAR LIMIT=50] -->
-    <string name="settingslib_state_animation_paused">Animation paused</string>
+    <!-- Default content description attached to the illustration if there is no content description. [CHAR LIMIT=NONE] -->
+    <string name="settingslib_illustration_content_description">Animation</string>
 </resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index e818a60..7776070 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -138,8 +138,12 @@
         ImageView backgroundViewTablet =
                 (ImageView) holder.findViewById(R.id.background_view_tablet);
 
-        backgroundView.setVisibility(mIsTablet ? View.GONE : View.VISIBLE);
-        backgroundViewTablet.setVisibility(mIsTablet ? View.VISIBLE : View.GONE);
+        if (backgroundView != null) {
+            backgroundView.setVisibility(mIsTablet ? View.GONE : View.VISIBLE);
+        }
+        if (backgroundViewTablet != null) {
+            backgroundViewTablet.setVisibility(mIsTablet ? View.VISIBLE : View.GONE);
+        }
         if (mIsTablet) {
             backgroundView = backgroundViewTablet;
         }
@@ -183,7 +187,9 @@
         if (mLottieDynamicColor) {
             LottieColorUtils.applyDynamicColors(getContext(), illustrationView);
         }
-        LottieColorUtils.applyMaterialColor(getContext(), illustrationView);
+        if (SettingsThemeHelper.isExpressiveTheme(getContext())) {
+            LottieColorUtils.applyMaterialColor(getContext(), illustrationView);
+        }
 
         if (mOnBindListener != null) {
             mOnBindListener.onBind(illustrationView);
@@ -443,6 +449,10 @@
         illustrationView.setMaxWidth((int) (restrictedMaxHeight * aspectRatio));
     }
 
+    public boolean isAnimatable() {
+        return mIsAnimatable;
+    }
+
     private void startAnimation(Drawable drawable) {
         if (!(drawable instanceof Animatable)) {
             return;
@@ -464,6 +474,10 @@
         if (mIsAnimatable) {
             // TODO(b/397340540): list out pages having illustration without a content description.
             if (TextUtils.isEmpty(mContentDescription)) {
+                // Default content description will be attached if there's no content description.
+                illustrationView.setContentDescription(
+                        getContext().getString(
+                                R.string.settingslib_illustration_content_description));
                 Log.w(TAG, "Illustration should have a content description. preference key = "
                         + getKey());
             }
@@ -483,8 +497,6 @@
     }
 
     private void updateAccessibilityAction(ViewGroup container) {
-        // Setting the state of animation
-        container.setStateDescription(getStateDescriptionForAnimation());
         container.setAccessibilityDelegate(new View.AccessibilityDelegate() {
             @Override
             public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -505,14 +517,6 @@
         }
     }
 
-    private String getStateDescriptionForAnimation() {
-        if (mIsAnimationPaused) {
-            return getContext().getString(R.string.settingslib_state_animation_paused);
-        } else {
-            return getContext().getString(R.string.settingslib_state_animation_playing);
-        }
-    }
-
     private static void startLottieAnimationWith(LottieAnimationView illustrationView,
             Uri imageUri) {
         final InputStream inputStream =
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
index 4421424..e59cc81 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -157,10 +157,6 @@
     /** Applies material colors. */
     public static void applyMaterialColor(@NonNull Context context,
             @NonNull LottieAnimationView lottieAnimationView) {
-        if (!SettingsThemeHelper.isExpressiveTheme(context)) {
-            return;
-        }
-
         for (String key : MATERIAL_COLOR_MAP.keySet()) {
             final int color = context.getColor(MATERIAL_COLOR_MAP.get(key));
             lottieAnimationView.addValueCallback(
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 7bd4b3f..9d4c5c2 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -113,8 +113,6 @@
             mSwitch.setOnCheckedChangeListener(this);
         }
 
-        setChecked(mSwitch.isChecked());
-
         if (attrs != null) {
             final TypedArray a = context.obtainStyledAttributes(attrs,
                     androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/,
@@ -130,8 +128,6 @@
             }
             a.recycle();
         }
-
-        setBackground(mSwitch.isChecked());
     }
 
     @Override
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
index 7f2a610..fcca823 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt
@@ -137,44 +137,6 @@
 
     /** Returns preference intent. */
     fun intent(context: Context): Intent? = null
-
-    /**
-     * Returns the preference title.
-     *
-     * Implement [PreferenceTitleProvider] interface if title content is generated dynamically.
-     */
-    fun getPreferenceTitle(context: Context): CharSequence? =
-        when {
-            title != 0 -> context.getText(title)
-            this is PreferenceTitleProvider -> getTitle(context)
-            else -> null
-        }
-
-    /**
-     * Returns the preference summary.
-     *
-     * Implement [PreferenceSummaryProvider] interface if summary content is generated dynamically
-     * (e.g. summary is provided per preference value).
-     */
-    fun getPreferenceSummary(context: Context): CharSequence? =
-        when {
-            summary != 0 -> context.getText(summary)
-            this is PreferenceSummaryProvider -> getSummary(context)
-            else -> null
-        }
-
-    /**
-     * Returns the preference icon.
-     *
-     * Implement [PreferenceIconProvider] interface if icon is provided dynamically (e.g. icon is
-     * provided based on flag value).
-     */
-    fun getPreferenceIcon(context: Context): Int =
-        when {
-            icon != 0 -> icon
-            this is PreferenceIconProvider -> getIcon(context)
-            else -> 0
-        }
 }
 
 /** Metadata of preference group. */
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Utils.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Utils.kt
new file mode 100644
index 0000000..6d580fb
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Utils.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.metadata
+
+import android.content.Context
+
+/** Returns the preference screen title. */
+fun PreferenceScreenMetadata.getPreferenceScreenTitle(context: Context): CharSequence? =
+    when {
+        screenTitle != 0 -> context.getString(screenTitle)
+        else -> getScreenTitle(context) ?: (this as? PreferenceTitleProvider)?.getTitle(context)
+    }
+
+/** Returns the preference title. */
+fun PreferenceMetadata.getPreferenceTitle(context: Context): CharSequence? =
+    when {
+        title != 0 -> context.getText(title)
+        this is PreferenceTitleProvider -> getTitle(context)
+        else -> null
+    }
+
+/** Returns the preference summary. */
+fun PreferenceMetadata.getPreferenceSummary(context: Context): CharSequence? =
+    when {
+        summary != 0 -> context.getText(summary)
+        this is PreferenceSummaryProvider -> getSummary(context)
+        else -> null
+    }
+
+/** Returns the preference icon. */
+fun PreferenceMetadata.getPreferenceIcon(context: Context): Int =
+    when {
+        icon != 0 -> icon
+        this is PreferenceIconProvider -> getIcon(context)
+        else -> 0
+    }
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
index 59141c9..8896af4 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt
@@ -25,10 +25,16 @@
 import androidx.preference.SeekBarPreference
 import com.android.settingslib.metadata.DiscreteIntValue
 import com.android.settingslib.metadata.DiscreteValue
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
+import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
 import com.android.settingslib.metadata.IntRangeValuePreference
 import com.android.settingslib.metadata.PreferenceAvailabilityProvider
 import com.android.settingslib.metadata.PreferenceMetadata
 import com.android.settingslib.metadata.PreferenceScreenMetadata
+import com.android.settingslib.metadata.getPreferenceIcon
+import com.android.settingslib.metadata.getPreferenceScreenTitle
+import com.android.settingslib.metadata.getPreferenceSummary
+import com.android.settingslib.metadata.getPreferenceTitle
 
 /** Binding of preference widget and preference metadata. */
 interface PreferenceBinding {
@@ -72,9 +78,22 @@
                 preference.icon = null
             }
             val isPreferenceScreen = preference is PreferenceScreen
+            val screenMetadata = this as? PreferenceScreenMetadata
+            // extras
             preference.peekExtras()?.clear()
             extras(context)?.let { preference.extras.putAll(it) }
-            preference.title = getPreferenceTitle(context)
+            if (!isPreferenceScreen && screenMetadata != null) {
+                val extras = preference.extras
+                // Pass the preference key to fragment, so that the fragment could find associated
+                // preference screen registered in PreferenceScreenRegistry
+                extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
+                screenMetadata.arguments?.let { extras.putBundle(EXTRA_BINDING_SCREEN_ARGS, it) }
+            }
+            preference.title =
+                when {
+                    isPreferenceScreen -> screenMetadata?.getPreferenceScreenTitle(context)
+                    else -> getPreferenceTitle(context)
+                }
             if (!isPreferenceScreen) {
                 preference.summary = getPreferenceSummary(context)
             }
@@ -82,12 +101,12 @@
             preference.isVisible =
                 (this as? PreferenceAvailabilityProvider)?.isAvailable(context) != false
             preference.isPersistent = isPersistent(context)
-            // PreferenceRegistry will notify dependency change, so we do not need to set
+            // PreferenceScreenBindingHelper will notify dependency change, so we do not need to set
             // dependency here. This simplifies dependency management and avoid the
             // IllegalStateException when call Preference.setDependency
             preference.dependency = null
             if (!isPreferenceScreen) { // avoid recursive loop when build graph
-                preference.fragment = (this as? PreferenceScreenCreator)?.fragmentClass()?.name
+                preference.fragment = screenMetadata?.fragmentClass()?.name
                 preference.intent = intent(context)
             }
             if (preference is DialogPreference) {
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
index 6287fda..33b614e 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindingFactory.kt
@@ -60,7 +60,6 @@
             ?: when (metadata) {
                 is SwitchPreference -> SwitchPreferenceBinding.INSTANCE
                 is PreferenceCategory -> PreferenceCategoryBinding.INSTANCE
-                is PreferenceScreenCreator -> PreferenceScreenBinding.INSTANCE
                 is MainSwitchPreference -> MainSwitchPreferenceBinding.INSTANCE
                 else -> DefaultPreferenceBinding
             }
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
index 44c93c7..71c46fa 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBindings.kt
@@ -19,45 +19,11 @@
 import android.content.Context
 import androidx.preference.Preference
 import androidx.preference.PreferenceCategory
-import androidx.preference.PreferenceScreen
 import androidx.preference.SwitchPreferenceCompat
 import androidx.preference.TwoStatePreference
-import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
-import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_KEY
 import com.android.settingslib.metadata.PreferenceMetadata
-import com.android.settingslib.metadata.PreferenceScreenMetadata
-import com.android.settingslib.metadata.PreferenceTitleProvider
 import com.android.settingslib.widget.MainSwitchPreference
 
-/** Binding of preference group associated with [PreferenceCategory]. */
-interface PreferenceScreenBinding : PreferenceBinding {
-
-    override fun bind(preference: Preference, metadata: PreferenceMetadata) {
-        super.bind(preference, metadata)
-        val context = preference.context
-        val screenMetadata = metadata as PreferenceScreenMetadata
-        val extras = preference.extras
-        // Pass the preference key to fragment, so that the fragment could find associated
-        // preference screen registered in PreferenceScreenRegistry
-        extras.putString(EXTRA_BINDING_SCREEN_KEY, preference.key)
-        screenMetadata.arguments?.let { extras.putBundle(EXTRA_BINDING_SCREEN_ARGS, it) }
-        if (preference is PreferenceScreen) {
-            val screenTitle = screenMetadata.screenTitle
-            preference.title =
-                if (screenTitle != 0) {
-                    context.getString(screenTitle)
-                } else {
-                    screenMetadata.getScreenTitle(context)
-                        ?: (screenMetadata as? PreferenceTitleProvider)?.getTitle(context)
-                }
-        }
-    }
-
-    companion object {
-        @JvmStatic val INSTANCE = object : PreferenceScreenBinding {}
-    }
-}
-
 /** Binding of preference category associated with [PreferenceCategory]. */
 interface PreferenceCategoryBinding : PreferenceBinding {
 
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
index b41ec95..9d3092d 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-af/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Geaktiveer deur administrateur"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Gedeaktiveer deur administrateur"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Geaktiveer deur Gevorderde Beskerming"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Gedeaktiveer deur Gevorderde Beskerming"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
index 8e94884..9617aca 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-am/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"በአስተዳዳሪ ነቅቷል"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"በአስተዳዳሪ ተሰናክሏል"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"በላቀ ጥበቃ የነቃ"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"በላቀ ጥበቃ የተሰናከለ"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
index 8b2ccdf..581b914 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ar/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"يفعِّل المشرف هذا الإعداد."</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"أوقف المشرف هذا الإعداد"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"تم التفعيل من خلال ميزة \"الحماية المتقدّمة\""</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"تم الإيقاف من خلال ميزة \"الحماية المتقدّمة\""</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
index 03e9e82..5824abd 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-as/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"প্ৰশাসকে সক্ষম কৰিছে"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"প্ৰশাসকে অক্ষম কৰিছে"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"সুৰক্ষা সম্পৰ্কীয় উন্নত সুবিধাটোৱে সক্ষম কৰিছে"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"সুৰক্ষা সম্পৰ্কীয় উন্নত সুবিধাটোৱে অক্ষম কৰিছে"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
index 9844716..f07e054 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-az/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Admin tərəfindən aktiv edildi"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Admin tərəfindən deaktiv edildi"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Qabaqcıl Qoruma tərəfindən aktiv edilib"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Qabaqcıl Qoruma tərəfindən deaktiv edilib"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
index c7b9be2..e09afbf 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-b+sr+Latn/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Administrator je omogućio"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Administrator je onemogućio"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogućila je Napredna zaštita"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogućila je Napredna zaštita"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
index 92ed111..a64734b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-be/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Уключана адміністратарам"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Адключана адміністратарам"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Уключана Палепшанай абаронай"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Адключана Палепшанай абаронай"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
index 57b50c5..ccaa656 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bg/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Активирано от администратора"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Деактивирано от администратора"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Активирано от „Разширена защита“"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Деактивирано от „Разширена защита“"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
index 939ceb8..0a48aa2 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bn/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"অ্যাডমিন চালু করেছেন"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"অ্যাডমিন বন্ধ করেছেন"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"উন্নত সুরক্ষা চালু করেছে"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"উন্নত সুরক্ষা বন্ধ করেছে"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
index 87cd3b8..eebcebf 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-bs/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Omogućio administrator"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Onemogućio administrator"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogućeno je Naprednom zaštitom"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogućeno je Naprednom zaštitom"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
index 34099b6..51e3fa9 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ca/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Activat per l\'administrador"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Desactivat per l\'administrador"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activat per la Protecció avançada"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Desactivat per la Protecció avançada"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
index 82cd56f..a5db609 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-cs/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Zapnuto administrátorem"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Vypnuto administrátorem"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivováno pokročilou ochranou"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktivováno pokročilou ochranou"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
index 7f7ae8b..7f10edf 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-da/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Aktiveret af administratoren"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Deaktiveret af administrator"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktiveret af Avanceret beskyttelse"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktiveret af Avanceret beskyttelse"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
index efaa50e..604593b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-de/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Vom Administrator aktiviert"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Vom Administrator deaktiviert"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Vom erweiterten Sicherheitsprogramm aktiviert"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Vom erweiterten Sicherheitsprogramm deaktiviert"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
index ddde3ece..79b4016 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-el/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Ενεργοποιήθηκε από τον διαχειριστή"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Απενεργοποιήθηκε από τον διαχειριστή"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Ενεργοποιήθηκε από την Ενισχυμένη προστασία"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Απενεργοποιήθηκε από την Ενισχυμένη προστασία"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
index 6a07741..14b9272 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rAU/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
index 6a07741..14b9272 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rCA/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
index 6a07741..14b9272 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rGB/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
index 6a07741..14b9272 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-en-rIN/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Enabled by admin"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Disabled by admin"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Enabled by Advanced Protection"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disabled by Advanced Protection"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
index 8dc15f7..616b568 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-es-rUS/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"El administrador habilitó la opción"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"El administrador inhabilitó la opción"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Habilitado por la Protección avanzada"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Inhabilitado por la Protección avanzada"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
index 7c9864d..351f16c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-es/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Habilitado por el administrador"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Inhabilitado por el administrador"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Habilitado por Protección Avanzada"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Inhabilitado por Protección Avanzada"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
index 5939b58..c59d645 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-et/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Administraatori lubatud"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Administraatori keelatud"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Lubatud täiustatud kaitsega"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Keelatud täiustatud kaitsega"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
index 27bef6e..2a88124 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-eu/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Administratzaileak gaitu egin du"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Administratzaileak desgaitu du"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Babes aurreratua programak gaitu du"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Babes aurreratua programak desgaitu du"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
index 8fb2646..9c39f98 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fa/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"توسط سرپرست فعال شده"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"توسط سرپرست غیرفعال شده"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"فعال‌شده با «محافظت پیشرفته»"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"غیرفعال‌شده با «محافظت پیشرفته»"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
index cd7cbd3..41fef5a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fi/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Järjestelmänvalvojan sallima"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Järjestelmänvalvojan estämä"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Lisäsuojaus on ottanut asetuksen käyttöön"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Lisäsuojaus on poistanut asetuksen käytöstä"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
index da74cf6..9ff1174 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fr-rCA/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Activé par l\'administrateur"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Désactivé par l\'administrateur"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activée par la protection avancée"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Désactivée par la protection avancée"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
index 2215af2..9ff1174 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-fr/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Activé par l\'administrateur"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Désactivé par l\'administrateur"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activé par la Protection Avancée"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Désactivé par la Protection Avancée"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
index 92f33bb..dbf8f7d 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-gl/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Opción activada polo administrador"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Opción desactivada polo administrador"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Opción activada por Protección avanzada"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Opción desactivada por Protección avanzada"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
index 026bdb1..4fc4ab4 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"વ્યવસ્થાપકે ચાલુ કરેલ"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"ઍડમિને બંધ કરેલું"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"અદ્યતન સુરક્ષા દ્વારા ચાલુ કરવામાં આવી છે"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"અદ્યતન સુરક્ષા દ્વારા બંધ કરવામાં આવી છે"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
index 8fc8fd0..6de8438 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hi/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"एडमिन की ओर से चालू किया गया"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"एडमिन ने यह सुविधा बंद की हुई है"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"\'ऐडवांस सुरक्षा\' सेटिंग ने चालू किया है"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"\'ऐडवांस सुरक्षा\' सेटिंग ने बंद किया है"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
index 40605a3b..eebcebf 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hr/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Omogućio administrator"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Onemogućio administrator"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogućila je napredna zaštita"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogućila je napredna zaštita"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
index 59135a4..ecfa2c7 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hu/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"A rendszergazda bekapcsolta"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"A rendszergazda letiltotta"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Engedélyezte a Speciális védelem"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Letiltotta a Speciális védelem"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
index 0221f93..23a2a6b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-hy/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Միացված է ադմինիստրատորի կողմից"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Անջատվել է ադմինիստրատորի կողմից"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Միացվել է Լրացուցիչ պաշտպանության կողմից"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Անջատվել է Լրացուցիչ պաշտպանության կողմից"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
index 6beb4c9..ae8ec82 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-in/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Diaktifkan oleh admin"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Dinonaktifkan oleh admin"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Diaktifkan oleh Perlindungan Lanjutan"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Dinonaktifkan oleh Perlindungan Lanjutan"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
index feb325b..55380b3 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-is/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Gert virkt af kerfisstjóra"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Gert óvirkt af kerfisstjóra"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Virkjað af ítarlegri vernd"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Gert óvirkt af ítarlegri vernd"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
index 6163920..bddf43c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Attivata dall\'amministratore"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Opzione disattivata dall\'amministratore"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Attivata dalla protezione avanzata"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Disattivata dalla protezione avanzata"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
index c342041..007de06 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-iw/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"מופעל על ידי מנהל המכשיר"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"האפשרות הושבתה על ידי האדמין"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ההעדפה הופעלה על ידי ההגנה המתקדמת"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ההעדפה הושבתה על ידי ההגנה המתקדמת"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
index bd386f5..490efd0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ja/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"管理者によって有効にされています"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"管理者により無効にされています"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"高度な保護機能により有効になっています"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"高度な保護機能により無効になっています"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
index a6fde90..5c394b8 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ka/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"ჩართულია ადმინისტრატორის მიერ"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"გათიშულია ადმინისტრატორის მიერ"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ჩართულია დამატებითი დაცვის საშუალებით"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"გათიშულია დამატებითი დაცვის საშუალებით"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
index ed0f95c2..eff7e44 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-kk/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Әкімші қосқан"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Әкімші өшірген"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Күшейтілген қорғаныс параметрі қосып қойды."</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Күшейтілген қорғаныс параметрі өшіріп тастады."</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
index f2f5ab8..5a4f074 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-km/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"បើកដោយ​អ្នកគ្រប់គ្រង"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"បានបិទដោយអ្នកគ្រប់គ្រង"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"បានបើកដោយការ​ការពារ​កម្រិតខ្ពស់"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"បានបិទដោយការ​ការពារ​កម្រិតខ្ពស់"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
index ebc41a52..9b7a0d8 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-kn/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"ನಿರ್ವಾಹಕರು ಸಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ಸುಧಾರಿತ ಸಂರಕ್ಷಣೆ ಮೂಲಕ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ಸುಧಾರಿತ ಸಂರಕ್ಷಣೆ ಮೂಲಕ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
index 552662b..d4f134c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ko/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"관리자가 사용 설정함"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"관리자가 사용 중지함"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"고급 보호 기능으로 사용 설정됨"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"고급 보호 기능으로 사용 중지됨"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
index 375ea19f..a934b51 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ky/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Администратор иштетип койгон"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Администратор өчүрүп койгон"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Өркүндөтүлгөн коргоо тарабынан иштетилди"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Өркүндөтүлгөн коргоо тарабынан өчүрүлдү"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
index 4b311c0..c2d80f2 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lo/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"ເປີດນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບ"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"ຖືກຜູ້ເບິ່ງແຍງລະບົບປິດໄວ້"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ໄດ້ເປີດການນຳໃຊ້ໂດຍການປົກປ້ອງຂັ້ນສູງ"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ໄດ້ປິດການນຳໃຊ້ໂດຍການປົກປ້ອງຂັ້ນສູງ"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
index cbbe923..2e96a0a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lt/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Įgalino administratorius"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Išjungė administratorius"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Įgalino Papildoma apsauga"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Išjungė Papildoma apsauga"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
index a5189aa..1d2bcb0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-lv/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Iespējoja administrators"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Atspējoja administrators"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Iespējota iestatījuma “Papildu aizsardzība” dēļ"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Atspējota iestatījuma “Papildu aizsardzība” dēļ"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
index 993b4ae..1c8f1d1 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mk/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Овозможено од администраторот"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Оневозможено од администраторот"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Овозможено од „Напредна заштита“"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Оневозможено од „Напредна заштита“"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
index 9deeb6a..c4ee224 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ml/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"അഡ്‌മിൻ പ്രവർത്തനക്ഷമമാക്കി"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"അഡ്‌മിൻ പ്രവർത്തനരഹിതമാക്കി"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"വിപുലമായ പരിരക്ഷ പ്രവർത്തനക്ഷമമാക്കി"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"വിപുലമായ പരിരക്ഷ പ്രവർത്തനരഹിതമാക്കി"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
index c9a91de..472c50a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mn/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Админ идэвхжүүлсэн"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Админ цуцалсан"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Дэвшилтэт хамгаалалтаар идэвхжүүлсэн"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Дэвшилтэт хамгаалалтаар идэвхгүй болгосон"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
index ede1242..d01bfc4 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-mr/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"अ‍ॅडमिनने सुरू केलेले"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"अ‍ॅडमिनने बंद केलेले"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"प्रगत संरक्षणाद्वारे सुरू केले आहे"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"प्रगत संरक्षणाद्वारे बंद केले आहे"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
index e8f710a..618ea8c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ms/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Didayakan oleh pentadbir"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Dilumpuhkan oleh pentadbir"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Didayakan oleh Perlindungan Lanjutan"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Dilumpuhkan oleh Perlindungan Lanjutan"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
index 97be99f..e462600 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-my/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"စီမံခန့်ခွဲသူက ဖွင့်ထားသည်"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"စီမံခန့်ခွဲသူက ပိတ်ထားသည်"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"အဆင့်မြင့်ကာကွယ်ရေးက ဖွင့်ထားသည်"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"အဆင့်မြင့်ကာကွယ်ရေးက ပိတ်ထားသည်"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
index 971d1cc..509e70b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-nb/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Aktivert av administratoren"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Deaktivert av administratoren"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivert av Avansert beskyttelse"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktivert av Avansert beskyttelse"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
index 3d2a74e..15bb85c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ne/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"प्रशासकद्वारा सक्षम पारिएको"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"एडमिनले अफ गरेको"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"सुरक्षासम्बन्धी उन्नत सुविधाले अन गरेको छ"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"सुरक्षासम्बन्धी उन्नत सुविधाले अफ गरेको छ"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
index 9830363..a73deaf 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-nl/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Aangezet door beheerder"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Uitgezet door beheerder"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aangezet door Geavanceerde beveiliging"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Uitgezet door Geavanceerde beveiliging"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
index 2dab159..4ce6460 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-or/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"ଆଡମିନଙ୍କ ଦ୍ୱାରା ସକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"ଆଡମିନଙ୍କ ଦ୍ଵାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ଆଡଭାନ୍ସଡ ପ୍ରୋଟେକସନ ଦ୍ୱାରା ସକ୍ଷମ କରାଯାଇଛି"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ଆଡଭାନ୍ସଡ ପ୍ରୋଟେକସନ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
index 12f296a..1a3a133 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pa/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"ਅਡਵਾਂਸ ਸੁਰੱਖਿਆ ਵੱਲੋਂ ਚਾਲੂ ਕੀਤੀ ਗਈ"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ਅਡਵਾਂਸ ਸੁਰੱਖਿਆ ਵੱਲੋਂ ਬੰਦ ਕੀਤੀ ਗਈ"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
index df73947..0523e2b 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pl/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Włączone przez administratora"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Wyłączone przez administratora"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Włączone przez Ochronę zaawansowaną"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Wyłączone przez Ochronę zaawansowaną"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
index a705ba4..908e2fb 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rBR/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Ativado pelo administrador"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Desativado pelo administrador"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Preferência ativada pela Proteção Avançada"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Preferência desativada pela Proteção Avançada"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
index b011183..908e2fb 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt-rPT/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Ativado pelo administrador"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Desativado pelo administrador"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Ativado pela Proteção avançada"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Desativado pela Proteção avançada"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
index a705ba4..908e2fb 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-pt/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Ativado pelo administrador"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Desativado pelo administrador"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Preferência ativada pela Proteção Avançada"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Preferência desativada pela Proteção Avançada"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
index 3eb347a..ad41605 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ro/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Activat de administrator"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Dezactivat de administrator"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Activată de Protecția avansată"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Dezactivată de Protecția avansată"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
index a004a1f..5900644 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ru/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Включено администратором"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Отключено администратором"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Включено Дополнительной защитой"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Отключено Дополнительной защитой"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
index addc8b3..de89710 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-si/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"පරිපාලක විසින් සබල කර ඇත"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"ඔබගේ පරිපාලක විසින් අබල කර ඇත"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"උසස් ආරක්ෂණය මගින් සබල කර ඇත"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"උසස් ආරක්ෂණය මගින් අබල කර ඇත"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
index 0277696..b8bb919 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sk/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Povolené správcom"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Zakázané správcom"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivované rozšírenou ochranou"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Deaktivované rozšírenou ochranou"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
index eb886bc..1d8ee57 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sl/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Omogočil skrbnik"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Onemogočil skrbnik"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Omogočila dodatna zaščita"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Onemogočila dodatna zaščita"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
index 2de5ffe..4ee40bf 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sq/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Aktivizuar nga administratori"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Çaktivizuar nga administratori"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktivizuar nga \"Mbrojtja e përparuar\""</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Çaktivizuar nga \"Mbrojtja e përparuar\""</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
index 94f52a0..9d006a7 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sr/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Администратор је омогућио"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Администратор је онемогућио"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Омогућила је Напредна заштита"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Онемогућила је Напредна заштита"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
index b41c4d8..faea070 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sv/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Aktiverad av administratör"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Inaktiverad av administratören"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Aktiverades av Avancerat skydd"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Inaktiverades av Avancerat skydd"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
index 4d2e0d9..59f511a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-sw/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Imewashwa na msimamizi"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Imezimwa na msimamizi"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Imewashwa kwa kutumia Ulinzi wa Hali ya Juu"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Imezimwa kwa kutumia Ulinzi wa Hali ya Juu"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
index 55b3006..3ef5f77 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ta/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"நிர்வாகி இயக்கியுள்ளார்"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"நிர்வாகி முடக்கியுள்ளார்"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"மேம்பட்ட பாதுகாப்பு அமைப்பால் இயக்கப்பட்டது"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"மேம்பட்ட பாதுகாப்பு அமைப்பால் முடக்கப்பட்டது"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
index fc6d00b..8f17dc5 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-te/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"అడ్మిన్ ఎనేబుల్ చేశారు"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"అడ్మిన్ డిజేబుల్ చేశారు"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"అడ్వాన్స్‌డ్ ప్రొటెక్షన్ ద్వారా ఎనేబుల్ చేయబడింది"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"అడ్వాన్స్‌డ్ ప్రొటెక్షన్ ద్వారా డిజేబుల్ చేయబడింది"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
index 51da8de..80fd0c0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-th/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"เปิดใช้โดยผู้ดูแลระบบ"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"ปิดใช้โดยผู้ดูแลระบบ"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"เปิดใช้โดยการปกป้องขั้นสูง"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"ปิดใช้โดยการปกป้องขั้นสูง"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
index 7fbf0e7..a4a538d 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-tl/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Na-enable ng admin"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Na-disable ng admin"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Na-enable ng Advanced na Proteksyon"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Na-disable ng Advanced na Proteksyon"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
index a529ca5..ac5ed6a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-tr/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Yönetici tarafından etkinleştirildi"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Yönetici tarafından devre dışı bırakıldı"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Gelişmiş Koruma tarafından etkinleştirildi"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Gelişmiş Koruma tarafından devre dışı bırakıldı"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
index fdf4160..32f02a4 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-uk/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Увімкнено адміністратором"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Вимкнено адміністратором"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Увімкнено Додатковим захистом"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Вимкнено Додатковим захистом"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
index b40cb682..f3752d9 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-ur/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"منتظم کی طرف سے فعال کردہ"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"منتظم کی طرف سے غیر فعال کردہ"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"اعلی تحفظ نے فعال کیا ہے"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"اعلی تحفظ نے غیر فعال کیا ہے"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
index 9a27735..e2e9f42 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-uz/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Administrator tomonidan yoqilgan"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Administrator tomonidan faolsizlantirilgan"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Kuchaytirilgan himoya tomonidan yoqilgan"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Kuchaytirilgan himoya tomonidan faolsizlantirilgan"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
index 3436762..dd654b2 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-vi/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Do quản trị viên bật"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Quản trị viên đã vô hiệu hóa chế độ này"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Được bật bởi chế độ Bảo vệ nâng cao"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Bị tắt bởi chế độ Bảo vệ nâng cao"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
index 5c9e302..8fa969e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rCN/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"已被管理员启用"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"已被管理员停用"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"已被“高级保护”功能启用"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"已被“高级保护”功能停用"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
index d4b8833..501f860 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rHK/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"已由管理員啟用"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"已由管理員停用"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"已由進階保護功能啟用"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"已由進階保護功能停用"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
index d4b8833..501f860 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zh-rTW/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"已由管理員啟用"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"已由管理員停用"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"已由進階保護功能啟用"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"已由進階保護功能停用"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
index 2a93d00..86a6acb 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-zu/strings.xml
@@ -19,6 +19,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enabled_by_admin" msgid="6630472777476410137">"Kunikwe amandla umlawuli"</string>
     <string name="disabled_by_admin" msgid="4023569940620832713">"Kukhutshazwe umlawuli"</string>
-    <string name="enabled_by_advanced_protection" msgid="6236917660829422499">"Kunikwe Amandla Ukuvikela Okuthuthukile"</string>
-    <string name="disabled_by_advanced_protection" msgid="369596009193239632">"Kukhutshazwe Ukuvikela Okuthuthukile"</string>
 </resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml
index 7580973..2ffdc93 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml
@@ -21,8 +21,4 @@
     <string name="enabled_by_admin">Enabled by admin</string>
     <!-- Summary for switch preference to denote it is switched off by an admin [CHAR LIMIT=50] -->
     <string name="disabled_by_admin">Disabled by admin</string>
-    <!-- Summary for switch preference to denote it is switched on by Advanced protection [CHAR LIMIT=50] -->
-    <string name="enabled_by_advanced_protection">Enabled by Advanced Protection</string>
-    <!-- Summary for switch preference to denote it is switched off by Advanced protection [CHAR LIMIT=50] -->
-    <string name="disabled_by_advanced_protection">Disabled by Advanced Protection</string>
 </resources>
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 0f6a2a0..1170f1e 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.AdapterView;
 import android.widget.Spinner;
 
@@ -110,7 +111,6 @@
         notifyChanged();
     }
 
-
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
@@ -119,6 +119,18 @@
         spinner.setSelection(mPosition);
         spinner.setOnItemSelectedListener(mOnSelectedListener);
         spinner.setLongClickable(false);
+        spinner.setAccessibilityDelegate(
+                new View.AccessibilityDelegate() {
+                    @Override
+                    public void sendAccessibilityEvent(View host, int eventType) {
+                        if (eventType == AccessibilityEvent.TYPE_VIEW_SELECTED) {
+                            // Ignore the INTERRUPT events TYPE_VIEW_SELECTED or Talkback will speak
+                            // for it while fragment updating.
+                            return;
+                        }
+                        super.sendAccessibilityEvent(host, eventType);
+                    }
+                });
         if (mShouldPerformClick) {
             mShouldPerformClick = false;
             // To show dropdown view.
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
index 686c148..112a69b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles_expressive.xml
@@ -262,4 +262,30 @@
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">wrap_content</item>
     </style>
+
+    <style name="SettingsLibEntityHeaderContent">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_centerHorizontal">true</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:gravity">center_horizontal</item>
+    </style>
+
+    <style name="SettingsLibEntityHeaderIcon">
+        <item name="android:layout_width">@dimen/settingslib_expressive_space_large3</item>
+        <item name="android:layout_height">@dimen/settingslib_expressive_space_large3</item>
+        <item name="android:scaleType">fitCenter</item>
+        <item name="android:antialias">true</item>
+    </style>
+
+    <style name="SettingsLibEntityHeaderTitle">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginTop">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:singleLine">false</item>
+        <item name="android:gravity">center</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:textDirection">locale</item>
+        <item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleLarge.Emphasized</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
index fe8e8b6..6d02c5d 100644
--- a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
+++ b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.widget;
 
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -46,6 +48,9 @@
  */
 public class SliderPreference extends Preference {
     private static final String TAG = "SliderPreference";
+    public static final int HAPTIC_FEEDBACK_MODE_NONE = 0;
+    public static final int HAPTIC_FEEDBACK_MODE_ON_TICKS = 1;
+    public static final int HAPTIC_FEEDBACK_MODE_ON_ENDS = 2;
 
     private final int mTextStartId;
     private final int mTextEndId;
@@ -71,6 +76,8 @@
     private int mMin;
     private int mMax;
     private int mSliderIncrement;
+    private int mHapticFeedbackMode = HAPTIC_FEEDBACK_MODE_NONE;
+    private boolean mTickVisible = false;
     private boolean mAdjustable;
     private boolean mTrackingTouch;
     private CharSequence mSliderContentDescription;
@@ -265,6 +272,7 @@
         }
         if (mSliderIncrement != 0) {
             mSlider.setStepSize(mSliderIncrement);
+            mSlider.setTickVisible(mTickVisible);
         } else {
             mSliderIncrement = (int) (mSlider.getStepSize());
         }
@@ -442,6 +450,29 @@
     }
 
     /**
+     * Sets the haptic feedback mode. HAPTIC_FEEDBACK_MODE_ON_TICKS means to perform haptic feedback
+     * as the {@link Slider} value is updated; HAPTIC_FEEDBACK_MODE_ON_ENDS means to perform haptic
+     * feedback as the {@link Slider} value is equal to the min/max value.
+     *
+     * @param hapticFeedbackMode The haptic feedback mode.
+     */
+    public void setHapticFeedbackMode(int hapticFeedbackMode) {
+        mHapticFeedbackMode = hapticFeedbackMode;
+    }
+
+    /**
+     * Sets whether the tick marks are visible. Only used when the slider is in discrete mode.
+     *
+     * @param tickVisible The visibility of tick marks.
+     */
+    public void setTickVisible(boolean tickVisible) {
+        if (tickVisible != mTickVisible) {
+            mTickVisible = tickVisible;
+            notifyChanged();
+        }
+    }
+
+    /**
      * Gets whether the current {@link Slider} value is displayed to the user.
      *
      * @return Whether the current {@link Slider} value is displayed to the user
@@ -519,7 +550,16 @@
         if (sliderValue != mSliderValue) {
             if (callChangeListener(sliderValue)) {
                 setValueInternal(sliderValue, false);
-                // TODO: mHapticFeedbackMode
+                switch (mHapticFeedbackMode) {
+                    case HAPTIC_FEEDBACK_MODE_ON_TICKS:
+                        slider.performHapticFeedback(CLOCK_TICK);
+                        break;
+                    case HAPTIC_FEEDBACK_MODE_ON_ENDS:
+                        if (mSliderValue == mMax || mSliderValue == mMin) {
+                            slider.performHapticFeedback(CLOCK_TICK);
+                        }
+                        break;
+                }
             } else {
                 slider.setValue(mSliderValue);
             }
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
index a848330..6f37f0c 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
@@ -46,7 +46,7 @@
 ) : BlockedByAdmin {
     override fun getSummary(checked: Boolean?) = when (checked) {
         true -> enterpriseRepository.getAdminSummaryString(
-            advancedProtectionStringId = R.string.enabled_by_advanced_protection,
+            advancedProtectionStringId = com.android.settingslib.R.string.enabled,
             updatableStringId = Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY,
             resId = R.string.enabled_by_admin,
             enforcedAdmin = enforcedAdmin,
@@ -54,7 +54,7 @@
         )
 
         false -> enterpriseRepository.getAdminSummaryString(
-            advancedProtectionStringId = R.string.disabled_by_advanced_protection,
+            advancedProtectionStringId = com.android.settingslib.R.string.disabled,
             updatableStringId = Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY,
             resId = R.string.disabled_by_admin,
             enforcedAdmin = enforcedAdmin,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemInteger.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemInteger.kt
new file mode 100644
index 0000000..db7a640
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemInteger.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.settingsprovider
+
+import android.content.ContentResolver
+import android.content.Context
+import android.provider.Settings
+import com.android.settingslib.spaprivileged.database.contentChangeFlow
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+fun Context.settingsSystemInteger(
+    name: String,
+    defaultValue: Int
+): ReadWriteProperty<Any?, Int> = SettingsSystemIntegerDelegate(this, name, defaultValue)
+
+fun Context.settingsSystemIntegerFlow(name: String, defaultValue: Int): Flow<Int> {
+    val value by settingsSystemInteger(name, defaultValue)
+    return contentChangeFlow(Settings.System.getUriFor(name))
+        .map { value }
+        .distinctUntilChanged()
+        .conflate()
+        .flowOn(Dispatchers.IO)
+}
+
+private class SettingsSystemIntegerDelegate(
+    context: Context,
+    private val name: String,
+    private val defaultValue: Int,
+) : ReadWriteProperty<Any?, Int> {
+
+    private val contentResolver: ContentResolver = context.contentResolver
+
+    override fun getValue(thisRef: Any?, property: KProperty<*>): Int =
+        Settings.System.getInt(contentResolver, name, defaultValue)
+
+    override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
+        Settings.System.putInt(contentResolver, name, value)
+    }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/robotests/Android.bp b/packages/SettingsLib/SpaPrivileged/tests/robotests/Android.bp
new file mode 100644
index 0000000..e3faf73
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/robotests/Android.bp
@@ -0,0 +1,59 @@
+//
+// Copyright (C) 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+    name: "SpaPrivilegedRoboTestStub",
+    defaults: [
+        "SpaPrivilegedLib-defaults",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
+
+android_robolectric_test {
+    name: "SpaPrivilegedRoboTests",
+    srcs: [
+        ":SpaPrivilegedLib_srcs",
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+
+    defaults: [
+        "SpaPrivilegedLib-defaults",
+    ],
+
+    static_libs: [
+        "SpaLibTestUtils",
+        "androidx.test.ext.junit",
+        "androidx.test.runner",
+    ],
+
+    java_resource_dirs: [
+        "config",
+    ],
+
+    instrumentation_for: "SpaPrivilegedRoboTestStub",
+
+    test_options: {
+        timeout: 36000,
+    },
+
+    strict_mode: false,
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/robotests/AndroidManifest.xml b/packages/SettingsLib/SpaPrivileged/tests/robotests/AndroidManifest.xml
new file mode 100644
index 0000000..113852d
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/robotests/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    coreApp="true"
+    package="com.android.settingslib.spaprivileged.settingsprovider">
+
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+    <application/>
+</manifest>
\ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/tests/robotests/config/robolectric.properties b/packages/SettingsLib/SpaPrivileged/tests/robotests/config/robolectric.properties
new file mode 100644
index 0000000..95a24bd
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/robotests/config/robolectric.properties
@@ -0,0 +1,16 @@
+/*
+* Copyright (C) 2025 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+sdk=NEWEST_SDK
\ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/tests/robotests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemIntegerTest.kt b/packages/SettingsLib/SpaPrivileged/tests/robotests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemIntegerTest.kt
new file mode 100644
index 0000000..67e4180
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/robotests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSystemIntegerTest.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.spaprivileged.settingsprovider
+
+import android.content.Context
+import android.provider.Settings
+
+import androidx.test.core.app.ApplicationProvider
+
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.android.settingslib.spa.testutils.toListWithTimeout
+import com.google.common.truth.Truth.assertThat
+
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class SettingsSystemIntegerTest {
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Before
+    fun setUp() {
+        Settings.System.putString(context.contentResolver, TEST_NAME, null)
+    }
+
+    @Test
+    fun setIntValue_returnSameValueByDelegate() {
+        val settingValue = 250
+
+        Settings.System.putInt(context.contentResolver, TEST_NAME, settingValue)
+
+        val value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+
+        assertThat(value).isEqualTo(settingValue)
+    }
+
+    @Test
+    fun setZero_returnZeroByDelegate() {
+        val settingValue = 0
+        Settings.System.putInt(context.contentResolver, TEST_NAME, settingValue)
+
+        val value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+
+        assertThat(value).isEqualTo(settingValue)
+    }
+
+    @Test
+    fun setValueByDelegate_getValueFromSettings() {
+        val settingsValue = 5
+        var value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+
+        value = settingsValue
+
+        assertThat(Settings.System.getInt(context.contentResolver, TEST_NAME, TEST_SETTING_DEFAULT_VALUE)).isEqualTo(settingsValue)
+    }
+
+    @Test
+    fun setZeroByDelegate_getZeroFromSettings() {
+        val settingValue = 0
+        var value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+
+        value = settingValue
+
+        assertThat(Settings.System.getInt(context.contentResolver, TEST_NAME, TEST_SETTING_DEFAULT_VALUE)).isEqualTo(settingValue)
+    }
+
+    @Test
+    fun setValueByDelegate_returnValueFromsettingsSystemIntegerFlow() = runBlocking<Unit> {
+        val settingValue = 7
+        var value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+        value = settingValue
+
+        val flow = context.settingsSystemIntegerFlow(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+
+        assertThat(flow.firstWithTimeoutOrNull()).isEqualTo(settingValue)
+    }
+
+    @Test
+    fun setValueByDelegateTwice_collectAfterValueChanged_onlyKeepLatest() = runBlocking<Unit> {
+        val firstSettingValue = 5
+        val secondSettingValue = 10
+
+        var value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+        value = firstSettingValue
+
+        val flow = context.settingsSystemIntegerFlow(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+        value = secondSettingValue
+
+        assertThat(flow.firstWithTimeoutOrNull()).isEqualTo(value)
+    }
+
+    @Test
+    fun settingsSystemIntegerFlow_collectBeforeValueChanged_getBoth() = runBlocking<Unit> {
+        val firstSettingValue = 12
+        val secondSettingValue = 17
+        val delay_ms = 100L
+
+        var value by context.settingsSystemInteger(TEST_NAME, TEST_SETTING_DEFAULT_VALUE)
+        value = firstSettingValue
+
+
+        val listDeferred = async {
+            context.settingsSystemIntegerFlow(TEST_NAME, TEST_SETTING_DEFAULT_VALUE).toListWithTimeout()
+        }
+
+        delay(delay_ms)
+        value = secondSettingValue
+
+        assertThat(listDeferred.await())
+            .containsAtLeast(firstSettingValue, secondSettingValue).inOrder()
+    }
+
+    private companion object {
+        const val TEST_NAME = "test_system_integer_delegate"
+        const val TEST_SETTING_DEFAULT_VALUE = -1
+    }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt
index f3245c9..189bf36 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt
@@ -77,8 +77,8 @@
             if (RestrictedLockUtilsInternal.isPolicyEnforcedByAdvancedProtection(context,
                     RESTRICTION, userId)) {
                 return when (advancedProtectionStringId) {
-                    R.string.enabled_by_advanced_protection -> ENABLED_BY_ADVANCED_PROTECTION
-                    R.string.disabled_by_advanced_protection -> DISABLED_BY_ADVANCED_PROTECTION
+                    com.android.settingslib.R.string.enabled -> ENABLED
+                    com.android.settingslib.R.string.disabled -> DISABLED
                     else -> ""
                 }
             }
@@ -129,7 +129,7 @@
 
         val summary = blockedByAdmin.getSummary(true)
 
-        assertThat(summary).isEqualTo(ENABLED_BY_ADVANCED_PROTECTION)
+        assertThat(summary).isEqualTo(ENABLED)
     }
 
     @RequiresFlagsEnabled(Flags.FLAG_AAPM_API)
@@ -148,7 +148,7 @@
 
         val summary = blockedByAdmin.getSummary(false)
 
-        assertThat(summary).isEqualTo(DISABLED_BY_ADVANCED_PROTECTION)
+        assertThat(summary).isEqualTo(DISABLED)
     }
 
     @RequiresFlagsEnabled(Flags.FLAG_AAPM_API)
@@ -202,7 +202,7 @@
 
         const val ENABLED_BY_ADMIN = "Enabled by admin"
         const val DISABLED_BY_ADMIN = "Disabled by admin"
-        const val ENABLED_BY_ADVANCED_PROTECTION = "Enabled by advanced protection"
-        const val DISABLED_BY_ADVANCED_PROTECTION = "Disabled by advanced protection"
+        const val ENABLED = "Enabled"
+        const val DISABLED = "Disabled"
     }
 }
diff --git a/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
index 79085af..308b285 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/unit/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
@@ -175,13 +175,7 @@
 
         val summary = getSummary(listModel)
 
-        assertThat(summary)
-            .isEqualTo(
-                context.getString(
-                    com.android.settingslib.widget.restricted.R.string
-                        .disabled_by_advanced_protection
-                )
-            )
+        assertThat(summary).isEqualTo(context.getString(com.android.settingslib.R.string.disabled))
     }
 
     @RequiresFlagsEnabled(Flags.FLAG_AAPM_API)
diff --git a/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml b/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml
new file mode 100644
index 0000000..6b534aa
--- /dev/null
+++ b/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2025 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="34dp"
+    android:height="42dp"
+    android:viewportWidth="34"
+    android:viewportHeight="42">
+    <path
+        android:pathData="M0.856,17.569C0.887,19.083 1.004,20.593 1.206,22.094C2.166,28.584 5.804,35.937 15.774,41.089C16.171,41.293 16.61,41.4 17.056,41.4C17.503,41.4 17.942,41.293 18.339,41.089C28.309,35.936 31.947,28.583 32.907,22.093C33.109,20.593 33.226,19.083 33.256,17.569V8.605C33.257,7.919 33.046,7.25 32.652,6.688C32.259,6.127 31.703,5.7 31.059,5.467L18.191,0.8C17.458,0.534 16.655,0.534 15.922,0.8L3.054,5.467C2.41,5.7 1.854,6.127 1.461,6.688C1.067,7.25 0.856,7.919 0.856,8.605V17.569Z"
+        android:fillColor="#D1C2CB"/>
+    <path
+        android:pathData="M15.067,24.333V10.733H18.933V24.333H15.067ZM15.067,31.267V27.433H18.933V31.267H15.067Z"
+        android:fillColor="@color/settingslib_materialColorSurfaceContainerLowest"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml b/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
index 54860d4..deda258 100644
--- a/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
+++ b/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
@@ -21,6 +21,7 @@
             <enum name="low" value="1"/>
             <enum name="medium" value="2"/>
             <enum name="high" value="3"/>
+            <enum name="off" value="4"/>
         </attr>
         <attr name="buttonLevel" format="enum">
             <enum name="generic" value="0"/>
diff --git a/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml b/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
index 19181dd..abc458b 100644
--- a/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
+++ b/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
@@ -22,4 +22,5 @@
     <color name="settingslib_expressive_color_status_level_medium">#FCBD00</color>
     <!-- static palette red50 -->
     <color name="settingslib_expressive_color_status_level_high">#DB372D</color>
+    <color name="settingslib_expressive_color_status_level_off">#D1C2CB</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
index 1f8cfb5..eda281c 100644
--- a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
+++ b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
@@ -40,7 +40,8 @@
         GENERIC,
         LOW,
         MEDIUM,
-        HIGH
+        HIGH,
+        OFF
     }
     var iconLevel: BannerStatus = BannerStatus.GENERIC
         set(value) {
@@ -87,6 +88,7 @@
         1 -> BannerStatus.LOW
         2 -> BannerStatus.MEDIUM
         3 -> BannerStatus.HIGH
+        4 -> BannerStatus.OFF
         else -> BannerStatus.GENERIC
     }
 
@@ -104,7 +106,10 @@
         }
 
         (holder.findViewById(R.id.status_banner_button) as? MaterialButton)?.apply {
-            setBackgroundColor(getBackgroundColor(buttonLevel))
+            setBackgroundColor(
+                if (buttonLevel == BannerStatus.OFF) getBackgroundColor(BannerStatus.GENERIC)
+                else getBackgroundColor(buttonLevel)
+            )
             text = buttonText
             setOnClickListener(listener)
             visibility = if (listener != null) View.VISIBLE else View.GONE
@@ -143,6 +148,11 @@
                 R.color.settingslib_expressive_color_status_level_high
             )
 
+            BannerStatus.OFF -> ContextCompat.getColor(
+                context,
+                R.color.settingslib_expressive_color_status_level_off
+            )
+
             else -> ContextCompat.getColor(
                 context,
                 com.android.settingslib.widget.theme.R.color.settingslib_materialColorPrimary
@@ -167,6 +177,11 @@
                 R.drawable.settingslib_expressive_icon_status_level_high
             )
 
+            BannerStatus.OFF -> ContextCompat.getDrawable(
+                context,
+                R.drawable.settingslib_expressive_icon_status_level_off
+            )
+
             else -> null
         }
     }
@@ -188,6 +203,7 @@
                 R.drawable.settingslib_expressive_background_level_high
             )
 
+            // GENERIC and OFF are using the same background drawable.
             else -> ContextCompat.getDrawable(
                 context,
                 R.drawable.settingslib_expressive_background_generic
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 418a76f..349d13a 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -37,13 +37,6 @@
 }
 
 flag {
-    name: "enable_hide_exclusively_managed_bluetooth_device"
-    namespace: "dck_framework"
-    description: "Hide exclusively managed Bluetooth devices in BT settings menu."
-    bug: "324475542"
-}
-
-flag {
     name: "enable_set_preferred_transport_for_le_audio_device"
     namespace: "bluetooth"
     description: "Enable setting preferred transport for Le Audio device"
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index dcad735..58ddc72 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Beheer deur Beperkte Instellings"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Onbeskikbaar tydens oproepe"</string>
     <string name="disabled" msgid="8017887509554714950">"Gedeaktiveer"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Toegelaat"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Nie toegelaat nie"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Installeer onbekende apps"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index dc6bd0f..3844a4b 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"በተገደበ ቅንብር ቁጥጥር የሚደረግበት"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"በጥሪዎች ጊዜ አይገኝም"</string>
     <string name="disabled" msgid="8017887509554714950">"ቦዝኗል"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"ይፈቀዳል"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"አይፈቀድም"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"ያልታወቁ መተግበሪያዎችን ይጫኑ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 5fbab96..1344a25 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"يتحكّم فيه إعداد محظور"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"غير متاح أثناء المكالمات"</string>
     <string name="disabled" msgid="8017887509554714950">"غير مفعّل"</string>
+    <string name="enabled" msgid="3997122818554810678">"مفعّلة"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"تطبيق مسموح به"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"تطبيق غير مسموح به"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"تثبيت التطبيقات غير المعروفة"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index f95a66b..56f73df 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"প্ৰতিবন্ধিত ছেটিঙৰ দ্বাৰা নিয়ন্ত্ৰিত"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"কল চলি থকাৰ সময়ত উপলব্ধ নহয়"</string>
     <string name="disabled" msgid="8017887509554714950">"নিষ্ক্ৰিয়"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"অনুমতি দিয়া হৈছে"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"অনুমতি দিয়া হোৱা নাই"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"অজ্ঞাত এপ্ ইনষ্টল কৰক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 5e47ff9..878d897 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Məhdudlaşdırılmış Ayar ilə nəzarət edilir"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Zənglər zamanı əlçatan deyil"</string>
     <string name="disabled" msgid="8017887509554714950">"Deaktiv"</string>
+    <string name="enabled" msgid="3997122818554810678">"Aktiv"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"İcazə verilib"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"İcazə verilməyib"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Tanınmayan tətbiqlərin quraşdırılması"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index cb62399..97873e5 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolišu ograničena podešavanja"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nedostupno tokom poziva"</string>
     <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Dozvoljeno"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Nije dozvoljeno"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instaliranje nepoznatih aplikacija"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index ef66fb3..78a5eaf 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Пад кіраваннем Абмежаванага наладжвання"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недаступна падчас выклікаў"</string>
     <string name="disabled" msgid="8017887509554714950">"Адключанае"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Дазволена"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Забаронена"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Усталёўка невядомых праграм"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 69d6866..f49b1d2 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Управлява се чрез ограничена настройка"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Заето по време на обаждания"</string>
     <string name="disabled" msgid="8017887509554714950">"Деактивирано"</string>
+    <string name="enabled" msgid="3997122818554810678">"Активирано"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Има разрешение"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Няма разрешение"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Инст. на неизвестни прилож."</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 7e8e3fe..79e4ed9 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"এটি বিধিনিষেধ সেটিং থেকে নিয়ন্ত্রণ করা হয়"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"কল চলাকালীন উপলভ্য হবে না"</string>
     <string name="disabled" msgid="8017887509554714950">"অক্ষম হয়েছে"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"অনুমোদিত"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"অনুমোদিত নয়"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"অজানা অ্যাপ ইনস্টল করা"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index d0b15d9e..d06ce34 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolira ograničena postavka"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nije dostupno tokom poziva"</string>
     <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Dozvoljeno"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Nije dozvoljeno"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instaliranje nepoznatih aplikacija"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 283182c..776caae 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -507,7 +507,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"Hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> segons l\'ús que en facis"</string>
     <string name="power_discharge_by" msgid="4113180890060388350">"Hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="92545648425937000">"Hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
-    <string name="power_discharge_by_only_short" msgid="5883041507426914446">"fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"És possible que la bateria s\'esgoti a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlat per l\'opció de configuració restringida"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"No està disponible durant les trucades"</string>
     <string name="disabled" msgid="8017887509554714950">"Desactivat"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Amb permís"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Sense permís"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instal·la aplicacions desconegudes"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index bc00d47..02722fd 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Spravováno omezeným nastavením"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Při volání nedostupné"</string>
     <string name="disabled" msgid="8017887509554714950">"Deaktivováno"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Povoleno"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Není povoleno"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalace neznámých aplikací"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index ef14483..4d37578 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Styres af en begrænset indstilling"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Kan ikke bruges under opkald"</string>
     <string name="disabled" msgid="8017887509554714950">"Deaktiveret"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Tilladt"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Ikke tilladt"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Installer ukendte apps"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index e7ffaa4..3e57002 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Gesteuert durch eingeschränkte Einstellung"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Während Anrufen nicht verfügbar"</string>
     <string name="disabled" msgid="8017887509554714950">"Deaktiviert"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Zugelassen"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Nicht zugelassen"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Installieren unbekannter Apps"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 2fc0ff2..be11356 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ελέγχεται από τη Ρύθμιση με περιορισμό"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Μη διαθέσιμη κατά τη διάρκεια κλήσεων"</string>
     <string name="disabled" msgid="8017887509554714950">"Απενεργοποιημένη"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Επιτρέπεται"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Δεν επιτρέπεται"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Εγκατ. άγνωστων εφ."</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 34d6bf7..8c819ac 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Unavailable during calls"</string>
     <string name="disabled" msgid="8017887509554714950">"Disabled"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Allowed"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Not allowed"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Install unknown apps"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 1203437..2faf2fe 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by Restricted Setting"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Unavailable during calls"</string>
     <string name="disabled" msgid="8017887509554714950">"Disabled"</string>
+    <string name="enabled" msgid="3997122818554810678">"Enabled"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Allowed"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Not allowed"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Install unknown apps"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 34d6bf7..8c819ac 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Unavailable during calls"</string>
     <string name="disabled" msgid="8017887509554714950">"Disabled"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Allowed"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Not allowed"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Install unknown apps"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 34d6bf7..8c819ac 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Unavailable during calls"</string>
     <string name="disabled" msgid="8017887509554714950">"Disabled"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Allowed"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Not allowed"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Install unknown apps"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index ca17178..0e83356 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Función controlada por configuración restringida"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"No disponible durante llamadas"</string>
     <string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string>
+    <string name="enabled" msgid="3997122818554810678">"Habilitada"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Con permiso"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"No permitida"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalar apps desconocidas"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 22e8b11..66d81d2 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlado por ajustes restringidos"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"No disponible durante las llamadas"</string>
     <string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Autorizadas"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"No autorizadas"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalar aplicaciones desconocidas"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 0de0197..59e1e4b 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Haldavad piiranguga seaded"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Pole kõnede ajal saadaval"</string>
     <string name="disabled" msgid="8017887509554714950">"Keelatud"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Lubatud"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Pole lubatud"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Tundmatute rakenduste installimine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 7a8bf56..6fd7e24 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ezarpen mugatuak kontrolatzen du"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ez dago erabilgarri deiak egin bitartean"</string>
     <string name="disabled" msgid="8017887509554714950">"Desgaituta"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Baimenduta"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Baimendu gabe"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalatu aplikazio ezezagunak"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 9ba2f35..483e76a 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"با تنظیم «حالت محدود» کنترل می‌شود"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"درطول تماس دردسترس نیست"</string>
     <string name="disabled" msgid="8017887509554714950">"غیر فعال شد"</string>
+    <string name="enabled" msgid="3997122818554810678">"فعال"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"مجاز بودن"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"مجاز نبودن"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"نصب برنامه‌های ناشناس"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index d1cc76c..f6af16e 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -246,7 +246,7 @@
     <item msgid="6946761421234586000">"400 %"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Valitse profiili"</string>
-    <string name="category_personal" msgid="6236798763159385225">"Henkilökohtainen"</string>
+    <string name="category_personal" msgid="6236798763159385225">"Omat"</string>
     <string name="category_work" msgid="4014193632325996115">"Työ"</string>
     <string name="category_private" msgid="4244892185452788977">"Yksityinen"</string>
     <string name="category_clone" msgid="1554511758987195974">"Klooni"</string>
@@ -539,9 +539,11 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Rajoitettujen asetusten mukaisesti"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ei käytettävissä puhelujen aikana"</string>
     <string name="disabled" msgid="8017887509554714950">"Pois päältä"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Sallittu"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Ei sallittu"</string>
-    <string name="install_other_apps" msgid="3232595082023199454">"Tuntemattomien sovellusten asentaminen"</string>
+    <string name="install_other_apps" msgid="3232595082023199454">"Asenna tuntemattomia sovelluksia"</string>
     <string name="home" msgid="973834627243661438">"Asetusten etusivu"</string>
   <string-array name="battery_labels">
     <item msgid="7878690469765357158">"0 %"</item>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index d833892..a5b212b 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Contrôlé par les paramètres restreints"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponible pendant les appels"</string>
     <string name="disabled" msgid="8017887509554714950">"Désactivée"</string>
+    <string name="enabled" msgid="3997122818554810678">"Activé"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Autorisée"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Non autorisée"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Installer les applis inconnues"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 07849e0..4cc8a9a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Contrôlé par les paramètres restreints"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponible pendant les appels"</string>
     <string name="disabled" msgid="8017887509554714950">"Désactivée"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Autorisé"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Non autorisé"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Installation d\'applis inconnues"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index b6eacfc..352b608 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Baixo o control de opcións restrinxidas"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Non dispoñible durante as chamadas"</string>
     <string name="disabled" msgid="8017887509554714950">"Desactivada"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Permiso concedido"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Permiso non concedido"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalar aplicacións descoñecidas"</string>
@@ -608,9 +610,9 @@
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
     <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string>
-    <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
-    <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analóxica"</string>
-    <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
+    <string name="media_transfer_digital_line_name" msgid="312091711951124301">"Conexión S/PDIF"</string>
+    <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Conexión analóxica"</string>
+    <string name="media_transfer_aux_line_name" msgid="894135835967856689">"Conexión AUX"</string>
     <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Non se pode reproducir contido neste dispositivo"</string>
     <string name="media_output_status_require_premium" msgid="8411255800047014822">"Cambia a conta a un plan superior para facer a modificación"</string>
     <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Non se poden reproducir as descargas neste dispositivo"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 57f8982..aa27f2a 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"પ્રતિબંધિત સેટિંગ દ્વારા નિયંત્રિત"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"કૉલ દરમિયાન અનુપલબ્ધ"</string>
     <string name="disabled" msgid="8017887509554714950">"બંધ કરી"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"મંજૂરી છે"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"મંજૂરી નથી"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"અજાણી ઍપ ઇન્સ્ટૉલ કરો"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 02bc92b..7c4d64f 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"इसे पाबंदी मोड वाली सेटिंग से कंट्रोल किया जाता है"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"कॉल के दौरान उपलब्ध नहीं है"</string>
     <string name="disabled" msgid="8017887509554714950">"बंद किया गया"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"अनुमति है"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"अनुमति नहीं है"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"अनजान ऐप्लिकेशन इंस्टॉल करने की अनुमति"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 15eb471..ba5650f 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolira ograničena postavka"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nije dostupno tijekom poziva"</string>
     <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Dopušteno"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Nije dopušteno"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalacija nepoznatih aplikacija"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 08ae0f8..faeec8d 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Korlátozott beállítás vezérli"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nem áll rendelkezésre hívások közben"</string>
     <string name="disabled" msgid="8017887509554714950">"Letiltva"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Engedélyezett"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Nem engedélyezett"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Ismeretlen alkalmazások telepítése"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 3471013..eedd262 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Կառավարվում է սահմանափակ ռեժիմի կարգավորումներով"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Զանգի ընթացքում հասանելի չէ"</string>
     <string name="disabled" msgid="8017887509554714950">"Կասեցված է"</string>
+    <string name="enabled" msgid="3997122818554810678">"Միացված է"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Թույլատրված է"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Արգելված"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Անհայտ հավելվածների տեղադրում"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 85109fc..7af3de0 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Dikontrol oleh Setelan Terbatas"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Tidak tersedia selama panggilan berlangsung"</string>
     <string name="disabled" msgid="8017887509554714950">"Dinonaktifkan"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Diizinkan"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Tidak diizinkan"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instal aplikasi tidak dikenal"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 3041151..e6ef20f 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Stýrt af takmarkaði stillingu"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ekki í boði á meðan á símtölum stendur"</string>
     <string name="disabled" msgid="8017887509554714950">"Óvirkt"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Heimilað"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Ekki heimilað"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Setja upp óþekkt forrit"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index c42cab3..24e7a4c 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Gestita tramite impostazioni con restrizioni"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Non disponibile durante le chiamate"</string>
     <string name="disabled" msgid="8017887509554714950">"Disattivato"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Autorizzazione concessa"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Autorizzazione non concessa"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Installa app sconosciute"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index c39521f..82d9043 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"בשליטה של הגדרה מוגבלת"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ההעדפה הזו לא זמינה במהלך שיחות"</string>
     <string name="disabled" msgid="8017887509554714950">"מושבת"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"מורשה"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"לא מורשה"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"התקנת אפליקציות לא מוכרות"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index bb997ff..adf5b71 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"制限付き設定によって管理されています"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"通話中は利用できません"</string>
     <string name="disabled" msgid="8017887509554714950">"無効"</string>
+    <string name="enabled" msgid="3997122818554810678">"有効"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"許可"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"許可しない"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"不明なアプリのインストール"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 91e8889..e9368aa 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"კონტროლდება შეზღუდული რეჟიმის პარამეტრით"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"მიუწვდომელია ზარების განხორციელებისას"</string>
     <string name="disabled" msgid="8017887509554714950">"გამორთული"</string>
+    <string name="enabled" msgid="3997122818554810678">"ჩართულია"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"დაშვებულია"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"დაუშვებელია"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"უცნობი აპების ინსტალაცია"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 25c92de..16b7c22 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Шектелген параметрлер арқылы басқарылады."</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Қоңырау шалу кезінде қолжетімді емес."</string>
     <string name="disabled" msgid="8017887509554714950">"Өшірілген"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Рұқсат берілген"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Рұқсат етілмеген"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Белгісіз қолданбаларды орнату"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 9dadee1..b183dc9 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"គ្រប់គ្រងដោយការកំណត់ដែលបានរឹតបន្តឹង"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"មិនអាច​ប្រើបានទេ​អំឡុងពេល​ហៅទូរសព្ទ"</string>
     <string name="disabled" msgid="8017887509554714950">"បិទ"</string>
+    <string name="enabled" msgid="3997122818554810678">"បាន​បើក"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"បាន​អនុញ្ញាត"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"មិន​បានអនុញ្ញាត​"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"ដំឡើងកម្មវិធីដែលមិនស្គាល់"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 72056bb..a4e01d9 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ನಿರ್ಬಂಧಿಸಲಾದ ಸೆಟ್ಟಿಂಗ್ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗುತ್ತದೆ"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ಕರೆಗಳ ಸಮಯದಲ್ಲಿ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="disabled" msgid="8017887509554714950">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="enabled" msgid="3997122818554810678">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"ಅನುಮತಿಸಲಾಗಿದೆ"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"ಅನುಮತಿ ಇಲ್ಲ"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"ಅಪರಿಚಿತ ಆ್ಯಪ್‍‍ಗಳನ್ನು ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 3b965e5..0488543 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"제한된 설정으로 제어됨"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"통화 중에는 사용할 수 없습니다."</string>
     <string name="disabled" msgid="8017887509554714950">"사용 안함"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"허용됨"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"허용되지 않음"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"알 수 없는 앱 설치"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 3eee360..b048837 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Чектелген параметр аркылуу көзөмөлдөнөт"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Сүйлөшүп жаткан учурда жеткиликсиз"</string>
     <string name="disabled" msgid="8017887509554714950">"Өчүрүлгөн"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Уруксат берилген"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Тыюу салынган"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Белгисиз колдонмолорду орнотуу"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 8044cce..ebba474 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ຄວບຄຸມໂດຍການຕັ້ງຄ່າທີ່ຈຳກັດໄວ້"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ບໍ່ສາມາດໃຊ້ໄດ້ລະຫວ່າງການໂທ"</string>
     <string name="disabled" msgid="8017887509554714950">"ປິດການນຳໃຊ້"</string>
+    <string name="enabled" msgid="3997122818554810678">"ເປີດການນຳໃຊ້ຢູ່"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"ອະນຸຍາດແລ້ວ"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"ບໍ່ອະນຸຍາດ"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"ຕິດຕັ້ງແອັບທີ່ບໍ່ຮູ້ຈັກ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index aec236a..058ca60 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Valdoma pagal apribotą nustatymą"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nepasiekiama per skambučius"</string>
     <string name="disabled" msgid="8017887509554714950">"Neleidžiama"</string>
+    <string name="enabled" msgid="3997122818554810678">"Įgalinta"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Leidžiama"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Neleidžiama"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Nežinomų programų diegimas"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 5716f8f..cdca19b 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolē ierobežots iestatījums"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ierīce nav pieejama zvanu laikā"</string>
     <string name="disabled" msgid="8017887509554714950">"Atspējots"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Atļauts"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Nav atļauts"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Nezināmu lietotņu instalēšana"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index bc6409d..e51ca04 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролирано со ограничени поставки"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недостапно при повици"</string>
     <string name="disabled" msgid="8017887509554714950">"Оневозможено"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Со дозвола"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Без дозвола"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Инсталирање непознати апликации"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index bcadd7d..54b6ff8 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"നിയന്ത്രിത ക്രമീകരണം ഉപയോഗിച്ച് നിയന്ത്രിക്കുന്നത്"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"കോളുകൾ ചെയ്യുമ്പോൾ ലഭ്യമല്ല"</string>
     <string name="disabled" msgid="8017887509554714950">"പ്രവർത്തനരഹിതമാക്കി"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"അനുവദനീയം"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"അനുവദിച്ചിട്ടില്ല"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"പരിചയമില്ലാത്ത ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 2b45e85..ed42362 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Хязгаарлагдсан тохиргоогоор хянадаг"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Дуудлагын үер боломжгүй"</string>
     <string name="disabled" msgid="8017887509554714950">"Идэвхгүйжүүлсэн"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Зөвшөөрсөн"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Зөвшөөрөөгүй"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Тодорхойгүй апп суулгах"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 8c77b08..6e7cd02 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"प्रतिबंधित केलेल्या सेटिंग द्वारे नियंत्रित"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"कॉल दरम्‍यान उपलब्ध नाही"</string>
     <string name="disabled" msgid="8017887509554714950">"अक्षम"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"अनुमती आहे"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"अनुमती नाही"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"अज्ञात अ‍ॅप्स इंस्टॉल करा"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index a2f9562..384635a 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Dikawal oleh Tetapan Terhad"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Tidak tersedia semasa panggilan berlangsung"</string>
     <string name="disabled" msgid="8017887509554714950">"Dilumpuhkan"</string>
+    <string name="enabled" msgid="3997122818554810678">"Didayakan"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Dibenarkan"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Tidak dibenarkan"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Pasang apl yang tidak diketahui"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index b0d86af..fc26db0 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ကန့်သတ်ဆက်တင်ဖြင့် ထိန်းချုပ်ထားသည်"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ဖုန်းခေါ်ဆိုနေချိန်တွင် မရနိုင်ပါ"</string>
     <string name="disabled" msgid="8017887509554714950">"ပိတ်ထားပြီး"</string>
+    <string name="enabled" msgid="3997122818554810678">"ဖွင့်ထားသည်"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"ခွင့်ပြုထားသည်"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"ခွင့်မပြုပါ"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"အမည်မသိအက်ပ် ထည့်သွင်းခြင်း"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 755df51..c8fc415 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollert av en begrenset innstilling"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Utilgjengelig under samtaler"</string>
     <string name="disabled" msgid="8017887509554714950">"Deaktivert"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Tillatt"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Ikke tillatt"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Installer ukjente apper"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 0a9ac17..5f09635 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"प्रतिबन्धित सेटिङले नियन्त्रण गरेको"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"कल चलिरहेका बेला उपलब्ध छैन"</string>
     <string name="disabled" msgid="8017887509554714950">"असक्षम पारियो"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"अनुमति छ"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"अनुमति छैन"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"अज्ञात एप इन्स्टल गर्ने अनुमति"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index f71f3ab..78dfdce 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Beheerd door beperkte instelling"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Niet beschikbaar tijdens gesprekken"</string>
     <string name="disabled" msgid="8017887509554714950">"Uitgezet"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Toegestaan"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Niet toegestaan"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Onbekende apps installeren"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index f30ae3d..d6e2631 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ପ୍ରତିବନ୍ଧିତ ସେଟିଂ ଦ୍ୱାରା ନିୟନ୍ତ୍ରଣ କରାଯାଇଛି"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"କଲ କରିବାବେଳେ ଉପଲବ୍ଧ ନଥାଏ"</string>
     <string name="disabled" msgid="8017887509554714950">"ଅକ୍ଷମ ହୋଇଛି"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"ଅନୁମତି ଦିଆଯାଇଛି"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"ଅନୁମତି ନାହିଁ"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"ଅଜଣା ଆପ ଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 2c2eab2..ab5f4af 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ਪ੍ਰਤਿਬੰਧਿਤ ਸੈਟਿੰਗ ਰਾਹੀਂ ਕੰਟਰੋਲ ਕੀਤੀ ਜਾਂਦੀ ਹੈ"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ਕਾਲਾਂ ਦੌਰਾਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
     <string name="disabled" msgid="8017887509554714950">"ਅਯੋਗ ਬਣਾਇਆ"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"ਮਨਜ਼ੂਰਸ਼ੁਦਾ"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"ਗੈਰ-ਮਨਜ਼ੂਰਸ਼ੁਦਾ"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"ਅਗਿਆਤ ਐਪਾਂ ਦੀ ਸਥਾਪਨਾ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index aed9b06..4621424 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Obowiązują ustawienia z ograniczonym dostępem"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Niedostępne w trakcie połączeń"</string>
     <string name="disabled" msgid="8017887509554714950">"Wyłączona"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Dozwolone"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Niedozwolone"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalowanie nieznanych aplikacji"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index e16e355..482f479 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlada pelas configurações restritas"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponível durante ligações"</string>
     <string name="disabled" msgid="8017887509554714950">"Desativado"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Permitido"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Não permitido"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalar apps desconhecidos"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 11f410f..d6ac533 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlado por uma definição restrita"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponível durante as chamadas"</string>
     <string name="disabled" msgid="8017887509554714950">"Desativada"</string>
+    <string name="enabled" msgid="3997122818554810678">"Ativado"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Autorizada"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Não autorizada"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalar apps desconhecidas"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index e16e355..482f479 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlada pelas configurações restritas"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponível durante ligações"</string>
     <string name="disabled" msgid="8017887509554714950">"Desativado"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Permitido"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Não permitido"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalar apps desconhecidos"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 83a6e68..757fee9 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlată de setarea restricționată"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponibil în timpul apelurilor"</string>
     <string name="disabled" msgid="8017887509554714950">"Dezactivată"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Permise"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Nepermise"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalarea aplicațiilor necunoscute"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 1ed7c36..9e922a0 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролируется настройками с ограниченным доступом"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недоступно во время вызовов"</string>
     <string name="disabled" msgid="8017887509554714950">"Отключено"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Разрешено"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Запрещено"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Установка неизвестных приложений"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index dffa392..517b00c 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"සීමා කළ සැකසීම මගින් පාලනය වේ"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ඇමතුම් අතරතුර නොපවතී"</string>
     <string name="disabled" msgid="8017887509554714950">"අබල කර ඇත"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"ඉඩ දුන්"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"ඉඩ නොදෙන"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"නොදන්නා යෙදුම් ස්ථාපනය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 0f4297a..e8fb53f 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ovládané obmedzeným nastavením"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Počas hovorov nie je k dispozícii"</string>
     <string name="disabled" msgid="8017887509554714950">"Deaktivované"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Povolené"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Nie je povolené"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Inštalácia neznámych aplikácií"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 744b4da..bae7722 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Pod nadzorom omejene nastavitve"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ni na voljo med klici"</string>
     <string name="disabled" msgid="8017887509554714950">"Onemogočeno"</string>
+    <string name="enabled" msgid="3997122818554810678">"Omogočeno"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Dovoljene"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Ni dovoljeno"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Nameščanje neznanih aplikacij"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 3d9622e..8a711d5e 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollohet nga \"Cilësimet e kufizuara\""</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nuk ofrohet gjatë telefonatave"</string>
     <string name="disabled" msgid="8017887509554714950">"Çaktivizuar"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Lejohet"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Nuk lejohet"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Instalo aplikacione të panjohura"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 32ec500..d57eb5c 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролишу ограничена подешавања"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недоступно током позива"</string>
     <string name="disabled" msgid="8017887509554714950">"Онемогућено"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Дозвољено"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Није дозвољено"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Инсталирање непознатих апликација"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index cf223f5b..5d27839 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Styrs av spärrad inställning"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ej tillgänglig under samtal"</string>
     <string name="disabled" msgid="8017887509554714950">"Inaktiverad"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Tillåts"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Tillåts inte"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Installera okända appar"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 26e0412..9ddb7ff 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Imedhibitiwa na Mpangilio wenye Mipaka"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Haipatikani wakati unaongea kwa simu"</string>
     <string name="disabled" msgid="8017887509554714950">"Imezimwa"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Imeruhusiwa"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Hairuhusiwi"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Kuweka programu zisizojulikana"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index c2e762f..68a60cf 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"வரையறுக்கப்பட்ட அமைப்பால் கட்டுப்படுத்தப்படுகிறது"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"அழைப்புகளின்போது பயன்படுத்த முடியாது"</string>
     <string name="disabled" msgid="8017887509554714950">"முடக்கப்பட்டது"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"அனுமதிக்கப்பட்டது"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"அனுமதிக்கப்படவில்லை"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"தெரியாத ஆப்ஸ்களை நிறுவுதல்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index f4678f3..c2f284e 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"పరిమితం చేసిన సెట్టింగ్ ద్వారా నియంత్రించబడుతుంది"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"కాల్స్ సమయంలో అందుబాటులో ఉండదు"</string>
     <string name="disabled" msgid="8017887509554714950">"డిజేబుల్ చేయబడింది"</string>
+    <string name="enabled" msgid="3997122818554810678">"ఎనేబుల్ చేయబడింది"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"అనుమతించినవి"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"అనుమతించబడలేదు"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"తెలియని యాప్‌ల ఇన్‌స్టలేషన్"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index e360ae6..035644f 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ควบคุมโดยการตั้งค่าที่จำกัด"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ใช้งานไม่ได้ขณะสนทนาโทรศัพท์"</string>
     <string name="disabled" msgid="8017887509554714950">"ปิดอยู่"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"อนุญาต"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"ไม่อนุญาต"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"ติดตั้งแอปที่ไม่รู้จัก"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 09f40c7..4ef2f43 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kinokontrol ng Pinaghihigpitang Setting"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Hindi available habang may tawag"</string>
     <string name="disabled" msgid="8017887509554714950">"Naka-disable"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Pinapayagan"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Hindi pinapayagan"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Mag-install ng di-kilalang app"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 19b3baf..19295aa 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kısıtlanmış ayar tarafından kontrol ediliyor"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Telefon aramaları sırasında kullanılamaz"</string>
     <string name="disabled" msgid="8017887509554714950">"Devre dışı"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"İzin verildi"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"İzin verilmiyor"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Bilinmeyen uygulamaları yükleme"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index ff18f5c..e46137f 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Керується налаштуваннями з обмеженнями"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недоступно під час викликів"</string>
     <string name="disabled" msgid="8017887509554714950">"Вимкнено"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Дозволено"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Заборонено"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Встановлювати невідомі додатки"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index a07a341..e4b4001 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"محدود کردہ ترتیب کے زیر انتظام ہے"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"کالز کے دوران غیر دستیاب"</string>
     <string name="disabled" msgid="8017887509554714950">"غیر فعال"</string>
+    <string name="enabled" msgid="3997122818554810678">"فعال ہے"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"اجازت ہے"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"اجازت نہیں ہے"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"نامعلوم ایپس انسٹال کریں"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 3a10aa8..2608a04 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -539,6 +539,7 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Cheklangan sozlama tomonidan boshqariladi"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Chaqiruv vaqtida ishlamaydi"</string>
     <string name="disabled" msgid="8017887509554714950">"Oʻchiq"</string>
+    <string name="enabled" msgid="3997122818554810678">"Yoniq"</string>
     <string name="external_source_trusted" msgid="1146522036773132905">"Ruxsat berilgan"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Ruxsat berilmagan"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Notanish ilovalarni o‘rnatish"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index c2c035c..8f5c0c2 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Do chế độ Cài đặt hạn chế kiểm soát"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Không dùng được khi có cuộc gọi"</string>
     <string name="disabled" msgid="8017887509554714950">"Đã tắt"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Được phép"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Không được phép"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Cài ứng dụng không rõ nguồn"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 06379e1..85d3c17 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由受限设置控制"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"通话期间无法使用"</string>
     <string name="disabled" msgid="8017887509554714950">"已停用"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"允许"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"不允许"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"安装未知应用"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index a3ae971..3ff5540 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由「受限設定」控制"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"通話時無法使用"</string>
     <string name="disabled" msgid="8017887509554714950">"已停用"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"允許"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"不允許"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"安裝不明的應用程式"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index f670088..5362d38 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由受限制的設定控管"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"通話時無法使用"</string>
     <string name="disabled" msgid="8017887509554714950">"已停用"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"允許"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"不允許"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"安裝不明應用程式"</string>
@@ -670,7 +672,7 @@
     <string name="add_user_failed" msgid="4809887794313944872">"無法建立新的使用者"</string>
     <string name="add_guest_failed" msgid="8074548434469843443">"無法建立新訪客"</string>
     <string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
-    <string name="edit_user_info_message" msgid="6677556031419002895">"這部裝置的所有使用者都能看到你選擇的名稱和相片。"</string>
+    <string name="edit_user_info_message" msgid="6677556031419002895">"這部裝置的所有使用者,都能看到你選擇的名稱和相片。"</string>
     <string name="user_add_user" msgid="7876449291500212468">"新增使用者"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 1d792a0..79ce6d7b 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -539,6 +539,8 @@
     <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kulawulwe Isethingi Elikhawulelwe"</string>
     <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Akutholakali ngesikhathi samakholi"</string>
     <string name="disabled" msgid="8017887509554714950">"Akusebenzi"</string>
+    <!-- no translation found for enabled (3997122818554810678) -->
+    <skip />
     <string name="external_source_trusted" msgid="1146522036773132905">"Kuvumelekile"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"Akuvumelekile"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"Faka ama-app angaziwa"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 91ec836..1297aa3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1263,6 +1263,8 @@
 
     <!-- [CHAR LIMIT=25] Manage applications, text telling using an application is disabled. -->
     <string name="disabled">Disabled</string>
+    <!-- Summary for a settings preference indicating it is enabled [CHAR LIMIT = 30] -->
+    <string name="enabled">Enabled</string>
     <!-- Summary of app trusted to install apps [CHAR LIMIT=45] -->
     <string name="external_source_trusted">Allowed</string>
     <!-- Summary of app not trusted to install apps [CHAR LIMIT=45] -->
@@ -1508,6 +1510,9 @@
     <!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
     <string name="profile_connect_timeout_subtext">Problem connecting. Turn device off &amp; back on</string>
 
+    <!-- Warning message when the bluetooth key is missing. [CHAR_LIMIT=NONE] -->
+    <string name="bluetooth_key_missing_subtext">Can’t connect</string>
+
     <!-- Name of the 3.5mm audio device. [CHAR LIMIT=40] -->
     <string name="media_transfer_wired_device_name">Wired audio device</string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/PreferenceBindings.kt b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreferenceBinding.kt
similarity index 90%
rename from packages/SettingsLib/src/com/android/settingslib/PreferenceBindings.kt
rename to packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreferenceBinding.kt
index a64e8cc..3489413 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PreferenceBindings.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreferenceBinding.kt
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@file:Suppress("ktlint:standard:filename") // remove once we have more bindings
 
 package com.android.settingslib
 
@@ -29,7 +28,8 @@
 
     override fun bind(preference: Preference, metadata: PreferenceMetadata) {
         super.bind(preference, metadata)
-        (preference as PrimarySwitchPreference).apply {
+        // Could bind on PreferenceScreen
+        (preference as? PrimarySwitchPreference)?.apply {
             isChecked = preferenceDataStore!!.getBoolean(key, false)
             isSwitchEnabled = isEnabled
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 1044750..bf6006b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -120,7 +120,7 @@
             final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
             if (summaryView != null) {
                 final CharSequence disabledText = getDisabledByAdminSummaryString();
-                if (mDisabledByAdmin) {
+                if (mDisabledByAdmin && disabledText != null) {
                     summaryView.setText(disabledText);
                 } else if (mDisabledByEcm) {
                     summaryView.setText(getEcmTextResId());
@@ -132,10 +132,10 @@
         }
     }
 
-    private String getDisabledByAdminSummaryString() {
+    private @Nullable String getDisabledByAdminSummaryString() {
         if (isRestrictionEnforcedByAdvancedProtection()) {
-            return mContext.getString(com.android.settingslib.widget.restricted
-                    .R.string.disabled_by_advanced_protection);
+            // Advanced Protection doesn't set the summary string, it keeps the current summary.
+            return null;
         }
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
             return mContext.getSystemService(DevicePolicyManager.class).getResources().getString(
@@ -151,6 +151,18 @@
                         UserHandle.myUserId());
     }
 
+    /**
+     * Configures the user restriction that this preference will track. This is equivalent to
+     * specifying {@link R.styleable#RestrictedPreference_userRestriction} in XML and allows
+     * configuring user restriction at runtime.
+     */
+    public void setUserRestriction(@Nullable String userRestriction) {
+        mAttrUserRestriction = userRestriction == null ||
+            RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext, userRestriction,
+                UserHandle.myUserId()) ? null : userRestriction;
+        setDisabledByAdmin(checkRestrictionEnforced());
+    }
+
     public void useAdminDisabledSummary(boolean useSummary) {
         mDisabledSummary = useSummary;
     }
@@ -321,7 +333,10 @@
         }
 
         if (android.security.Flags.aapmApi() && !isEnabled && mDisabledByAdmin) {
-            mPreference.setSummary(getDisabledByAdminSummaryString());
+            String summary = getDisabledByAdminSummaryString();
+            if (summary != null) {
+                mPreference.setSummary(summary);
+            }
         }
 
         if (!isEnabled && mDisabledByEcm) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index a5fa6a8..67c4207 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -36,6 +36,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceViewHolder;
@@ -141,7 +142,7 @@
             final TextView additionalSummaryView = (TextView) holder.findViewById(
                     R.id.additional_summary);
             if (additionalSummaryView != null) {
-                if (isDisabledByAdmin()) {
+                if (isDisabledByAdmin() && switchSummary != null) {
                     additionalSummaryView.setText(switchSummary);
                     additionalSummaryView.setVisibility(View.VISIBLE);
                 } else {
@@ -151,7 +152,7 @@
         } else {
             final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
             if (summaryView != null) {
-                if (isDisabledByAdmin()) {
+                if (isDisabledByAdmin() && switchSummary != null) {
                     summaryView.setText(switchSummary);
                     summaryView.setVisibility(View.VISIBLE);
                 }
@@ -171,14 +172,10 @@
                 () -> context.getString(resId));
     }
 
-    private String getRestrictedSwitchSummary() {
+    private @Nullable String getRestrictedSwitchSummary() {
         if (mHelper.isRestrictionEnforcedByAdvancedProtection()) {
-            final int apmResId = isChecked()
-                    ? com.android.settingslib.widget.restricted.R.string
-                            .enabled_by_advanced_protection
-                    : com.android.settingslib.widget.restricted.R.string
-                            .disabled_by_advanced_protection;
-            return getContext().getString(apmResId);
+            // Advanced Protection doesn't set the summary string, it keeps the current summary.
+            return null;
         }
 
         return isChecked()
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 3625c00..7cdc13c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -36,6 +36,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.collection.ArraySet;
 
 import com.android.settingslib.R;
 import com.android.settingslib.flags.Flags;
@@ -274,29 +275,37 @@
     @VisibleForTesting
     void dispatchActiveDeviceChanged(
             @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
-        CachedBluetoothDevice targetDevice = activeDevice;
+        CachedBluetoothDevice mainActiveDevice = activeDevice;
         for (CachedBluetoothDevice cachedDevice : mDeviceManager.getCachedDevicesCopy()) {
-            // should report isActive from main device or it will cause trouble to other callers.
             CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
-            CachedBluetoothDevice finalTargetDevice = targetDevice;
-            if (targetDevice != null
-                    && ((subDevice != null && subDevice.equals(targetDevice))
-                    || cachedDevice.getMemberDevice().stream().anyMatch(
-                            memberDevice -> memberDevice.equals(finalTargetDevice)))) {
-                Log.d(TAG,
-                        "The active device is the sub/member device "
-                                + targetDevice.getDevice().getAnonymizedAddress()
-                                + ". change targetDevice as main device "
-                                + cachedDevice.getDevice().getAnonymizedAddress());
-                targetDevice = cachedDevice;
+            Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+            final Set<CachedBluetoothDevice> cachedDevices = new ArraySet<>();
+            cachedDevices.add(cachedDevice);
+            if (!memberDevices.isEmpty()) {
+                cachedDevices.addAll(memberDevices);
+            } else if (subDevice != null) {
+                cachedDevices.add(subDevice);
             }
-            boolean isActiveDevice = cachedDevice.equals(targetDevice);
-            cachedDevice.onActiveDeviceChanged(isActiveDevice, bluetoothProfile);
+
+            // should report isActive from main device or it will cause trouble to other callers.
+            if (activeDevice != null
+                    && (cachedDevices.stream().anyMatch(
+                            device -> device.equals(activeDevice)))) {
+                Log.d(TAG, "The active device is in the set, report main device as active device:"
+                        + cachedDevice.getDevice() + ", active device:" + activeDevice.getDevice());
+                mainActiveDevice = cachedDevice;
+            }
+            boolean isActiveDevice = cachedDevice.equals(mainActiveDevice);
+            cachedDevices.forEach(
+                    device -> device.onActiveDeviceChanged(isActiveDevice, bluetoothProfile));
+            //TODO: b/400440223 - Check if we can call DeviceManager.onActiveDeviceChanged &
+            // Callback.onActiveDeviceChanged for cachedDevices Set also, so we don't need to report
+            // isActive from main device.
             mDeviceManager.onActiveDeviceChanged(cachedDevice);
         }
 
         for (BluetoothCallback callback : mCallbacks) {
-            callback.onActiveDeviceChanged(targetDevice, bluetoothProfile);
+            callback.onActiveDeviceChanged(mainActiveDevice, bluetoothProfile);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 3646842..011b2fc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1495,6 +1495,11 @@
         int leftBattery = -1;
         int rightBattery = -1;
 
+        Integer keyMissingCount = BluetoothUtils.getKeyMissingCount(mDevice);
+        if (keyMissingCount != null && keyMissingCount > 0) {
+            return mContext.getString(R.string.bluetooth_key_missing_subtext);
+        }
+
         if (isProfileConnectedFail() && isConnected()) {
             return mContext.getString(R.string.profile_connect_timeout_subtext);
         }
@@ -1863,10 +1868,31 @@
                 + " mIsLeAudioProfileConnectedFail=" + mIsLeAudioProfileConnectedFail
                 + " mIsHeadsetProfileConnectedFail=" + mIsHeadsetProfileConnectedFail
                 + " isConnectedSapDevice()=" + isConnectedSapDevice());
-
-        return mIsA2dpProfileConnectedFail || mIsHearingAidProfileConnectedFail
-                || (!isConnectedSapDevice() && mIsHeadsetProfileConnectedFail)
-                || mIsLeAudioProfileConnectedFail;
+        if (mIsA2dpProfileConnectedFail) {
+            A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+            if (a2dpProfile != null && a2dpProfile.isEnabled(mDevice)) {
+                return true;
+            }
+        }
+        if (mIsHearingAidProfileConnectedFail) {
+            HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+            if (hearingAidProfile != null && hearingAidProfile.isEnabled(mDevice)) {
+                return true;
+            }
+        }
+        if (!isConnectedSapDevice() && mIsHeadsetProfileConnectedFail) {
+            HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+            if (headsetProfile != null && headsetProfile.isEnabled(mDevice)) {
+                return true;
+            }
+        }
+        if (mIsLeAudioProfileConnectedFail) {
+            LeAudioProfile leAudioProfile = mProfileManager.getLeAudioProfile();
+            if (leAudioProfile != null && leAudioProfile.isEnabled(mDevice)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 08f7806..b0f3796 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -19,6 +19,9 @@
 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
 
 import static com.android.settingslib.Utils.isAudioModeOngoingCall;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.STREAMING;
 
 import static java.util.stream.Collectors.toList;
 
@@ -70,9 +73,11 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -89,11 +94,14 @@
             "com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_STATE_CHANGE";
     public static final String ACTION_LE_AUDIO_SHARING_DEVICE_CONNECTED =
             "com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_DEVICE_CONNECTED";
+    public static final String ACTION_LE_AUDIO_PRIVATE_BROADCAST_RECEIVED =
+            "com.android.settings.action.BLUETOOTH_LE_AUDIO_PRIVATE_BROADCAST_RECEIVED";
     public static final String EXTRA_LE_AUDIO_SHARING_STATE = "BLUETOOTH_LE_AUDIO_SHARING_STATE";
     public static final String EXTRA_BLUETOOTH_DEVICE = "BLUETOOTH_DEVICE";
     public static final String EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE = "BT_DEVICE_TO_AUTO_ADD_SOURCE";
     public static final String EXTRA_START_LE_AUDIO_SHARING = "START_LE_AUDIO_SHARING";
     public static final String EXTRA_PAIR_AND_JOIN_SHARING = "PAIR_AND_JOIN_SHARING";
+    public static final String EXTRA_PRIVATE_BROADCAST_RECEIVE_DATA = "RECEIVE_DATA";
     public static final String BLUETOOTH_LE_BROADCAST_PRIMARY_DEVICE_GROUP_ID =
             "bluetooth_le_broadcast_primary_device_group_id";
     public static final int BROADCAST_STATE_UNKNOWN = 0;
@@ -135,6 +143,8 @@
             };
     private final Context mContext;
     private final CachedBluetoothDeviceManager mDeviceManager;
+    private final boolean mHysteresisModeFixAvailable;
+    private final boolean mIsWorkProfile;
     private BluetoothLeBroadcast mServiceBroadcast;
     private BluetoothLeBroadcastAssistant mServiceBroadcastAssistant;
     private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata;
@@ -155,6 +165,7 @@
     // Cached broadcast callbacks being register before service is connected.
     private ConcurrentHashMap<BluetoothLeBroadcast.Callback, Executor>
             mCachedBroadcastCallbackExecutorMap = new ConcurrentHashMap<>();
+    private Set<BluetoothDevice> mLocalSinksPendingSourceRemoval = new HashSet<>();
 
     private final ServiceListener mServiceListener =
             new ServiceListener() {
@@ -373,6 +384,7 @@
                                         + ", sourceId = "
                                         + sourceId);
                     }
+                    mLocalSinksPendingSourceRemoval.remove(sink);
                 }
 
                 @Override
@@ -405,6 +417,35 @@
                                         + ", state = "
                                         + state);
                     }
+                    if (!Flags.audioStreamMediaServiceByReceiveState()) {
+                        Log.d(TAG, "Skip notifyPrivateBroadcastReceived, flag off.");
+                        return;
+                    }
+                    if (mIsWorkProfile) {
+                        Log.d(TAG, "Skip notifyPrivateBroadcastReceived for work profile.");
+                        return;
+                    }
+                    if (state.getBroadcastId() == mBroadcastId
+                            || !mLocalSinksPendingSourceRemoval.isEmpty()) {
+                        Log.d(TAG,
+                                "Skip notifyPrivateBroadcastReceived, onReceiveStateChanged "
+                                        + "triggered by personal audio sharing.");
+                        return;
+                    }
+                    var sourceState = LocalBluetoothLeBroadcastAssistant.getLocalSourceState(state);
+                    if (sourceState == STREAMING || sourceState == DECRYPTION_FAILED
+                            || (mHysteresisModeFixAvailable && sourceState == PAUSED)) {
+                        List<BluetoothLeAudioContentMetadata> subgroupMetadata =
+                                state.getSubgroupMetadata();
+                        String programInfo = subgroupMetadata.isEmpty() ? ""
+                                : subgroupMetadata.getFirst().getProgramInfo();
+                        notifyPrivateBroadcastReceived(
+                                sink,
+                                sourceId,
+                                state.getBroadcastId(),
+                                programInfo == null ? "" : programInfo,
+                                sourceState);
+                    }
                 }
             };
 
@@ -436,6 +477,10 @@
         BluetoothAdapter.getDefaultAdapter()
                 .getProfileProxy(
                         context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+
+        mHysteresisModeFixAvailable = BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(
+                context);
+        mIsWorkProfile = isWorkProfile(mContext);
     }
 
     /**
@@ -721,7 +766,10 @@
             Log.d(TAG, "The BluetoothLeBroadcast is null");
             return null;
         }
-        if (mBluetoothLeBroadcastMetadata == null) {
+        if (mBluetoothLeBroadcastMetadata == null
+                // mBroadcastId is updated when onBroadcastStarted, which is always before
+                // onBroadcastMetadataChanged, so mBroadcastId is always the latest broadcast info
+                || mBluetoothLeBroadcastMetadata.getBroadcastId() != mBroadcastId) {
             final List<BluetoothLeBroadcastMetadata> metadataList =
                     mServiceBroadcast.getAllBroadcastMetadata();
             mBluetoothLeBroadcastMetadata =
@@ -729,6 +777,7 @@
                             .filter(i -> i.getBroadcastId() == mBroadcastId)
                             .findFirst()
                             .orElse(null);
+            Log.d(TAG, "getLatestBluetoothLeBroadcastMetadata for broadcast id " + mBroadcastId);
         }
         return mBluetoothLeBroadcastMetadata;
     }
@@ -1131,6 +1180,7 @@
                 int localBroadcastId = getLatestBroadcastId();
                 if (receiveState.getBroadcastId() != localBroadcastId) continue;
 
+                mLocalSinksPendingSourceRemoval.add(device);
                 mServiceBroadcastAssistant.removeSource(device, receiveState.getSourceId());
             }
         }
@@ -1144,7 +1194,7 @@
             Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded, disable flag is on");
             return;
         }
-        if (isWorkProfile(mContext)) {
+        if (mIsWorkProfile) {
             Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded for work profile.");
             return;
         }
@@ -1272,7 +1322,7 @@
             Log.d(TAG, "Skip notifyBroadcastStateChange, not triggered by Settings or SystemUI.");
             return;
         }
-        if (isWorkProfile(mContext)) {
+        if (mIsWorkProfile) {
             Log.d(TAG, "Skip notifyBroadcastStateChange, not triggered for work profile.");
             return;
         }
@@ -1283,6 +1333,26 @@
         mContext.sendBroadcast(intent);
     }
 
+    private void notifyPrivateBroadcastReceived(BluetoothDevice sink, int sourceId, int broadcastId,
+            String programInfo,
+            LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState state) {
+        String packageName = mContext.getPackageName();
+        if (!packageName.equals(SYSUI_PKG)) {
+            Log.d(TAG, "Skip notifyPrivateBroadcastReceived, not triggered by SystemUI.");
+            return;
+        }
+        var data = new PrivateBroadcastReceiveData(sink, sourceId, broadcastId, programInfo, state);
+        Intent intent = new Intent(ACTION_LE_AUDIO_PRIVATE_BROADCAST_RECEIVED);
+        intent.putExtra(EXTRA_PRIVATE_BROADCAST_RECEIVE_DATA, data);
+        intent.setPackage(SETTINGS_PKG);
+        Log.d(TAG,
+                "notifyPrivateBroadcastReceived for sink = " + sink + " with sourceId = " + sourceId
+                        + " state = " + state
+                        + " programInfo =" + programInfo
+                        + " broadcastId = " + broadcastId);
+        mContext.sendBroadcast(intent);
+    }
+
     private boolean isWorkProfile(Context context) {
         UserManager userManager = context.getSystemService(UserManager.class);
         return userManager != null && userManager.isManagedProfile();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt
new file mode 100644
index 0000000..a284d20
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveData.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth
+
+import android.bluetooth.BluetoothDevice
+import android.os.Parcel
+import android.os.Parcelable
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.STREAMING
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED
+
+/**
+ * Data class representing information received in a private broadcast.
+ * This class encapsulates details about the sink device, source ID, broadcast ID, and the
+ * broadcast source state.
+ *
+ * @param sink The [BluetoothDevice] acting as the sink.
+ * @param sourceId The ID of the audio source.
+ * @param broadcastId The ID of the broadcast source.
+ * @param programInfo The program info string of the broadcast source.
+ * @param state The current state of the broadcast source.
+ */
+data class PrivateBroadcastReceiveData(
+    val sink: BluetoothDevice?,
+    val sourceId: Int = -1,
+    val broadcastId: Int = -1,
+    val programInfo: String = "",
+    val state: LocalBluetoothLeBroadcastSourceState?,
+) : Parcelable {
+
+    override fun describeContents(): Int = 0
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.writeParcelable(sink, flags)
+        parcel.writeInt(sourceId)
+        parcel.writeInt(broadcastId)
+        parcel.writeString(programInfo)
+        parcel.writeSerializable(state)
+    }
+
+    companion object {
+        @JvmField
+        val CREATOR: Parcelable.Creator<PrivateBroadcastReceiveData> =
+            object : Parcelable.Creator<PrivateBroadcastReceiveData> {
+                override fun createFromParcel(parcel: Parcel) =
+                    parcel.run {
+                        PrivateBroadcastReceiveData(
+                            sink = readParcelable(
+                                BluetoothDevice::class.java.classLoader,
+                                BluetoothDevice::class.java
+                            ),
+                            sourceId = readInt(),
+                            broadcastId = readInt(),
+                            programInfo = readString() ?: "",
+                            state = readSerializable(
+                                LocalBluetoothLeBroadcastSourceState::class.java.classLoader,
+                                LocalBluetoothLeBroadcastSourceState::class.java
+                            )
+                        )
+                    }
+                override fun newArray(size: Int): Array<PrivateBroadcastReceiveData?> {
+                    return arrayOfNulls(size)
+                }
+            }
+
+        fun PrivateBroadcastReceiveData.isValid(): Boolean {
+            return sink != null
+                    && sourceId != -1
+                    && broadcastId != -1
+                    && (state == STREAMING
+                    || state == PAUSED
+                    || state == DECRYPTION_FAILED)
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
index 58c7907..086516b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
@@ -114,14 +114,19 @@
         activeModesDurations.remove(id)
     }
 
-    // Update the active state while maintaining the mode's position in the list
+    /** Updates a [ZenMode]'s active state, preserving its position in the list. */
     private fun updateModeActiveState(id: String, isActive: Boolean) {
+        updateMode(id) { TestModeBuilder(it).setActive(isActive).build() }
+    }
+
+    /** Updates a [ZenMode], preserving its position in the list. */
+    fun updateMode(id: String, update: (original: ZenMode) -> ZenMode) {
         val modes = mutableModesFlow.value.toMutableList()
         val index = modes.indexOfFirst { it.id == id }
         if (index < 0) {
             throw IllegalArgumentException("mode $id not found")
         }
-        modes[index] = TestModeBuilder(modes[index]).setActive(isActive).build()
+        modes[index] = update(modes[index])
         mutableModesFlow.value = modes
     }
 }
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/packages/SettingsLib/src/com/android/settingslib/supervision/SupervisionLog.kt
similarity index 72%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to packages/SettingsLib/src/com/android/settingslib/supervision/SupervisionLog.kt
index 5d14631..92455c0 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/supervision/SupervisionLog.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package com.android.settingslib.supervision
 
-parcelable PictureProfileHandle;
+/** Constants used in supervision logs. */
+object SupervisionLog {
+    const val TAG = "SupervisionSettings"
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/supervision/SupervisionRestrictionsHelper.kt b/packages/SettingsLib/src/com/android/settingslib/supervision/SupervisionRestrictionsHelper.kt
new file mode 100644
index 0000000..1be8a17
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/supervision/SupervisionRestrictionsHelper.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.supervision
+
+import android.app.admin.DeviceAdminReceiver
+import android.app.supervision.SupervisionManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.util.Log
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
+
+/** Helper class for supervision-enforced restrictions. */
+object SupervisionRestrictionsHelper {
+
+    /**
+     * Creates an instance of [EnforcedAdmin] that uses the correct supervision component or returns
+     * null if supervision is not enabled.
+     */
+    @JvmStatic
+    fun createEnforcedAdmin(
+        context: Context,
+        restriction: String,
+        user: UserHandle,
+    ): EnforcedAdmin? {
+        val supervisionManager = context.getSystemService(SupervisionManager::class.java)
+        val supervisionAppPackage = supervisionManager?.activeSupervisionAppPackage ?: return null
+        var supervisionComponent: ComponentName? = null
+
+        // Try to find the service whose package matches the active supervision app.
+        val resolveSupervisionApps =
+            context.packageManager.queryIntentServicesAsUser(
+                Intent("android.app.action.BIND_SUPERVISION_APP_SERVICE"),
+                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+                user.identifier,
+            )
+        resolveSupervisionApps
+            .mapNotNull { it.serviceInfo?.componentName }
+            .find { it.packageName == supervisionAppPackage }
+            ?.let { supervisionComponent = it }
+
+        if (supervisionComponent == null) {
+            // Try to find the PO receiver whose package matches the active supervision app, for
+            // backwards compatibility.
+            val resolveDeviceAdmins =
+                context.packageManager.queryBroadcastReceiversAsUser(
+                    Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
+                    PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+                    user.identifier,
+                )
+            resolveDeviceAdmins
+                .mapNotNull { it.activityInfo?.componentName }
+                .find { it.packageName == supervisionAppPackage }
+                ?.let { supervisionComponent = it }
+        }
+
+        if (supervisionComponent == null) {
+            Log.d(SupervisionLog.TAG, "Could not find the supervision component.")
+        }
+        return EnforcedAdmin(supervisionComponent, restriction, user)
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserActivity.java
index c5e6f60..2b95fd1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserActivity.java
@@ -93,18 +93,10 @@
 
     @Override
     public boolean onTouchEvent(@Nullable MotionEvent event) {
-        onBackInvoked();
+        cancel();
         return super.onTouchEvent(event);
     }
 
-    private void onBackInvoked() {
-        if (mSetupUserDialog != null) {
-            mSetupUserDialog.dismiss();
-        }
-        setResult(RESULT_CANCELED);
-        finish();
-    }
-
     @VisibleForTesting
     void setSuccessResult(String userName, Drawable userIcon, String path, Boolean isAdmin) {
         Intent intent = new Intent(this, CreateUserActivity.class);
@@ -112,14 +104,12 @@
         intent.putExtra(EXTRA_IS_ADMIN, isAdmin);
         intent.putExtra(EXTRA_USER_ICON_PATH, path);
 
-        mSetupUserDialog.dismiss();
         setResult(RESULT_OK, intent);
         finish();
     }
 
     @VisibleForTesting
     void cancel() {
-        mSetupUserDialog.dismiss();
         setResult(RESULT_CANCELED);
         finish();
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
index d9f1b63..bce229f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
@@ -282,7 +282,7 @@
                 mCustomDialogHelper.getDialog().dismiss();
                 break;
             case EXIT_DIALOG:
-                finish();
+                mUserCreationDialog.dismiss();
                 break;
             default:
                 if (mCurrentState < EXIT_DIALOG) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
index 88bccc9..2ed437c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
@@ -509,6 +509,7 @@
                     val intent = AdvancedProtectionManager.createSupportIntent(
                         AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP,
                         AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION)
+                    intent.putExtra(DIALOG_WINDOW_TYPE, dialogWindowType)
                     onStartActivity(intent)
                 } else if (wifiManager.isWepSupported == true && wifiManager.queryWepAllowed()) {
                     onAllowed()
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
new file mode 100644
index 0000000..78dba57
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate
+
+import android.content.ContentResolver
+import android.content.Context
+import android.content.res.Resources
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceStateAutoRotateSettingManagerImplTest {
+    @get:Rule
+    val rule = MockitoJUnit.rule()
+
+    private val fakeSecureSettings = FakeSecureSettings()
+    private val executor: Executor = Executor { it.run() }
+    private val configPerDeviceStateRotationLockDefaults = arrayOf(
+        "$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED:" +
+                "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+                "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+        "$DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY:" +
+                "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+                "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+        "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+        "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+    )
+
+    @Mock
+    private lateinit var mockContext: Context
+
+    @Mock
+    private lateinit var mockContentResolver: ContentResolver
+
+    @Mock
+    private lateinit var mockPosturesHelper: PosturesHelper
+
+    @Mock
+    private lateinit var mockHandler: Handler
+
+    @Mock
+    private lateinit var mockDeviceStateManager: DeviceStateManager
+
+    @Mock
+    private lateinit var mockResources: Resources
+    private lateinit var settingManager: DeviceStateAutoRotateSettingManagerImpl
+
+    @Before
+    fun setUp() {
+        whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
+        whenever(mockContext.resources).thenReturn(mockResources)
+        whenever(mockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+            .thenReturn(configPerDeviceStateRotationLockDefaults)
+        whenever(mockHandler.post(any(Runnable::class.java))).thenAnswer { invocation ->
+            val runnable = invocation.arguments[0] as Runnable
+            runnable.run()
+            null
+        }
+        whenever(mockContext.getSystemService(DeviceStateManager::class.java))
+            .thenReturn(mockDeviceStateManager)
+        whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_UNFOLDED))
+            .thenReturn(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
+        whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_FOLDED))
+            .thenReturn(DEVICE_STATE_ROTATION_KEY_FOLDED)
+        whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED))
+            .thenReturn(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
+        whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_INVALID))
+            .thenReturn(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+        whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY))
+            .thenReturn(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
+
+        settingManager =
+            DeviceStateAutoRotateSettingManagerImpl(
+                mockContext,
+                executor,
+                fakeSecureSettings,
+                mockHandler,
+                mockPosturesHelper,
+            )
+    }
+
+    @Test
+    fun registerListener_onSettingsChanged_listenerNotified() {
+        val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+        settingManager.registerListener(listener)
+
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        verify(listener).onSettingsChanged()
+    }
+
+    @Test
+    fun registerMultipleListeners_onSettingsChanged_allListenersNotified() {
+        val listener1 = mock(DeviceStateAutoRotateSettingListener::class.java)
+        val listener2 = mock(DeviceStateAutoRotateSettingListener::class.java)
+        settingManager.registerListener(listener1)
+        settingManager.registerListener(listener2)
+
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        verify(listener1).onSettingsChanged()
+        verify(listener2).onSettingsChanged()
+    }
+
+    @Test
+    fun unregisterListener_onSettingsChanged_listenerNotNotified() {
+        val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+        settingManager.registerListener(listener)
+        settingManager.unregisterListener(listener)
+
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        verify(listener, never()).onSettingsChanged()
+    }
+
+    @Test
+    fun getAutoRotateSetting_offForUnfolded_returnsOff() {
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_onForFolded_returnsOn() {
+        persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_forInvalidPostureWithNoFallback_returnsIgnored() {
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_INVALID)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_forInvalidPosture_returnsSettingForFallbackPosture() {
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+        persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_HALF_FOLDED)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_invalidFormat_returnsIgnored() {
+        persistSettings("invalid_format")
+
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_invalidNumberFormat_returnsIgnored() {
+        persistSettings("$DEVICE_STATE_ROTATION_KEY_FOLDED:4")
+
+        val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+        assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+    }
+
+    @Test
+    fun getAutoRotateSetting_multipleSettings_returnsCorrectSetting() {
+        persistSettings(
+            "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+                    "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+        )
+
+        val foldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+        val unfoldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+        assertThat(foldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+        assertThat(unfoldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+    }
+
+    @Test
+    fun isAutoRotateOff_offForUnfolded_returnsTrue() {
+        persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+        val isAutoRotateOff = settingManager.isRotationLocked(DEVICE_STATE_UNFOLDED)
+
+        assertThat(isAutoRotateOff).isTrue()
+    }
+
+    @Test
+    fun isRotationLockedForAllStates_allStatesLocked_returnsTrue() {
+        persistSettings(
+            "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+                    "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+        )
+
+        val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+        assertThat(isRotationLockedForAllStates).isTrue()
+    }
+
+    @Test
+    fun isRotationLockedForAllStates_someStatesLocked_returnsFalse() {
+        persistSettings(
+            "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+                    "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+        )
+
+        val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+        assertThat(isRotationLockedForAllStates).isFalse()
+    }
+
+    @Test
+    fun isRotationLockedForAllStates_noStatesLocked_returnsFalse() {
+        persistSettings(
+            "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+                    "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+        )
+
+        val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+        assertThat(isRotationLockedForAllStates).isFalse()
+    }
+
+    @Test
+    fun getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
+        val settableDeviceStates = settingManager.getSettableDeviceStates()
+
+        assertThat(settableDeviceStates)
+            .containsExactly(
+                SettableDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED, isSettable = true),
+                SettableDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED, isSettable = true),
+                SettableDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, isSettable = false),
+                SettableDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY, isSettable = false),
+                SettableDeviceState(DEVICE_STATE_ROTATION_LOCK_IGNORED, isSettable = false),
+            )
+    }
+
+    private fun persistSettings(devicePosture: Int, autoRotateSetting: Int) {
+        persistSettings("$devicePosture:$autoRotateSetting")
+    }
+
+    private fun persistSettings(value: String) {
+        fakeSecureSettings.putStringForUser(
+            Settings.Secure.DEVICE_STATE_ROTATION_LOCK, value, UserHandle.USER_CURRENT
+        )
+    }
+
+    private companion object {
+        const val DEVICE_STATE_FOLDED = 0
+        const val DEVICE_STATE_HALF_FOLDED = 1
+        const val DEVICE_STATE_UNFOLDED = 2
+        const val DEVICE_STATE_REAR_DISPLAY = 3
+        const val DEVICE_STATE_INVALID = 4
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index 9f9aaf5..baebaf7 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -40,7 +40,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
 
 import com.google.common.truth.Expect;
 
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
index 08484bc..8d9982f 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
@@ -2,9 +2,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyFloat;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
index 9ab17c4..b849919 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
@@ -18,10 +18,10 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
index ab28d06..94bb616 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
@@ -16,11 +16,11 @@
 
 package com.android.settingslib.users;
 
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.nullable;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
index 995314e..055487b 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
@@ -22,7 +22,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.never;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index e7533bc1..7f4d366 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -71,7 +71,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
-import org.mockito.Matchers;
+import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
@@ -197,7 +197,7 @@
                 .registerNetworkScoreCache(
                         anyInt(),
                         mScoreCacheCaptor.capture(),
-                        Matchers.anyInt());
+                        ArgumentMatchers.anyInt());
 
         // Capture requested keys and count down latch if present
         doAnswer(
@@ -213,7 +213,7 @@
                         }
                         return true;
                     }
-                }).when(mockNetworkScoreManager).requestScores(Matchers.<NetworkKey[]>any());
+                }).when(mockNetworkScoreManager).requestScores(ArgumentMatchers.<NetworkKey[]>any());
 
         // We use a latch to detect callbacks as Tracker initialization state often invokes
         // callbacks
@@ -464,9 +464,9 @@
         startTracking(tracker);
         verify(mockNetworkScoreManager)
                 .registerNetworkScoreCache(
-                          Matchers.anyInt(),
+                          ArgumentMatchers.anyInt(),
                           mScoreCacheCaptor.capture(),
-                          Matchers.anyInt());
+                          ArgumentMatchers.anyInt());
 
         WifiNetworkScoreCache scoreCache = mScoreCacheCaptor.getValue();
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
index dbbbd5b..f9769fa 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
@@ -23,6 +23,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.atMostOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -130,7 +131,7 @@
 
     @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
     @Test
-    public void bindPreference_disabled_byAdvancedProtection_shouldDisplayDisabledSummary() {
+    public void bindPreference_disabled_byAdvancedProtection_shouldKeepExistingSummary() {
         final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
         final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
         final RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils
@@ -143,16 +144,14 @@
                 .thenReturn(summaryView);
         when(mDevicePolicyManager.getEnforcingAdmin(UserHandle.myUserId(), userRestriction))
                 .thenReturn(advancedProtectionEnforcingAdmin);
-        when(mContext.getString(
-                com.android.settingslib.widget.restricted.R.string.disabled_by_advanced_protection))
-                .thenReturn("advanced_protection");
 
+        summaryView.setText("existing summary");
         mHelper.useAdminDisabledSummary(true);
         mHelper.setDisabledByAdmin(enforcedAdmin);
         mHelper.onBindViewHolder(mViewHolder);
 
-        verify(summaryView).setText("advanced_protection");
-        verify(summaryView, never()).setVisibility(View.GONE);
+        verify(summaryView, atMostOnce()).setText(any()); // To set it to existing summary
+        verify(summaryView, never()).setVisibility(View.VISIBLE);
     }
 
     @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_API)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index eac69234..2620174 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -70,6 +70,9 @@
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private static final String DEVICE_NAME = "test_device_name";
+    private static final String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
+    private static final String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
+    private static final String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33";
 
     @Mock
     private LocalBluetoothAdapter mLocalAdapter;
@@ -132,6 +135,9 @@
         when(mA2dpProfile.isProfileReady()).thenReturn(true);
         when(mHearingAidProfile.isProfileReady()).thenReturn(true);
         when(mLeAudioProfile.isProfileReady()).thenReturn(true);
+        when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
+        when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
+        when(mDevice3.getAddress()).thenReturn(DEVICE_ADDRESS_3);
         mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1);
         mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2);
         mCachedDevice3 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3);
@@ -515,7 +521,6 @@
         cachedDevices.add(mCachedDevice2);
 
         int group1 = 1;
-        when(mDevice3.getAddress()).thenReturn("testAddress3");
         mCachedDevice1.setGroupId(group1);
         mCachedDevice3.setGroupId(group1);
         mCachedDevice1.addMemberDevice(mCachedDevice3);
@@ -620,18 +625,32 @@
     }
 
     @Test
-    public void dispatchActiveDeviceChanged_activeFromSubDevice_mainCachedDeviceActive() {
+    public void dispatchActiveDeviceChanged_activeFromSubDevice_bothCachedDevicesActive() {
         CachedBluetoothDevice subDevice = new CachedBluetoothDevice(mContext, mLocalProfileManager,
                 mDevice3);
         mCachedDevice1.setSubDevice(subDevice);
         when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
                 Collections.singletonList(mCachedDevice1));
-        mCachedDevice1.onProfileStateChanged(mHearingAidProfile,
-                BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
 
-        assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
         mBluetoothEventManager.dispatchActiveDeviceChanged(subDevice, BluetoothProfile.HEARING_AID);
+
         assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
+        assertThat(subDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
+    }
+
+    @Test
+    public void dispatchActiveDeviceChanged_activeFromMemberDevice_allCachedDevicesActive() {
+        mCachedDevice1.addMemberDevice(mCachedDevice2);
+        when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+                Collections.singletonList(mCachedDevice1));
+        mCachedDevice1.onProfileStateChanged(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+
+        mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice2,
+                BluetoothProfile.LE_AUDIO);
+
+        assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).isTrue();
+        assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.LE_AUDIO)).isTrue();
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index ed53d8d..f57ee0c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -182,6 +182,8 @@
         updateProfileStatus(connectingProfile, BluetoothProfile.STATE_CONNECTING);
         // Set connection policy
         when(connectingProfile.getConnectionPolicy(mDevice)).thenReturn(connectionPolicy);
+        when(connectingProfile.isEnabled(mDevice))
+                .thenReturn(connectionPolicy > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
 
         // Act & Assert:
         //   Get the expected connection summary.
@@ -191,6 +193,9 @@
 
     @Test
     public void onProfileStateChanged_testConnectingToDisconnected_policyAllowed_problem() {
+        when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+        when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+
         String connectTimeoutString = mContext.getString(R.string.profile_connect_timeout_subtext);
 
         testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile,
@@ -205,6 +210,9 @@
 
     @Test
     public void onProfileStateChanged_testConnectingToDisconnected_policyForbidden_noProblem() {
+        when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+        when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+
         testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile,
         BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null);
         testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile,
@@ -217,6 +225,9 @@
 
     @Test
     public void onProfileStateChanged_testConnectingToDisconnected_policyUnknown_noProblem() {
+        when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+        when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+
         testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile,
         BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null);
         testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile,
@@ -1830,6 +1841,10 @@
     @Test
     public void getConnectionSummary_profileConnectedFail_showErrorMessage() {
         final A2dpProfile profile = mock(A2dpProfile.class);
+        when(mProfileManager.getA2dpProfile()).thenReturn(profile);
+        when(profile.getConnectionPolicy(mDevice))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        when(profile.isEnabled(mDevice)).thenReturn(true);
         mCachedDevice.onProfileStateChanged(profile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.setProfileConnectedStatus(BluetoothProfile.A2DP, true);
 
@@ -1842,6 +1857,10 @@
     @Test
     public void getTvConnectionSummary_profileConnectedFail_showErrorMessage() {
         final A2dpProfile profile = mock(A2dpProfile.class);
+        when(mProfileManager.getA2dpProfile()).thenReturn(profile);
+        when(profile.getConnectionPolicy(mDevice))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        when(profile.isEnabled(mDevice)).thenReturn(true);
         mCachedDevice.onProfileStateChanged(profile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.setProfileConnectedStatus(BluetoothProfile.A2DP, true);
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt
new file mode 100644
index 0000000..5fd67a1
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PrivateBroadcastReceiveDataTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.os.Parcel
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState
+import com.android.settingslib.bluetooth.PrivateBroadcastReceiveData.Companion.isValid
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class PrivateBroadcastReceiveDataTest {
+
+    @Test
+    fun parcelable() {
+        val original = PrivateBroadcastReceiveData(
+            sink = sink,
+            sourceId = 1,
+            broadcastId = 2,
+            programInfo = "Test Program",
+            state = LocalBluetoothLeBroadcastSourceState.STREAMING
+        )
+
+        val parcel = Parcel.obtain()
+        original.writeToParcel(parcel, 0)
+        parcel.setDataPosition(0)
+
+        val recreated = PrivateBroadcastReceiveData.CREATOR.createFromParcel(parcel)
+
+        assertEquals(original, recreated)
+    }
+
+    @Test
+    fun isValid_validData() {
+        val data = PrivateBroadcastReceiveData(
+            sink = sink,
+            sourceId = 1,
+            broadcastId = 2,
+            state = LocalBluetoothLeBroadcastSourceState.STREAMING
+        )
+        assertTrue(data.isValid())
+    }
+
+    @Test
+    fun isValid_nullSink() {
+        val data = PrivateBroadcastReceiveData(
+            sink = null,
+            sourceId = 1,
+            broadcastId = 2,
+            state = LocalBluetoothLeBroadcastSourceState.STREAMING
+        )
+        assertFalse(data.isValid())
+    }
+
+    @Test
+    fun isValid_invalidSourceId() {
+        val data = PrivateBroadcastReceiveData(
+            sink = sink,
+            sourceId = -1,
+            broadcastId = 2,
+            state = LocalBluetoothLeBroadcastSourceState.STREAMING
+        )
+        assertFalse(data.isValid())
+    }
+
+    @Test
+    fun isValid_invalidBroadcastId() {
+        val data = PrivateBroadcastReceiveData(
+            sink = sink,
+            sourceId = 1,
+            broadcastId = -1,
+            state = LocalBluetoothLeBroadcastSourceState.STREAMING
+        )
+        assertFalse(data.isValid())
+    }
+
+    @Test
+    fun isValid_nullState() {
+        val data = PrivateBroadcastReceiveData(
+            sink = sink,
+            sourceId = 1,
+            broadcastId = 2,
+            state = null
+        )
+        assertFalse(data.isValid())
+    }
+
+    @Test
+    fun isValid_correctStates() {
+        assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.STREAMING).isValid())
+        assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.PAUSED).isValid())
+        assertTrue(PrivateBroadcastReceiveData(sink, 1, 1, state = LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED).isValid())
+    }
+
+    private companion object {
+        const val TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"
+
+        val sink: BluetoothDevice =
+            BluetoothAdapter.getDefaultAdapter().getRemoteLeDevice(
+                TEST_DEVICE_ADDRESS,
+                BluetoothDevice.ADDRESS_TYPE_RANDOM
+            )
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 3d16d6f..c387d48b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -507,4 +507,25 @@
         assertThat(mPhoneMediaDevice.getSelectionBehavior()).isEqualTo(
                 SELECTION_BEHAVIOR_TRANSFER);
     }
+
+    @Test
+    public void getSelectionBehavior_withRouteListingPreferenceItem_returnPreferenceBehavior() {
+        mItem =
+                new RouteListingPreference.Item.Builder(DEVICE_ADDRESS_1)
+                        .setSelectionBehavior(SELECTION_BEHAVIOR_GO_TO_APP)
+                        .build();
+        MediaDevice castMediaDevice = new ComplexMediaDevice(mContext, mRouteInfo1, mItem);
+
+        assertThat(castMediaDevice.hasRouteListingPreferenceItem()).isTrue();
+        assertThat(castMediaDevice.getSelectionBehavior()).isEqualTo(SELECTION_BEHAVIOR_GO_TO_APP);
+    }
+
+    @Test
+    public void getSelectionBehavior_withoutRouteListingPreferenceItem_returnTransfer() {
+        MediaDevice castMediaDevice =
+                new ComplexMediaDevice(mContext, mRouteInfo1, /* item= */ null);
+
+        assertThat(castMediaDevice.hasRouteListingPreferenceItem()).isFalse();
+        assertThat(castMediaDevice.getSelectionBehavior()).isEqualTo(SELECTION_BEHAVIOR_TRANSFER);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/supervision/OWNERS b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/supervision/OWNERS
new file mode 100644
index 0000000..04e7058
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/supervision/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:/core/java/android/app/supervision/OWNERS
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/supervision/SupervisionIntentProviderTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/supervision/SupervisionIntentProviderTest.kt
index 2ceed28..83ffa93 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/supervision/SupervisionIntentProviderTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/supervision/SupervisionIntentProviderTest.kt
@@ -19,6 +19,7 @@
 import android.app.supervision.SupervisionManager
 import android.content.Context
 import android.content.ContextWrapper
+import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -77,8 +78,8 @@
     fun getSettingsIntent_unresolvedIntent() {
         `when`(mockSupervisionManager.activeSupervisionAppPackage)
             .thenReturn(SUPERVISION_APP_PACKAGE)
-        `when`(mockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), anyInt()))
-            .thenReturn(emptyList())
+        `when`(mockPackageManager.queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()))
+            .thenReturn(emptyList<ResolveInfo>())
 
         val intent = SupervisionIntentProvider.getSettingsIntent(context)
 
@@ -89,7 +90,7 @@
     fun getSettingsIntent_resolvedIntent() {
         `when`(mockSupervisionManager.activeSupervisionAppPackage)
             .thenReturn(SUPERVISION_APP_PACKAGE)
-        `when`(mockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), anyInt()))
+        `when`(mockPackageManager.queryIntentActivitiesAsUser(any<Intent>(), anyInt(), anyInt()))
             .thenReturn(listOf(ResolveInfo()))
 
         val intent = SupervisionIntentProvider.getSettingsIntent(context)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/supervision/SupervisionRestrictionsHelperTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/supervision/SupervisionRestrictionsHelperTest.kt
new file mode 100644
index 0000000..872fc2a
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/supervision/SupervisionRestrictionsHelperTest.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.supervision
+
+import android.app.admin.DeviceAdminReceiver
+import android.app.supervision.SupervisionManager
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.content.pm.ServiceInfo
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatcher
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.argThat
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+/**
+ * Unit tests for [SupervisionRestrictionsHelper].
+ *
+ * Run with `atest SupervisionRestrictionsHelperTest`.
+ */
+@RunWith(AndroidJUnit4::class)
+class SupervisionRestrictionsHelperTest {
+    @get:Rule val mocks: MockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var mockPackageManager: PackageManager
+
+    @Mock private lateinit var mockSupervisionManager: SupervisionManager
+
+    private lateinit var context: Context
+
+    @Before
+    fun setUp() {
+        context =
+            object : ContextWrapper(InstrumentationRegistry.getInstrumentation().context) {
+                override fun getPackageManager() = mockPackageManager
+
+                override fun getSystemService(name: String) =
+                    when (name) {
+                        Context.SUPERVISION_SERVICE -> mockSupervisionManager
+                        else -> super.getSystemService(name)
+                    }
+            }
+    }
+
+    @Test
+    fun createEnforcedAdmin_nullSupervisionPackage() {
+        `when`(mockSupervisionManager.activeSupervisionAppPackage).thenReturn(null)
+
+        val enforcedAdmin =
+            SupervisionRestrictionsHelper.createEnforcedAdmin(context, RESTRICTION, USER_HANDLE)
+
+        assertThat(enforcedAdmin).isNull()
+    }
+
+    @Test
+    fun createEnforcedAdmin_supervisionAppService() {
+        val resolveInfo =
+            ResolveInfo().apply {
+                serviceInfo =
+                    ServiceInfo().apply {
+                        packageName = SUPERVISION_APP_PACKAGE
+                        name = "service.class"
+                    }
+            }
+
+        `when`(mockSupervisionManager.activeSupervisionAppPackage)
+            .thenReturn(SUPERVISION_APP_PACKAGE)
+        `when`(
+                mockPackageManager.queryIntentServicesAsUser(
+                    argThat(hasAction("android.app.action.BIND_SUPERVISION_APP_SERVICE")),
+                    anyInt(),
+                    eq(USER_ID),
+                )
+            )
+            .thenReturn(listOf(resolveInfo))
+
+        val enforcedAdmin =
+            SupervisionRestrictionsHelper.createEnforcedAdmin(context, RESTRICTION, USER_HANDLE)
+
+        assertThat(enforcedAdmin).isNotNull()
+        assertThat(enforcedAdmin!!.component).isEqualTo(resolveInfo.serviceInfo.componentName)
+        assertThat(enforcedAdmin.enforcedRestriction).isEqualTo(RESTRICTION)
+        assertThat(enforcedAdmin.user).isEqualTo(USER_HANDLE)
+    }
+
+    @Test
+    fun createEnforcedAdmin_profileOwnerReceiver() {
+        val resolveInfo =
+            ResolveInfo().apply {
+                activityInfo =
+                    ActivityInfo().apply {
+                        packageName = SUPERVISION_APP_PACKAGE
+                        name = "service.class"
+                    }
+            }
+
+        `when`(mockSupervisionManager.activeSupervisionAppPackage)
+            .thenReturn(SUPERVISION_APP_PACKAGE)
+        `when`(mockPackageManager.queryIntentServicesAsUser(any<Intent>(), anyInt(), eq(USER_ID)))
+            .thenReturn(emptyList<ResolveInfo>())
+        `when`(
+                mockPackageManager.queryBroadcastReceiversAsUser(
+                    argThat(hasAction(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED)),
+                    anyInt(),
+                    eq(USER_ID),
+                )
+            )
+            .thenReturn(listOf(resolveInfo))
+
+        val enforcedAdmin =
+            SupervisionRestrictionsHelper.createEnforcedAdmin(context, RESTRICTION, USER_HANDLE)
+
+        assertThat(enforcedAdmin).isNotNull()
+        assertThat(enforcedAdmin!!.component).isEqualTo(resolveInfo.activityInfo.componentName)
+        assertThat(enforcedAdmin.enforcedRestriction).isEqualTo(RESTRICTION)
+        assertThat(enforcedAdmin.user).isEqualTo(USER_HANDLE)
+    }
+
+    @Test
+    fun createEnforcedAdmin_noSupervisionComponent() {
+        `when`(mockSupervisionManager.activeSupervisionAppPackage)
+            .thenReturn(SUPERVISION_APP_PACKAGE)
+        `when`(mockPackageManager.queryIntentServicesAsUser(any<Intent>(), anyInt(), anyInt()))
+            .thenReturn(emptyList<ResolveInfo>())
+        `when`(mockPackageManager.queryBroadcastReceiversAsUser(any<Intent>(), anyInt(), anyInt()))
+            .thenReturn(emptyList<ResolveInfo>())
+
+        val enforcedAdmin =
+            SupervisionRestrictionsHelper.createEnforcedAdmin(context, RESTRICTION, USER_HANDLE)
+
+        assertThat(enforcedAdmin).isNotNull()
+        assertThat(enforcedAdmin!!.component).isNull()
+        assertThat(enforcedAdmin.enforcedRestriction).isEqualTo(RESTRICTION)
+        assertThat(enforcedAdmin.user).isEqualTo(USER_HANDLE)
+    }
+
+    private fun hasAction(action: String) =
+        object : ArgumentMatcher<Intent> {
+            override fun matches(intent: Intent?) = intent?.action == action
+        }
+
+    private companion object {
+        const val SUPERVISION_APP_PACKAGE = "app.supervision"
+        const val RESTRICTION = "restriction"
+        val USER_HANDLE = UserHandle.CURRENT
+        val USER_ID = USER_HANDLE.identifier
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserActivityTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserActivityTest.java
index f58eb7c..220c03e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserActivityTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserActivityTest.java
@@ -65,23 +65,23 @@
     }
 
     @Test
-    public void onTouchEvent_dismissesDialogAndCancelsResult() {
+    public void onTouchEvent_finishesActivityAndCancelsResult() {
         mCreateUserActivity.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0,
                 0));
 
-        assertThat(mCreateUserActivity.mSetupUserDialog.isShowing()).isFalse();
+        assertThat(mCreateUserActivity.isFinishing()).isTrue();
         assertThat(shadowOf(mCreateUserActivity).getResultCode())
                 .isEqualTo(Activity.RESULT_CANCELED);
     }
 
     @Test
-    public void setSuccessResult_dismissesDialogAndSetsSuccessResult() {
+    public void setSuccessResult_finishesActivityAndSetsSuccessResult() {
         Drawable mockDrawable = mock(Drawable.class);
 
         mCreateUserActivity.setSuccessResult(TEST_USER_NAME, mockDrawable, TEST_USER_ICON_PATH,
                 TEST_IS_ADMIN);
 
-        assertThat(mCreateUserActivity.mSetupUserDialog.isShowing()).isFalse();
+        assertThat(mCreateUserActivity.isFinishing()).isTrue();
         assertThat(shadowOf(mCreateUserActivity).getResultCode()).isEqualTo(Activity.RESULT_OK);
 
         Intent resultIntent = shadowOf(mCreateUserActivity).getResultIntent();
@@ -92,10 +92,10 @@
     }
 
     @Test
-    public void cancel_dismissesDialogAndSetsCancelResult() {
+    public void cancel_finishesActivityAndSetsCancelResult() {
         mCreateUserActivity.cancel();
 
-        assertThat(mCreateUserActivity.mSetupUserDialog.isShowing()).isFalse();
+        assertThat(mCreateUserActivity.isFinishing()).isTrue();
         assertThat(shadowOf(mCreateUserActivity).getResultCode())
                 .isEqualTo(Activity.RESULT_CANCELED);
     }
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index dafcc72..94aa955 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -187,6 +187,9 @@
     <!-- Default state of tap to wake -->
     <bool name="def_double_tap_to_wake">true</bool>
 
+    <!-- Default setting for double tap to sleep (Settings.Secure.DOUBLE_TAP_TO_SLEEP) -->
+    <bool name="def_double_tap_to_sleep">false</bool>
+
     <!-- Default for Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT -->
     <string name="def_nfc_payment_component"></string>
 
@@ -338,4 +341,7 @@
 
     <!-- The default ringer mode. See `AudioManager` for list of valid values. -->
     <integer name="def_ringer_mode">2</integer>
+
+    <!-- Caps minsum contrast from -1.0 (Material API) to 0.0 (Android Support)-->
+    <bool name="config_increaseMinContrast">true</bool>
 </resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index fc61b1e..d3291b4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -118,6 +118,8 @@
         Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
         Settings.Global.Wearable.WRIST_DETECTION_AUTO_LOCKING_ENABLED,
         Settings.Global.Wearable.AUTO_BEDTIME_MODE,
+        Settings.Global.Wearable.GESTURE_PRIMARY_ACTION_USER_PREFERENCE,
+        Settings.Global.Wearable.GESTURE_DISMISS_ACTION_USER_PREFERENCE,
         Settings.Global.FORCE_ENABLE_PSS_PROFILING,
         Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_ENABLED,
         Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_TYPE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index c010529..829d4cb 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -85,6 +85,7 @@
         Settings.Secure.MOUNT_UMS_PROMPT,
         Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED,
         Settings.Secure.DOUBLE_TAP_TO_WAKE,
+        Settings.Secure.DOUBLE_TAP_TO_SLEEP,
         Settings.Secure.WAKE_GESTURE_ENABLED,
         Settings.Secure.LONG_PRESS_TIMEOUT,
         Settings.Secure.KEY_REPEAT_ENABLED,
@@ -220,6 +221,8 @@
         Settings.Secure.EMERGENCY_GESTURE_ENABLED,
         Settings.Secure.EMERGENCY_GESTURE_SOUND_ENABLED,
         Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
+        Settings.Secure.ADAPTIVE_CONNECTIVITY_WIFI_ENABLED,
+        Settings.Secure.ADAPTIVE_CONNECTIVITY_MOBILE_NETWORK_ENABLED,
         Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
         Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
         Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
@@ -229,6 +232,7 @@
         Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
         Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
+        Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
         Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ENABLED,
@@ -266,6 +270,7 @@
         Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED,
         Settings.Secure.HUB_MODE_TUTORIAL_STATE,
         Settings.Secure.GLANCEABLE_HUB_ENABLED,
+        Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
         Settings.Secure.STYLUS_BUTTONS_ENABLED,
         Settings.Secure.STYLUS_HANDWRITING_ENABLED,
         Settings.Secure.DEFAULT_NOTE_TASK_PROFILE,
@@ -293,5 +298,11 @@
         Settings.Secure.FINGERPRINT_APP_ENABLED,
         Settings.Secure.FINGERPRINT_KEYGUARD_ENABLED,
         Settings.Secure.DUAL_SHADE,
+        Settings.Secure.BROWSER_CONTENT_FILTERS_ENABLED,
+        Settings.Secure.SEARCH_CONTENT_FILTERS_ENABLED,
+        Settings.Secure.SPELL_CHECKER_ENABLED,
+        Settings.Secure.SELECTED_SPELL_CHECKER,
+        // SELECTED_SPELL_CHECKER_SUBTYPE needs to be restored after SELECTED_SPELL_CHECKER
+        Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index cf0447f..98f5fac 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -124,7 +124,8 @@
                 Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
                 Settings.System.NOTIFICATION_COOLDOWN_ALL,
                 Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
-                Settings.System.PREFERRED_REGION
+                Settings.System.PREFERRED_REGION,
+                Settings.System.CV_ENABLED
         ));
         if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
             settings.add(Settings.System.PEAK_REFRESH_RATE);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 4c6a1ba..cd6521f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -474,5 +474,7 @@
                                 String.valueOf(
                                         Global.Wearable.STATUS_TRAY_CONFIGURATION_SYSTEM_HIDDEN)
                         }));
+        VALIDATORS.put(Global.Wearable.GESTURE_PRIMARY_ACTION_USER_PREFERENCE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.GESTURE_DISMISS_ACTION_USER_PREFERENCE, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 0ffdf53..d0f8462 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -131,6 +131,7 @@
         VALIDATORS.put(Secure.MOUNT_UMS_PROMPT, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.MOUNT_UMS_NOTIFY_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DOUBLE_TAP_TO_WAKE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.DOUBLE_TAP_TO_SLEEP, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.WAKE_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.LONG_PRESS_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.KEY_REPEAT_ENABLED, BOOLEAN_VALIDATOR);
@@ -323,6 +324,10 @@
                         Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL));
         VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE,
+                new InclusiveIntegerRangeValidator(
+                        Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CONTINUOUS,
+                        Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_EDGE));
         VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
@@ -353,6 +358,8 @@
         VALIDATORS.put(
                 Secure.EMERGENCY_GESTURE_UI_LAST_STARTED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
         VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_WIFI_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_MOBILE_NETWORK_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
                 Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
@@ -429,6 +436,8 @@
         VALIDATORS.put(Secure.DND_CONFIGS_MIGRATED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.HUB_MODE_TUTORIAL_STATE, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.GLANCEABLE_HUB_ENABLED, new InclusiveIntegerRangeValidator(0, 1));
+        VALIDATORS.put(Secure.WHEN_TO_START_GLANCEABLE_HUB,
+                new InclusiveIntegerRangeValidator(0, 3));
         VALIDATORS.put(Secure.STYLUS_BUTTONS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.STYLUS_HANDWRITING_ENABLED,
                 new DiscreteValueValidator(new String[] {"-1", "0", "1"}));
@@ -461,5 +470,10 @@
         VALIDATORS.put(Secure.FINGERPRINT_APP_ENABLED,  BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.FINGERPRINT_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DUAL_SHADE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.BROWSER_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.SEARCH_CONTENT_FILTERS_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.SPELL_CHECKER_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.SELECTED_SPELL_CHECKER, NULLABLE_COMPONENT_NAME_VALIDATOR);
+        VALIDATORS.put(Secure.SELECTED_SPELL_CHECKER_SUBTYPE, ANY_INTEGER_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 4f649ed..3a58440 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -271,5 +271,7 @@
         VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.PREFERRED_REGION, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(System.CV_ENABLED,
+                new InclusiveIntegerRangeValidator(0, 1));
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
index b0086c1..78c87b3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
+++ b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
@@ -1,2 +1,2 @@
-per-file WritableNamespacePrefixes.java = mpgroover@google.com,tedbauer@google.com
-per-file WritableNamespaces.java = mpgroover@google.com,tedbauer@google.com
+per-file WritableNamespacePrefixes.java = mpgroover@google.com
+per-file WritableNamespaces.java = mpgroover@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index fc402d4..37ada93 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -37,10 +37,12 @@
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupRestoreEventLogger;
 import android.app.backup.FullBackupDataOutput;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
 import android.database.Cursor;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
@@ -941,6 +943,7 @@
         Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
 
         int restoredSettingsCount = 0;
+        boolean selectedSpellCheckerRestored = false;
         for (String key : allowlist.mSettingsAllowlist) {
             boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
             if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri,  key)) {
@@ -1068,6 +1071,25 @@
                     }
                     continue;
                 }
+            } else if (Settings.Secure.SELECTED_SPELL_CHECKER.equals(key)) {
+                ServiceInfo si = getServiceInfoOrNull(value);
+                if (si == null || si.applicationInfo == null) {
+                    Log.i(TAG, "Skipping restore for setting selected_spell_checker "
+                            + "as it is not installed");
+                    continue;
+                } else if (!si.applicationInfo.isSystemApp()
+                        && !si.applicationInfo.isUpdatedSystemApp()) {
+                    Log.i(TAG, "Skipping restore for setting selected_spell_checker "
+                            + "as it is not a system app");
+                    continue;
+                }
+                selectedSpellCheckerRestored = true;
+            } else if (Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE.equals(key)) {
+                if (!selectedSpellCheckerRestored) {
+                    Log.i(TAG, "Skipping restore for setting selected_spell_checker_subtype "
+                            + "as selected_spell_checker was not restored");
+                    continue;
+                }
             }
 
             if (Settings.System.FONT_SCALE.equals(key)) {
@@ -1085,6 +1107,21 @@
                 Log.d(TAG, "Restored font scale from: " + toRestore + " to " + value);
             }
 
+            if (Settings.Secure.CONTRAST_LEVEL.equals(key)) {
+                boolean increaseMinContrast = getBaseContext().getResources()
+                        .getBoolean(R.bool.config_increaseMinContrast);
+
+                float valueFloat;
+                try {
+                    valueFloat = Float.parseFloat(value);
+                } catch (NumberFormatException e) {
+                    valueFloat = 0.0f;
+                }
+
+                float newValue = Math.max(valueFloat, increaseMinContrast ? 0.0f : -1.0f);
+                value = String.valueOf(newValue);
+            }
+
             settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
                     mRestoredFromSdkInt);
 
@@ -1868,6 +1905,18 @@
         return result;
     }
 
+    @Nullable
+    private ServiceInfo getServiceInfoOrNull(@Nullable String flattenedServiceName) {
+        if (flattenedServiceName == null) return null;
+        ComponentName componentName = ComponentName.unflattenFromString(flattenedServiceName);
+        if (componentName == null) return null;
+        try {
+            return getBaseContext().getPackageManager().getServiceInfo(componentName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+    }
+
     /**
      * Store the allowlist of settings to be backed up and validators for them.
      */
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e07832e..584b21a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1881,6 +1881,10 @@
                 SecureSettingsProto.Accessibility
                         .ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED);
         dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE,
+                SecureSettingsProto.Accessibility
+                        .ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE);
+        dumpSetting(s, p,
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
                 SecureSettingsProto.Accessibility
                         .ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED);
@@ -2107,6 +2111,12 @@
         dumpSetting(s, p,
                 Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
                 SecureSettingsProto.ADAPTIVE_CONNECTIVITY_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_WIFI_ENABLED,
+                SecureSettingsProto.ADAPTIVE_CONNECTIVITY_WIFI_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_MOBILE_NETWORK_ENABLED,
+                SecureSettingsProto.ADAPTIVE_CONNECTIVITY_MOBILE_NETWORK_ENABLED);
 
         final long controlsToken = p.start(SecureSettingsProto.CONTROLS);
         dumpSetting(s, p,
@@ -3147,6 +3157,12 @@
                 SystemSettingsProto.Volume.MASTER_BALANCE);
         p.end(volumeToken);
 
+        final long systemDisplayToken = p.start(SystemSettingsProto.DISPLAY);
+        dumpSetting(s, p,
+                Settings.System.CV_ENABLED,
+                SystemSettingsProto.Display.CV_ENABLED);
+        p.end(systemDisplayToken);
+
         dumpSetting(s, p,
                 Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
                 SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4a225bd..65ede9d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4080,7 +4080,7 @@
 
         @VisibleForTesting
         final class UpgradeController {
-            private static final int SETTINGS_VERSION = 227;
+            private static final int SETTINGS_VERSION = 228;
 
             private final int mUserId;
 
@@ -6319,6 +6319,23 @@
                     currentVersion = 227;
                 }
 
+                // Version 227: Add default value for DOUBLE_TAP_TO_SLEEP.
+                if (currentVersion == 227) {
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting doubleTapToSleep = secureSettings.getSettingLocked(
+                            Settings.Secure.DOUBLE_TAP_TO_SLEEP);
+                    if (doubleTapToSleep.isNull()) {
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
+                                Settings.Secure.DOUBLE_TAP_TO_SLEEP,
+                                getContext().getResources().getBoolean(
+                                        R.bool.def_double_tap_to_sleep) ? "1" : "0",
+                                null /* tag */,
+                                true /* makeDefault */,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 228;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 70c042c..7179cbd 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -567,6 +567,7 @@
                     Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
                     Settings.Global.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, // cache per hearing device
                     Settings.Global.HEARING_DEVICE_LOCAL_NOTIFICATION, // cache per hearing device
+                    Settings.Global.REDACT_OTP_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS,
                     Settings.Global.Wearable.COMBINED_LOCATION_ENABLE,
                     Settings.Global.Wearable.HAS_PAY_TOKENS,
                     Settings.Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN,
@@ -749,15 +750,12 @@
                  Settings.Secure.SECURE_FRP_MODE,
                  Settings.Secure.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT,
                  Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
-                 Settings.Secure.SELECTED_SPELL_CHECKER,  // Intentionally removed in Q
-                 Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE,  // Intentionally removed in Q
                  Settings.Secure.SETTINGS_CLASSNAME,
                  Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, // candidate?
                  Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
                  Settings.Secure.SKIP_FIRST_USE_HINTS, // candidate?
                  Settings.Secure.SLEEP_TIMEOUT,
                  Settings.Secure.SMS_DEFAULT_APPLICATION,
-                 Settings.Secure.SPELL_CHECKER_ENABLED,  // Intentionally removed in Q
                  Settings.Secure.TRUST_AGENTS_INITIALIZED,
                  Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED,
                  Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index 48c360b..bc727d3 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -16,15 +16,15 @@
 
 package com.android.providers.settings;
 
-import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_NEW_CONFIG;
-import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG;
 import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SIM_SPECIFIC_SETTINGS_2;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_NEW_CONFIG;
 import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_SETTINGS_BACKUP_DATA;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -35,12 +35,10 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.when;
 
-import android.annotation.Nullable;
 import android.app.backup.BackupAnnotations.BackupDestination;
 import android.app.backup.BackupAnnotations.OperationType;
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupRestoreEventLogger;
 import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -69,13 +67,11 @@
 
 import com.android.window.flags.Flags;
 
-import java.util.List;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
@@ -331,6 +327,47 @@
     }
 
     @Test
+    public void testOnRestore_minContrastLevelIsRestoredToZero() {
+        mAgentUnderTest = new TestFriendlySettingsBackupAgent() {
+            @Override
+            protected Set<String> getBlockedSettings(int blockedSettingsArrayId) {
+                return new HashSet<>();
+            }
+        };
+        mAgentUnderTest.attach(mContext);
+
+        TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+        mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+        String contrastLevelValue = "-1.0";
+        Map<String, String> settingsToRestore = Map.of(Settings.Secure.CONTRAST_LEVEL,
+                contrastLevelValue);
+
+        byte[] backupData = generateBackupData(settingsToRestore);
+        mAgentUnderTest.restoreSettings(
+                backupData,
+                /* pos */ 0,
+                backupData.length,
+                Settings.Secure.CONTENT_URI,
+                null,
+                null,
+                null,
+                /* blockedSettingsArrayId */ 0,
+                Collections.emptySet(),
+                Collections.emptySet(),
+                KEY_SECURE);
+
+        // Check that the contrast level has been restored.
+        assertTrue(settingsHelper.mWrittenValues.containsKey(Settings.Secure.CONTRAST_LEVEL));
+
+        String restoredContrastLevel = settingsHelper.mWrittenValues.get(
+                Settings.Secure.CONTRAST_LEVEL);
+
+        float restoredFloat = Float.parseFloat(restoredContrastLevel);
+        assertEquals(0.0f, restoredFloat, 0.001f);
+    }
+
+    @Test
     @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
     public void onCreate_metricsFlagIsDisabled_areAgentMetricsEnabledIsFalse() {
         mAgentUnderTest.onCreate();
diff --git a/packages/Shell/res/values-et/strings.xml b/packages/Shell/res/values-et/strings.xml
index 48c7334..c18687f 100644
--- a/packages/Shell/res/values-et/strings.xml
+++ b/packages/Shell/res/values-et/strings.xml
@@ -21,7 +21,7 @@
     <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Luuakse veaaruannet <xliff:g id="ID">#%d</xliff:g>"</string>
     <string name="bugreport_finished_title" msgid="4429132808670114081">"Jäädvustati veaaruanne <xliff:g id="ID">#%d</xliff:g>"</string>
     <string name="bugreport_updating_title" msgid="4423539949559634214">"Üksikasjade lisamine veaaruandesse"</string>
-    <string name="bugreport_updating_wait" msgid="3322151947853929470">"Oodake …"</string>
+    <string name="bugreport_updating_wait" msgid="3322151947853929470">"Palun oodake…"</string>
     <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Veaaruanne kuvatakse telefonis peagi"</string>
     <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Veaaruande jagamiseks valige"</string>
     <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Veaaruande jagamiseks puudutage"</string>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index fb0678f..5bba99f 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -255,11 +255,11 @@
     /** 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();
 
+/** Minimum delay between percentage points before sending an update notification */
+    private static final int MIN_NOTIFICATION_GAP = 10;
+
     /** Managed bugreport info (keyed by id) */
     @GuardedBy("mLock")
     private final SparseArray<BugreportInfo> mBugreportInfos = new SparseArray<>();
@@ -1460,17 +1460,6 @@
      * Sends a notification indicating the bugreport has finished so use can share it.
      */
     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);
 
@@ -1523,7 +1512,7 @@
             builder.setSubText(info.getName());
         }
 
-        Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
+        Log.d(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
         NotificationManager.from(mContext).notify(info.id, builder.build());
     }
 
@@ -2753,6 +2742,11 @@
         if (progress > CAPPED_PROGRESS) {
             progress = CAPPED_PROGRESS;
         }
+
+        if ((progress - info.lastProgress.intValue()) < MIN_NOTIFICATION_GAP) {
+            return;
+        }
+
         if (DEBUG) {
             if (progress != info.progress.intValue()) {
                 Log.v(TAG, "Updating progress for name " + info.getName() + "(id: " + info.id
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java b/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java
index 433eca2..8031b10 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportProgressServiceTest.java
@@ -20,7 +20,7 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
 
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
 import android.accounts.Account;
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5b48566..129949f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -534,6 +534,7 @@
         "androidx.compose.animation_animation-graphics",
         "androidx.lifecycle_lifecycle-viewmodel-compose",
         "kairos",
+        "displaylib",
         "aconfig_settings_flags_lib",
     ],
     libs: [
@@ -728,6 +729,7 @@
         "Traceur-res",
         "aconfig_settings_flags_lib",
         "kairos",
+        "displaylib",
     ],
 }
 
@@ -770,6 +772,7 @@
         "androidx.compose.runtime_runtime",
         "kairos",
         "kosmos",
+        "displaylib",
         "testables",
         "androidx.test.rules",
         "platform-compat-test-rules",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 72ae76a..f628a42 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -312,6 +312,9 @@
     <!-- Permission necessary to change car audio volume through CarAudioManager -->
     <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
 
+    <!-- To detect when projecting to Android Auto -->
+    <uses-permission android:name="android.permission.READ_PROJECTION_STATE" />
+
     <!-- Permission to control Android Debug Bridge (ADB) -->
     <uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
 
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 1362ffe..86559fd 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -1,22 +1,6 @@
 {
-  // Curious where your @Scenario tests are running?
-  //
-  // @Ignore: Will not run in any configuration
-  //
-  // @FlakyTest: Tests that don't block pre/postsubmit but are staged to run known failures.
-  //             Tests will run in postsubmit on sysui-e2e-staged suite.
-  //
-  //
-  // @PlatinumTest: Marking your test with this annotation will put your tests in presubmit.
-  //                Please DO NOT annotate new or old tests with @PlatinumTest annotation
-  //                without discussing with mdb:android-platinum
-  //
-  // @Postsubmit: Do not use this annotation for e2e tests. This won't have any affect.
-
-  // For all other e2e tests which are not platinum, they run in sysui-silver suite,that
-  // primarily runs in postsubmit with an exception to e2e test related changes.
-  // If you want to see one shot place to monitor all e2e tests, look for
-  // sysui-e2e-staged suite.
+  // Test mappings for SystemUI unit tests.
+  // For e2e mappings, see go/sysui-e2e-test-mapping
 
   // v2/android-virtual-infra/test_mapping/presubmit-avd
   "presubmit": [
diff --git a/packages/SystemUI/TEST_OWNERS b/packages/SystemUI/TEST_OWNERS
index eadc86e..21faf03 100644
--- a/packages/SystemUI/TEST_OWNERS
+++ b/packages/SystemUI/TEST_OWNERS
@@ -3,3 +3,6 @@
 # for restructuring and test maintenance only
 
 saff@google.com
+
+# Work around per-file set noparent includes
+per-file *=saff@google.com
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml
index db733fc..afb337d 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="accessibility_menu_service_name" msgid="730136711554740131">"תפריט נגישות"</string>
+    <string name="accessibility_menu_service_name" msgid="730136711554740131">"תפריט הנגישות"</string>
     <string name="accessibility_menu_intro" msgid="3164193281544042394">"תפריט הנגישות הוא תפריט גדול שמופיע במסך ומאפשר לשלוט במכשיר. אפשר לנעול את המכשיר, לשלוט בעוצמת הקול ובבהירות, לצלם צילומי מסך ועוד."</string>
     <string name="assistant_label" msgid="6796392082252272356">"Assistant"</string>
     <string name="assistant_utterance" msgid="65509599221141377">"Assistant"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
index 7172619..1543dbe 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -46,7 +46,7 @@
 import android.media.AudioManager;
 import android.os.PowerManager;
 import android.os.UserManager;
-import android.platform.uiautomator_helpers.WaitUtils;
+import android.platform.uiautomatorhelpers.WaitUtils;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Display;
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 3cb3025..c6bc1c7 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -155,3 +155,13 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "hearing_devices_input_routing_ui_improvement"
+    namespace: "accessibility"
+    description: "UI improvement for hearing device input routing feature"
+    bug: "397314200"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig
index 89a0d89..dd9a8b1 100644
--- a/packages/SystemUI/aconfig/predictive_back.aconfig
+++ b/packages/SystemUI/aconfig/predictive_back.aconfig
@@ -9,8 +9,11 @@
 }
 
 flag {
-    name: "predictive_back_delay_transition"
+    name: "predictive_back_delay_wm_transition"
     namespace: "systemui"
     description: "Slightly delays the back transition start"
     bug: "301195601"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
 }
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 9db4346..7cfd3e7 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -213,18 +213,6 @@
 }
 
 flag {
-    name: "notification_undo_guts_on_config_changed"
-    namespace: "systemui"
-    description: "Fixes a bug where a theme or font change while notification guts were open"
-        " (e.g. the snooze options or notification info) would show an empty notification by"
-        " closing the guts and undoing changes."
-    bug: "379267630"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
    name: "pss_app_selector_recents_split_screen"
    namespace: "systemui"
    description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
@@ -292,6 +280,17 @@
 }
 
 flag {
+  name: "notification_row_accessibility_expanded"
+  namespace: "systemui"
+  description: "Prepare ExpandableNotificationRow for new A11y expansion APIs."
+  bug: "380027122"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+
+flag {
     name: "scene_container"
     namespace: "systemui"
     description: "Enables the scene container framework go/flexiglass."
@@ -1390,6 +1389,16 @@
 }
 
 flag {
+    name: "media_controls_device_manager_background_execution"
+    namespace: "systemui"
+    description: "Sends some instances creation to background thread"
+    bug: "400200474"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
   name: "output_switcher_redesign"
   namespace: "systemui"
   description: "Enables visual update for Media Output Switcher"
@@ -1825,6 +1834,16 @@
 }
 
 flag {
+  name: "disable_blurred_shade_visible"
+  namespace: "systemui"
+  description: "Removes the check for a blur radius when determining shade window visibility"
+  bug: "394977231"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
     name: "notification_shade_blur"
     namespace: "systemui"
     description: "Enables the new blur effect on the Notification Shade."
@@ -2110,3 +2129,21 @@
     description: "Enables moving the launching window on top of the origin window in the Animation library."
     bug: "390422470"
 }
+
+flag {
+    name: "status_bar_chips_return_animations"
+    namespace: "systemui"
+    description: "Enables return animations for status bar chips"
+    bug: "202516970"
+}
+
+flag {
+    name: "media_projection_grey_error_text"
+    namespace: "systemui"
+    description: "Set the error text color to grey when app sharing is hidden by the requesting app"
+    bug: "390624334"
+    metadata {
+       purpose: PURPOSE_BUGFIX
+    }
+}
+
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index e43b8a0..7ee6a6e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -107,6 +107,16 @@
      */
     // TODO(b/301385865): Remove this flag.
     private val disableWmTimeout: Boolean = false,
+
+    /**
+     * Whether we should disable the reparent transaction that puts the opening/closing window above
+     * the view's window. This should be set to true in tests only, where we can't currently use a
+     * valid leash.
+     *
+     * TODO(b/397180418): Remove this flag when we don't have the RemoteAnimation wrapper anymore
+     *   and we can just inject a fake transaction.
+     */
+    private val skipReparentTransaction: Boolean = false,
 ) {
     @JvmOverloads
     constructor(
@@ -1140,6 +1150,7 @@
                     DelegatingAnimationCompletionListener(listener, this::dispose),
                     transitionAnimator,
                     disableWmTimeout,
+                    skipReparentTransaction,
                 )
         }
 
@@ -1173,6 +1184,16 @@
          */
         // TODO(b/301385865): Remove this flag.
         disableWmTimeout: Boolean = false,
+
+        /**
+         * Whether we should disable the reparent transaction that puts the opening/closing window
+         * above the view's window. This should be set to true in tests only, where we can't
+         * currently use a valid leash.
+         *
+         * TODO(b/397180418): Remove this flag when we don't have the RemoteAnimation wrapper
+         *   anymore and we can just inject a fake transaction.
+         */
+        private val skipReparentTransaction: Boolean = false,
     ) : RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> {
         private val transitionContainer = controller.transitionContainer
         private val context = transitionContainer.context
@@ -1515,7 +1536,7 @@
                             )
                         }
 
-                        if (moveTransitionAnimationLayer()) {
+                        if (moveTransitionAnimationLayer() && !skipReparentTransaction) {
                             // Ensure that the launching window is rendered above the view's window,
                             // so it is not obstructed.
                             // TODO(b/397180418): re-use the start transaction once the
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
index 9a74687..e07d7b3 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
@@ -16,17 +16,18 @@
 
 package com.android.systemui.animation
 
+import kotlin.text.buildString
+
 class FontVariationUtils {
     private var mWeight = -1
     private var mWidth = -1
     private var mOpticalSize = -1
     private var mRoundness = -1
-    private var isUpdated = false
+    private var mCurrentFVar = ""
 
     /*
      * generate fontVariationSettings string, used for key in typefaceCache in TextAnimator
      * the order of axes should align to the order of parameters
-     * if every axis remains unchanged, return ""
      */
     fun updateFontVariation(
         weight: Int = -1,
@@ -34,15 +35,17 @@
         opticalSize: Int = -1,
         roundness: Int = -1,
     ): String {
-        isUpdated = false
+        var isUpdated = false
         if (weight >= 0 && mWeight != weight) {
             isUpdated = true
             mWeight = weight
         }
+
         if (width >= 0 && mWidth != width) {
             isUpdated = true
             mWidth = width
         }
+
         if (opticalSize >= 0 && mOpticalSize != opticalSize) {
             isUpdated = true
             mOpticalSize = opticalSize
@@ -52,23 +55,32 @@
             isUpdated = true
             mRoundness = roundness
         }
-        var resultString = ""
-        if (mWeight >= 0) {
-            resultString += "'${GSFAxes.WEIGHT.tag}' $mWeight"
+
+        if (!isUpdated) {
+            return mCurrentFVar
         }
-        if (mWidth >= 0) {
-            resultString +=
-                (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.WIDTH.tag}' $mWidth"
-        }
-        if (mOpticalSize >= 0) {
-            resultString +=
-                (if (resultString.isBlank()) "" else ", ") +
-                    "'${GSFAxes.OPTICAL_SIZE.tag}' $mOpticalSize"
-        }
-        if (mRoundness >= 0) {
-            resultString +=
-                (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.ROUND.tag}' $mRoundness"
-        }
-        return if (isUpdated) resultString else ""
+
+        return buildString {
+                if (mWeight >= 0) {
+                    if (!isBlank()) append(", ")
+                    append("'${GSFAxes.WEIGHT.tag}' $mWeight")
+                }
+
+                if (mWidth >= 0) {
+                    if (!isBlank()) append(", ")
+                    append("'${GSFAxes.WIDTH.tag}' $mWidth")
+                }
+
+                if (mOpticalSize >= 0) {
+                    if (!isBlank()) append(", ")
+                    append("'${GSFAxes.OPTICAL_SIZE.tag}' $mOpticalSize")
+                }
+
+                if (mRoundness >= 0) {
+                    if (!isBlank()) append(", ")
+                    append("'${GSFAxes.ROUND.tag}' $mRoundness")
+                }
+            }
+            .also { mCurrentFVar = it }
     }
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index 694fc8e..21ec896 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -22,6 +22,7 @@
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX;
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
@@ -244,6 +245,17 @@
                 runner.onAnimationCancelled();
                 finishRunnable.run();
             }
+
+            @Override
+            public void onTransitionConsumed(IBinder transition, boolean aborted)
+                    throws RemoteException {
+                // Notify the remote runner that the transition has been canceled if the transition
+                // was merged into another transition or aborted
+                synchronized (mFinishRunnables) {
+                    mFinishRunnables.remove(transition);
+                }
+                runner.onAnimationCancelled();
+            }
         };
     }
 
@@ -261,7 +273,8 @@
             SurfaceControl.Transaction startTransaction
     ) {
         checkArgument(isOpeningMode(launcherChange.getMode()));
-        if (!isClosingType(info.getType())) {
+        if (!isClosingType(info.getType())
+                && !ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX.isTrue()) {
             return;
         }
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 4a39cff..4f01d7f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -82,6 +82,10 @@
     }
 }
 
+interface TextAnimatorListener : TextInterpolatorListener {
+    fun onInvalidate() {}
+}
+
 /**
  * This class provides text animation between two styles.
  *
@@ -110,13 +114,19 @@
 class TextAnimator(
     layout: Layout,
     private val typefaceCache: TypefaceVariantCache,
-    private val invalidateCallback: () -> Unit = {},
+    private val listener: TextAnimatorListener? = null,
 ) {
-    @VisibleForTesting var textInterpolator = TextInterpolator(layout, typefaceCache)
+    var textInterpolator = TextInterpolator(layout, typefaceCache, listener)
     @VisibleForTesting var createAnimator: () -> ValueAnimator = { ValueAnimator.ofFloat(1f) }
 
     var animator: ValueAnimator? = null
 
+    val progress: Float
+        get() = textInterpolator.progress
+
+    val linearProgress: Float
+        get() = textInterpolator.linearProgress
+
     val fontVariationUtils = FontVariationUtils()
 
     sealed class PositionedGlyph {
@@ -288,8 +298,9 @@
             animator = buildAnimator(animation).apply { start() }
         } else {
             textInterpolator.progress = 1f
+            textInterpolator.linearProgress = 1f
             textInterpolator.rebase()
-            invalidateCallback()
+            listener?.onInvalidate()
         }
     }
 
@@ -302,7 +313,7 @@
             addUpdateListener {
                 textInterpolator.progress = it.animatedValue as Float
                 textInterpolator.linearProgress = it.currentPlayTime / it.duration.toFloat()
-                invalidateCallback()
+                listener?.onInvalidate()
             }
 
             addListener(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 457f453..22c5258 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -27,8 +27,19 @@
 import com.android.internal.graphics.ColorUtils
 import java.lang.Math.max
 
+interface TextInterpolatorListener {
+    fun onPaintModified() {}
+
+    fun onRebased() {}
+}
+
 /** Provide text style linear interpolation for plain text. */
-class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache) {
+class TextInterpolator(
+    layout: Layout,
+    var typefaceCache: TypefaceVariantCache,
+    private val listener: TextInterpolatorListener? = null,
+) {
+
     /**
      * Returns base paint used for interpolation.
      *
@@ -136,6 +147,7 @@
      */
     fun onTargetPaintModified() {
         updatePositionsAndFonts(shapeText(layout, targetPaint), updateBase = false)
+        listener?.onPaintModified()
     }
 
     /**
@@ -146,6 +158,7 @@
      */
     fun onBasePaintModified() {
         updatePositionsAndFonts(shapeText(layout, basePaint), updateBase = true)
+        listener?.onPaintModified()
     }
 
     /**
@@ -204,6 +217,7 @@
      */
     fun rebase() {
         if (progress == 0f) {
+            listener?.onRebased()
             return
         } else if (progress == 1f) {
             basePaint.set(targetPaint)
@@ -233,6 +247,8 @@
         }
 
         progress = 0f
+        linearProgress = 0f
+        listener?.onRebased()
     }
 
     /**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index 5d9c441..a4a96d1 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -1006,13 +1006,32 @@
             Log.d(TAG, "Animation ended")
         }
 
-        // TODO(b/330672236): Post this to the main thread instead so that it does not
-        // flicker with Flexiglass enabled.
-        controller.onTransitionAnimationEnd(isExpandingFullyAbove)
-        transitionContainerOverlay.remove(windowBackgroundLayer)
+        val onEnd = {
+            controller.onTransitionAnimationEnd(isExpandingFullyAbove)
+            transitionContainerOverlay.remove(windowBackgroundLayer)
 
-        if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
-            openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+            if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
+                openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+            }
+        }
+        // TODO(b/330672236): Post this to the main thread for launches as well, so that they do not
+        //   flicker with Flexiglass enabled.
+        if (controller.isLaunching) {
+            onEnd()
+        } else {
+            // onAnimationEnd is called at the end of the animation, on a Choreographer animation
+            // tick. During dialog launches, the following calls will move the animated content from
+            // the dialog overlay back to its original position, and this change must be reflected
+            // in the next frame given that we then sync the next frame of both the content and
+            // dialog ViewRoots. During SysUI activity launches, we will instantly collapse the
+            // shade at the end of the transition. However, if those are rendered by Compose, whose
+            // compositions are also scheduled on a Choreographer frame, any state change made
+            // *right now* won't be reflected in the next frame given that a Choreographer frame
+            // can't schedule another and have it happen in the same frame. So we post the forwarded
+            // calls to [Controller.onLaunchAnimationEnd] in the main executor, leaving this
+            // Choreographer frame, ensuring that any state change applied by
+            // onTransitionAnimationEnd() will be reflected in the same frame.
+            mainExecutor.execute { onEnd() }
         }
     }
 
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RunBlockingDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RunBlockingDetector.kt
new file mode 100644
index 0000000..fce536a
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RunBlockingDetector.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *            http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UFile
+import org.jetbrains.uast.UImportStatement
+
+/** Detects whether [runBlocking] is being imported. */
+class RunBlockingDetector : Detector(), SourceCodeScanner {
+
+    override fun getApplicableUastTypes(): List<Class<out UElement>> {
+        return listOf(UFile::class.java)
+    }
+
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        return object : UElementHandler() {
+            override fun visitFile(node: UFile) {
+                for (importStatement in node.imports) {
+                    visitImportStatement(context, importStatement)
+                }
+            }
+        }
+    }
+
+    private fun visitImportStatement(context: JavaContext, importStatement: UImportStatement) {
+        val importName = importStatement.importReference?.asSourceString()
+        if (FORBIDDEN_IMPORTS.contains(importName)) {
+            context.report(
+                ISSUE,
+                importStatement as UElement,
+                context.getLocation(importStatement),
+                "Importing $importName is not allowed.",
+            )
+        }
+    }
+
+    companion object {
+        @JvmField
+        val ISSUE =
+            Issue.create(
+                id = "RunBlockingUsage",
+                briefDescription = "Discouraged runBlocking call",
+                explanation =
+                    """
+                    Using `runBlocking` is generally discouraged in Android
+                    development as it can lead to UI freezes and ANRs.
+                    Consider using `launch` or `async` with coroutine scope
+                    instead. If needed from java, consider introducing a method
+                    with a callback instead from kotlin.
+                    """,
+                category = Category.PERFORMANCE,
+                priority = 8,
+                severity = Severity.WARNING,
+                implementation =
+                    Implementation(RunBlockingDetector::class.java, Scope.JAVA_FILE_SCOPE),
+            )
+
+        val FORBIDDEN_IMPORTS = listOf("kotlinx.coroutines.runBlocking")
+    }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index adb3116..b455c00 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -50,6 +50,7 @@
                 ShadeDisplayAwareDialogDetector.ISSUE,
                 RegisterContentObserverSyncViaSettingsProxyDetector.SYNC_WARNING,
                 RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR,
+                RunBlockingDetector.ISSUE,
             )
 
     override val api: Int
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RunBlockingDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RunBlockingDetectorTest.kt
new file mode 100644
index 0000000..4ae429d2
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RunBlockingDetectorTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *            http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class RunBlockingDetectorTest : SystemUILintDetectorTest() {
+
+    override fun getDetector(): Detector = RunBlockingDetector()
+
+    override fun getIssues(): List<Issue> = listOf(RunBlockingDetector.ISSUE)
+
+    @Test
+    fun testViolation() {
+        lint()
+            .files(
+                kotlin(
+                    """
+                    package com.example
+
+                    import kotlinx.coroutines.runBlocking
+
+                    class MyClass {
+                        fun myMethod() {
+                            runBlocking {
+                                // Some code here
+                            }
+                        }
+                    }
+                    """
+                ),
+                RUN_BLOCKING_DEFINITION,
+            )
+            .issues(RunBlockingDetector.ISSUE)
+            .run()
+            .expect(
+                """
+src/com/example/MyClass.kt:4: Warning: Importing kotlinx.coroutines.runBlocking is not allowed. [RunBlockingUsage]
+                    import kotlinx.coroutines.runBlocking
+                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+0 errors, 1 warnings
+"""
+                    .trimIndent()
+            )
+    }
+
+    // Verifies that the lint check does *not* flag calls to other methods.
+    @Test
+    fun testNotViolation() {
+        lint()
+            .detector(RunBlockingDetector())
+            .issues(RunBlockingDetector.ISSUE)
+            .files(
+                kotlin(
+                    """
+                    package com.example
+
+                    class MyClass {
+                        fun myMethod() {
+                            myOtherMethod {
+                            }
+                        }
+
+                        fun myOtherMethod(block: () -> Unit) {
+                            block()
+                        }
+                    }
+                    """
+                )
+            )
+            .run()
+            .expectClean()
+    }
+
+    private companion object {
+        val RUN_BLOCKING_DEFINITION =
+            kotlin(
+                """
+                    package kotlinx.coroutines
+
+                    fun runBlocking(block: suspend () -> Unit) {
+                        // Implementation details don't matter for this test.
+                    }
+                    """
+            )
+    }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index a352b1e..82e5f5b 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -21,7 +21,6 @@
 import android.view.ViewGroup
 import android.view.ViewGroupOverlay
 import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -62,6 +61,7 @@
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.graphics.drawscope.scale
 import androidx.compose.ui.graphics.drawscope.translate
+import androidx.compose.ui.graphics.isSpecified
 import androidx.compose.ui.graphics.layer.GraphicsLayer
 import androidx.compose.ui.graphics.layer.drawLayer
 import androidx.compose.ui.graphics.rememberGraphicsLayer
@@ -82,6 +82,7 @@
 import androidx.lifecycle.setViewTreeViewModelStoreOwner
 import androidx.savedstate.findViewTreeSavedStateRegistryOwner
 import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.android.compose.modifiers.animatedBackground
 import com.android.compose.modifiers.thenIf
 import com.android.compose.ui.graphics.FullScreenComposeViewInOverlay
 import com.android.systemui.animation.ComposableControllerFactory
@@ -291,7 +292,7 @@
                     .updateExpandableSize()
                     .then(minInteractiveSizeModifier)
                     .then(clickModifier(controller, onClick, interactionSource))
-                    .background(color, shape)
+                    .animatedBackground(color, shape = shape)
                     .border(controller)
                     .onGloballyPositioned { controller.boundsInComposeViewRoot = it.boundsInRoot() }
             ) {
@@ -307,19 +308,27 @@
     contentColor: Color,
     content: @Composable (Expandable) -> Unit,
 ) {
-    CompositionLocalProvider(LocalContentColor provides contentColor) {
-        // We make sure that the content itself (wrapped by the background) is at least 40.dp, which
-        // is the same as the M3 buttons. This applies even if onClick is null, to make it easier to
-        // write expandables that are sometimes clickable and sometimes not. There shouldn't be any
-        // Expandable smaller than 40dp because if the expandable is not clickable directly, then
-        // something in its content should be (and with a size >= 40dp).
-        val minSize = 40.dp
-        Box(
-            Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
-            contentAlignment = Alignment.Center,
-        ) {
-            content(expandable)
+    val minSizeContent =
+        @Composable {
+            // We make sure that the content itself (wrapped by the background) is at least 40.dp,
+            // which is the same as the M3 buttons. This applies even if onClick is null, to make it
+            // easier to write expandables that are sometimes clickable and sometimes not. There
+            // shouldn't be any Expandable smaller than 40dp because if the expandable is not
+            // clickable directly, then something in its content should be (and with a size >=
+            // 40dp).
+            val minSize = 40.dp
+            Box(
+                Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+                contentAlignment = Alignment.Center,
+            ) {
+                content(expandable)
+            }
         }
+
+    if (contentColor.isSpecified) {
+        CompositionLocalProvider(LocalContentColor provides contentColor, content = minSizeContent)
+    } else {
+        minSizeContent()
     }
 }
 
@@ -345,7 +354,7 @@
         .thenIf(drawContent) {
             Modifier.border(controller)
                 .then(clickModifier(controller, onClick, interactionSource))
-                .background(controller.color, controller.shape)
+                .animatedBackground(controller.color, shape = controller.shape)
         }
         .onPlaced { controller.boundsInComposeViewRoot = it.boundsInRoot() }
         .drawWithContent {
@@ -422,7 +431,7 @@
             // Background.
             this@draw.drawBackground(
                 state,
-                controller.color,
+                controller.color(),
                 controller.borderStroke,
                 size = Size(state.width.toFloat(), state.height.toFloat()),
             )
@@ -469,7 +478,7 @@
 /** Draw [content] in [overlay] while respecting its screen position given by [animatorState]. */
 @Composable
 private fun AnimatedContentInOverlay(
-    color: Color,
+    color: () -> Color,
     sizeInOriginalLayout: Size,
     overlay: ViewGroupOverlay,
     controller: ExpandableControllerImpl,
@@ -523,7 +532,7 @@
                                     return@drawWithContent
                                 }
 
-                                drawBackground(animatorState, color, controller.borderStroke)
+                                drawBackground(animatorState, color(), controller.borderStroke)
                                 drawContent()
                             },
                             // We center the content in the expanding container.
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 72da175e..d7d5a48 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -26,7 +26,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -80,6 +79,24 @@
     borderStroke: BorderStroke? = null,
     transitionControllerFactory: ComposableControllerFactory? = null,
 ): ExpandableController {
+    return rememberExpandableController(
+        color = { color },
+        shape = shape,
+        contentColor = contentColor,
+        borderStroke = borderStroke,
+        transitionControllerFactory = transitionControllerFactory,
+    )
+}
+
+/** Create an [ExpandableController] to control an [Expandable]. */
+@Composable
+fun rememberExpandableController(
+    color: () -> Color,
+    shape: Shape,
+    contentColor: Color = Color.Unspecified,
+    borderStroke: BorderStroke? = null,
+    transitionControllerFactory: ComposableControllerFactory? = null,
+): ExpandableController {
     val composeViewRoot = LocalView.current
     val density = LocalDensity.current
     val layoutDirection = LocalLayoutDirection.current
@@ -125,7 +142,7 @@
 }
 
 internal class ExpandableControllerImpl(
-    internal val color: Color,
+    internal val color: () -> Color,
     internal val contentColor: Color,
     internal val shape: Shape,
     internal val borderStroke: BorderStroke?,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index 959f28f..362748e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -95,13 +95,41 @@
      * nested scrollable.
      *
      * This is called whenever a nested scrollable does not consume some scroll amount. If this
-     * returns `true`, then [onDragStarted] will be called and this draggable will have priority and
+     * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
      * consume all future events during preScroll until the nested scroll is finished.
      */
-    fun shouldConsumeNestedScroll(sign: Float): Boolean
+    fun shouldConsumeNestedPostScroll(sign: Float): Boolean = true
+
+    /**
+     * Whether this draggable should consume any scroll amount with the given [sign] *before* it can
+     * be consumed by a nested scrollable.
+     *
+     * This is called before a nested scrollable is able to consume that scroll amount. If this
+     * returns `true`, then [onDragStarted] will be called, this draggable will have priority and
+     * consume all future scroll events during preScroll until the nested scroll is finished.
+     */
+    fun shouldConsumeNestedPreScroll(sign: Float): Boolean = false
 
     interface Controller {
         /**
+         * Whether this controller is ready to drag. [onDrag] will be called only if this returns
+         * `true`, and any drag event will be ignored until then.
+         *
+         * This can for instance be used to wait for the content we are dragging to to be composed
+         * before actually dragging, reducing perceivable jank at the beginning of a drag.
+         */
+        val isReadyToDrag: Boolean
+            get() = true
+
+        /**
+         * Whether drags that were started from nested scrolls should be automatically
+         * [stopped][onDragStopped] as soon as they don't consume the entire `delta` passed to
+         * [onDrag].
+         */
+        val autoStopNestedDrags: Boolean
+            get() = false
+
+        /**
          * Drag by [delta] pixels.
          *
          * @return the consumed [delta]. Any non-consumed delta will be dispatched to the next
@@ -256,6 +284,9 @@
     /** The pointers currently down, in order of which they were done and mapping to their type. */
     private val pointersDown = linkedMapOf<PointerId, PointerType>()
 
+    /** Whether the next drag event should be ignored. */
+    private var ignoreNextDrag = false
+
     init {
         delegate(nestedScrollModifierNode(this, nestedScrollDispatcher))
     }
@@ -408,6 +439,7 @@
         velocityTracker: VelocityTracker,
     ) {
         velocityTracker.addPointerInputChange(change)
+        if (shouldIgnoreDrag(controller)) return
 
         scrollWithOverscroll(delta.toOffset()) { deltaFromOverscroll ->
             scrollWithNestedScroll(deltaFromOverscroll) { deltaFromNestedScroll ->
@@ -416,6 +448,23 @@
         }
     }
 
+    private fun shouldIgnoreDrag(controller: NestedDraggable.Controller): Boolean {
+        return when {
+            !controller.isReadyToDrag -> {
+                // The controller is not ready yet, so we are waiting for an expensive frame to be
+                // composed. We should ignore this drag and the next one, given that the first delta
+                // after an expensive frame will be large.
+                ignoreNextDrag = true
+                true
+            }
+            ignoreNextDrag -> {
+                ignoreNextDrag = false
+                true
+            }
+            else -> false
+        }
+    }
+
     private fun onDragStopped(controller: NestedDraggable.Controller, velocity: Velocity) {
         // We launch in the scope of the dispatcher so that the fling is not cancelled if this node
         // is removed right after onDragStopped() is called.
@@ -540,6 +589,14 @@
     }
 
     override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+        val sign = available.toFloat().sign
+        maybeCreateNewController(
+            sign = sign,
+            condition = {
+                source == NestedScrollSource.UserInput &&
+                    draggable.shouldConsumeNestedPreScroll(sign)
+            },
+        )
         val controller = nestedScrollController ?: return Offset.Zero
         return scrollWithOverscroll(controller, available)
     }
@@ -560,33 +617,48 @@
         }
 
         val sign = offset.sign
-        if (
-            nestedDragsEnabled &&
-                nestedScrollController == null &&
-                // TODO(b/388231324): Remove this.
-                !lastEventWasScrollWheel &&
-                draggable.shouldConsumeNestedScroll(sign) &&
-                lastFirstDown != null
-        ) {
-            val startedPosition = checkNotNull(lastFirstDown)
-
-            // TODO(b/382665591): Ensure that there is at least one pointer down.
-            val pointersDownCount = pointersDown.size.coerceAtLeast(1)
-            val pointerType = pointersDown.entries.firstOrNull()?.value
-            nestedScrollController =
-                NestedScrollController(
-                    overscrollEffect,
-                    draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
-                )
-        }
-
+        maybeCreateNewController(
+            sign,
+            condition = { draggable.shouldConsumeNestedPostScroll(sign) },
+        )
         val controller = nestedScrollController ?: return Offset.Zero
         return scrollWithOverscroll(controller, available)
     }
 
+    private fun maybeCreateNewController(sign: Float, condition: () -> Boolean) {
+        if (
+            !nestedDragsEnabled ||
+                nestedScrollController != null ||
+                lastEventWasScrollWheel ||
+                lastFirstDown == null ||
+                !condition()
+        ) {
+            return
+        }
+
+        // TODO(b/382665591): Ensure that there is at least one pointer down.
+        val pointersDownCount = pointersDown.size.coerceAtLeast(1)
+        val pointerType = pointersDown.entries.firstOrNull()?.value
+        val startedPosition = checkNotNull(lastFirstDown)
+        nestedScrollController =
+            NestedScrollController(
+                overscrollEffect,
+                draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
+            )
+    }
+
     private fun scrollWithOverscroll(controller: NestedScrollController, offset: Offset): Offset {
-        return scrollWithOverscroll(offset) {
-            controller.controller.onDrag(it.toFloat()).toOffset()
+        if (shouldIgnoreDrag(controller.controller)) return offset
+
+        return scrollWithOverscroll(offset) { delta ->
+            val available = delta.toFloat()
+            val consumed = controller.controller.onDrag(available)
+            if (controller.controller.autoStopNestedDrags && consumed != available) {
+                controller.ensureOnDragStoppedIsCalled()
+                this.nestedScrollController = null
+            }
+
+            consumed.toOffset()
         }
     }
 
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt
new file mode 100644
index 0000000..5b1f0a7
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt
@@ -0,0 +1,159 @@
+/*
+ * 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.compose.modifiers
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.drawOutline
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ObserverModifierNode
+import androidx.compose.ui.node.invalidateDraw
+import androidx.compose.ui.node.observeReads
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.LayoutDirection
+
+/**
+ * Draws a background in a given [shape] and with a [color] or [alpha] that can be animated.
+ *
+ * @param color color to paint background with
+ * @param alpha alpha of the background
+ * @param shape desired shape of the background
+ */
+fun Modifier.animatedBackground(
+    color: () -> Color,
+    alpha: () -> Float = DefaultAlpha,
+    shape: Shape = RectangleShape,
+) =
+    this.then(
+        BackgroundElement(
+            color = color,
+            alpha = alpha,
+            shape = shape,
+            inspectorInfo =
+                debugInspectorInfo {
+                    name = "background"
+                    value = color
+                    properties["color"] = color
+                    properties["alpha"] = alpha
+                    properties["shape"] = shape
+                },
+        )
+    )
+
+private val DefaultAlpha = { 1f }
+
+private class BackgroundElement(
+    private val color: () -> Color,
+    private val alpha: () -> Float,
+    private val shape: Shape,
+    private val inspectorInfo: InspectorInfo.() -> Unit,
+) : ModifierNodeElement<BackgroundNode>() {
+    override fun create(): BackgroundNode {
+        return BackgroundNode(color, alpha, shape)
+    }
+
+    override fun update(node: BackgroundNode) {
+        node.color = color
+        node.alpha = alpha
+        node.shape = shape
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        inspectorInfo()
+    }
+
+    override fun hashCode(): Int {
+        var result = color.hashCode()
+        result = 31 * result + alpha.hashCode()
+        result = 31 * result + shape.hashCode()
+        return result
+    }
+
+    override fun equals(other: Any?): Boolean {
+        val otherModifier = other as? BackgroundElement ?: return false
+        return color == otherModifier.color &&
+            alpha == otherModifier.alpha &&
+            shape == otherModifier.shape
+    }
+}
+
+private class BackgroundNode(var color: () -> Color, var alpha: () -> Float, var shape: Shape) :
+    DrawModifierNode, Modifier.Node(), ObserverModifierNode {
+
+    // Naively cache outline calculation if input parameters are the same, we manually observe
+    // reads inside shape#createOutline separately
+    private var lastSize: Size = Size.Unspecified
+    private var lastLayoutDirection: LayoutDirection? = null
+    private var lastOutline: Outline? = null
+    private var lastShape: Shape? = null
+    private var tmpOutline: Outline? = null
+
+    override fun ContentDrawScope.draw() {
+        if (shape === RectangleShape) {
+            // shortcut to avoid Outline calculation and allocation
+            drawRect()
+        } else {
+            drawOutline()
+        }
+        drawContent()
+    }
+
+    override fun onObservedReadsChanged() {
+        // Reset cached properties
+        lastSize = Size.Unspecified
+        lastLayoutDirection = null
+        lastOutline = null
+        lastShape = null
+        // Invalidate draw so we build the cache again - this is needed because observeReads within
+        // the draw scope obscures the state reads from the draw scope's observer
+        invalidateDraw()
+    }
+
+    private fun ContentDrawScope.drawRect() {
+        drawRect(color = color(), alpha = alpha())
+    }
+
+    private fun ContentDrawScope.drawOutline() {
+        val outline = getOutline()
+        drawOutline(outline, color = color(), alpha = alpha())
+    }
+
+    private fun ContentDrawScope.getOutline(): Outline {
+        val outline: Outline?
+        if (size == lastSize && layoutDirection == lastLayoutDirection && lastShape == shape) {
+            outline = lastOutline!!
+        } else {
+            // Manually observe reads so we can directly invalidate the outline when it changes
+            // Use tmpOutline to avoid creating an object reference to local var outline
+            observeReads { tmpOutline = shape.createOutline(size, layoutDirection, this) }
+            outline = tmpOutline
+            tmpOutline = null
+        }
+        lastOutline = outline
+        lastSize = size
+        lastLayoutDirection = layoutDirection
+        lastShape = shape
+        return outline!!
+    }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
deleted file mode 100644
index 794b7a4..0000000
--- a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
+++ /dev/null
@@ -1,113 +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.compose.modifiers
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.DrawModifier
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.drawOutline
-import androidx.compose.ui.graphics.drawscope.ContentDrawScope
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.unit.LayoutDirection
-
-/**
- * Draws a fading [shape] with a solid [color] and [alpha] behind the content.
- *
- * @param color color to paint background with
- * @param alpha alpha of the background
- * @param shape desired shape of the background
- */
-fun Modifier.fadingBackground(color: Color, alpha: () -> Float, shape: Shape = RectangleShape) =
-    this.then(
-        FadingBackground(
-            brush = SolidColor(color),
-            alpha = alpha,
-            shape = shape,
-            inspectorInfo =
-                debugInspectorInfo {
-                    name = "background"
-                    value = color
-                    properties["color"] = color
-                    properties["alpha"] = alpha
-                    properties["shape"] = shape
-                },
-        )
-    )
-
-private class FadingBackground
-constructor(
-    private val brush: Brush,
-    private val shape: Shape,
-    private val alpha: () -> Float,
-    inspectorInfo: InspectorInfo.() -> Unit,
-) : DrawModifier, InspectorValueInfo(inspectorInfo) {
-    // naive cache outline calculation if size is the same
-    private var lastSize: Size? = null
-    private var lastLayoutDirection: LayoutDirection? = null
-    private var lastOutline: Outline? = null
-
-    override fun ContentDrawScope.draw() {
-        if (shape === RectangleShape) {
-            // shortcut to avoid Outline calculation and allocation
-            drawRect()
-        } else {
-            drawOutline()
-        }
-        drawContent()
-    }
-
-    private fun ContentDrawScope.drawRect() {
-        drawRect(brush, alpha = alpha())
-    }
-
-    private fun ContentDrawScope.drawOutline() {
-        val outline =
-            if (size == lastSize && layoutDirection == lastLayoutDirection) {
-                lastOutline!!
-            } else {
-                shape.createOutline(size, layoutDirection, this)
-            }
-        drawOutline(outline, brush = brush, alpha = alpha())
-        lastOutline = outline
-        lastSize = size
-        lastLayoutDirection = layoutDirection
-    }
-
-    override fun hashCode(): Int {
-        var result = brush.hashCode()
-        result = 31 * result + alpha.hashCode()
-        result = 31 * result + shape.hashCode()
-        return result
-    }
-
-    override fun equals(other: Any?): Boolean {
-        val otherModifier = other as? FadingBackground ?: return false
-        return brush == otherModifier.brush &&
-            alpha == otherModifier.alpha &&
-            shape == otherModifier.shape
-    }
-
-    override fun toString(): String = "FadingBackground(brush=$brush, alpha = $alpha, shape=$shape)"
-}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInContainer.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInContainer.kt
index d08d859..fc4d53a 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInContainer.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInContainer.kt
@@ -22,7 +22,6 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Path
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
@@ -32,7 +31,6 @@
 import androidx.compose.ui.graphics.layer.GraphicsLayer
 import androidx.compose.ui.graphics.layer.drawLayer
 import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.onPlaced
 import androidx.compose.ui.layout.positionInWindow
 import androidx.compose.ui.modifier.ModifierLocalModifierNode
 import androidx.compose.ui.node.DrawModifierNode
@@ -50,11 +48,7 @@
  * The elements redirected to this container will be drawn above the content of this composable.
  */
 fun Modifier.container(state: ContainerState): Modifier {
-    return onPlaced { state.lastOffsetInWindow = it.positionInWindow() }
-        .drawWithContent {
-            drawContent()
-            state.drawInOverlay(this)
-        }
+    return this then ContainerElement(state)
 }
 
 /**
@@ -105,6 +99,30 @@
     fun drawInOverlay(drawScope: DrawScope)
 }
 
+private data class ContainerElement(private val state: ContainerState) :
+    ModifierNodeElement<ContainerNode>() {
+    override fun create(): ContainerNode {
+        return ContainerNode(state)
+    }
+
+    override fun update(node: ContainerNode) {
+        node.state = state
+    }
+}
+
+/** A node implementing [container] that can be delegated to. */
+class ContainerNode(var state: ContainerState) :
+    Modifier.Node(), LayoutAwareModifierNode, DrawModifierNode {
+    override fun onPlaced(coordinates: LayoutCoordinates) {
+        state.lastOffsetInWindow = coordinates.positionInWindow()
+    }
+
+    override fun ContentDrawScope.draw() {
+        drawContent()
+        state.drawInOverlay(this)
+    }
+}
+
 private data class DrawInContainerElement(
     var state: ContainerState,
     var enabled: () -> Boolean,
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
index d8e46ad..b316173 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -971,6 +971,98 @@
         assertThat(availableToEffectPostFling).isWithin(1f).of(100f)
     }
 
+    @Test
+    fun isReadyToDrag() {
+        var isReadyToDrag by mutableStateOf(false)
+        val draggable = TestDraggable(isReadyToDrag = { isReadyToDrag })
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+            }
+
+        rule.onRoot().performTouchInput {
+            down(center)
+            moveBy((touchSlop + 10f).toOffset())
+        }
+
+        assertThat(draggable.onDragStartedCalled).isTrue()
+        assertThat(draggable.onDragDelta).isEqualTo(0f)
+
+        rule.onRoot().performTouchInput { moveBy(20f.toOffset()) }
+        assertThat(draggable.onDragDelta).isEqualTo(0f)
+
+        // Flag as ready to drag. We still ignore the next drag after that.
+        isReadyToDrag = true
+        rule.onRoot().performTouchInput { moveBy(30f.toOffset()) }
+        assertThat(draggable.onDragDelta).isEqualTo(0f)
+
+        // Now we drag.
+        rule.onRoot().performTouchInput { moveBy(40f.toOffset()) }
+        assertThat(draggable.onDragDelta).isEqualTo(40f)
+    }
+
+    @Test
+    fun consumeNestedPreScroll() {
+        var consumeNestedPreScroll by mutableStateOf(false)
+        val draggable = TestDraggable(shouldConsumeNestedPreScroll = { consumeNestedPreScroll })
+
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(
+                    Modifier.fillMaxSize()
+                        .nestedDraggable(draggable, orientation)
+                        // Always consume everything so that the only way to start the drag is to
+                        // intercept preScroll events.
+                        .scrollable(rememberScrollableState { it }, orientation)
+                )
+            }
+
+        rule.onRoot().performTouchInput {
+            down(center)
+            moveBy((touchSlop + 1f).toOffset())
+        }
+
+        assertThat(draggable.onDragStartedCalled).isFalse()
+
+        consumeNestedPreScroll = true
+        rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }
+
+        assertThat(draggable.onDragStartedCalled).isTrue()
+    }
+
+    @Test
+    fun autoStopNestedDrags() {
+        var consumeScrolls by mutableStateOf(true)
+        val draggable =
+            TestDraggable(autoStopNestedDrags = true, onDrag = { if (consumeScrolls) it else 0f })
+
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(
+                    Modifier.fillMaxSize()
+                        .nestedDraggable(draggable, orientation)
+                        .scrollable(rememberScrollableState { 0f }, orientation)
+                )
+            }
+
+        rule.onRoot().performTouchInput {
+            down(center)
+            moveBy((touchSlop + 1f).toOffset())
+        }
+
+        assertThat(draggable.onDragStartedCalled).isTrue()
+        assertThat(draggable.onDragStoppedCalled).isFalse()
+
+        rule.onRoot().performTouchInput { moveBy(50f.toOffset()) }
+
+        assertThat(draggable.onDragStoppedCalled).isFalse()
+
+        consumeScrolls = false
+        rule.onRoot().performTouchInput { moveBy(1f.toOffset()) }
+
+        assertThat(draggable.onDragStoppedCalled).isTrue()
+    }
+
     private fun ComposeContentTestRule.setContentWithTouchSlop(
         content: @Composable () -> Unit
     ): Float {
@@ -996,7 +1088,10 @@
             { velocity, _ ->
                 velocity
             },
-        private val shouldConsumeNestedScroll: (Float) -> Boolean = { true },
+        private val shouldConsumeNestedPostScroll: (Float) -> Boolean = { true },
+        private val shouldConsumeNestedPreScroll: (Float) -> Boolean = { false },
+        private val isReadyToDrag: () -> Boolean = { true },
+        private val autoStopNestedDrags: Boolean = false,
     ) : NestedDraggable {
         var shouldStartDrag = true
         var onDragStartedCalled = false
@@ -1026,6 +1121,11 @@
 
             onDragStarted.invoke(position, sign)
             return object : NestedDraggable.Controller {
+                override val autoStopNestedDrags: Boolean = this@TestDraggable.autoStopNestedDrags
+
+                override val isReadyToDrag: Boolean
+                    get() = isReadyToDrag()
+
                 override fun onDrag(delta: Float): Float {
                     onDragCalled = true
                     onDragDelta += delta
@@ -1042,8 +1142,12 @@
             }
         }
 
-        override fun shouldConsumeNestedScroll(sign: Float): Boolean {
-            return shouldConsumeNestedScroll.invoke(sign)
+        override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
+            return shouldConsumeNestedPostScroll.invoke(sign)
+        }
+
+        override fun shouldConsumeNestedPreScroll(sign: Float): Boolean {
+            return shouldConsumeNestedPreScroll.invoke(sign)
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 4e1aab5..2b8fe39 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -47,6 +47,7 @@
 import com.android.compose.animation.scene.rememberMutableSceneTransitionLayoutState
 import com.android.compose.animation.scene.transitions
 import com.android.compose.modifiers.thenIf
+import com.android.systemui.Flags
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -104,7 +105,9 @@
             fade(Communal.Elements.Grid)
             fade(Communal.Elements.IndicationArea)
             fade(Communal.Elements.LockIcon)
-            fade(Communal.Elements.StatusBar)
+            if (!Flags.glanceableHubV2()) {
+                fade(Communal.Elements.StatusBar)
+            }
         }
         timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
     }
@@ -131,7 +134,9 @@
             fade(Communal.Elements.Grid)
             fade(Communal.Elements.IndicationArea)
             fade(Communal.Elements.LockIcon)
-            fade(Communal.Elements.StatusBar)
+            if (!Flags.glanceableHubV2()) {
+                fade(Communal.Elements.StatusBar)
+            }
         }
         timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
     }
@@ -182,7 +187,7 @@
         viewModel.communalBackground.collectAsStateWithLifecycle(
             initialValue = CommunalBackgroundType.ANIMATED
         )
-    val swipeToHubEnabled by viewModel.swipeToHubEnabled.collectAsStateWithLifecycle()
+    val swipeToHubEnabled by viewModel.swipeToHubEnabled.collectAsStateWithLifecycle(false)
     val state: MutableSceneTransitionLayoutState =
         rememberMutableSceneTransitionLayoutState(
             initialScene = currentSceneKey,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 2d03e2b..0181928 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
 import com.android.compose.animation.scene.ContentScope
+import com.android.systemui.Flags
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
 import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
@@ -70,8 +71,10 @@
                 content = {
                     Box(modifier = Modifier.fillMaxSize()) {
                         with(communalPopupSection) { Popup() }
-                        with(ambientStatusBarSection) {
-                            AmbientStatusBar(modifier = Modifier.fillMaxWidth().zIndex(1f))
+                        if (!Flags.glanceableHubV2()) {
+                            with(ambientStatusBarSection) {
+                                AmbientStatusBar(modifier = Modifier.fillMaxWidth().zIndex(1f))
+                            }
                         }
                         CommunalHub(
                             viewModel = viewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 8ad96a5..62b1342 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -77,6 +77,16 @@
         list.apply { add(toIndex, removeAt(fromIndex)) }
     }
 
+    /** Swap the two items in the list with the given indices. */
+    fun swapItems(index1: Int, index2: Int) {
+        list.apply {
+            val item1 = get(index1)
+            val item2 = get(index2)
+            set(index2, item1)
+            set(index1, item2)
+        }
+    }
+
     /** Remove widget from the list and the database. */
     fun onRemove(indexToRemove: Int) {
         if (list[indexToRemove].isWidgetContent()) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
index 0aef7f2..dda388a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
@@ -18,8 +18,10 @@
 
 import android.content.ClipDescription
 import android.view.DragEvent
+import androidx.compose.animation.core.tween
 import androidx.compose.foundation.draganddrop.dragAndDropTarget
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.animateScrollBy
 import androidx.compose.foundation.gestures.scrollBy
 import androidx.compose.foundation.lazy.grid.LazyGridState
 import androidx.compose.runtime.Composable
@@ -37,6 +39,7 @@
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.unit.dp
 import com.android.systemui.Flags.communalWidgetResizing
+import com.android.systemui.Flags.glanceableHubV2
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
 import com.android.systemui.communal.util.WidgetPickerIntentUtils
@@ -51,13 +54,14 @@
  * @see dragAndDropTarget
  */
 @Composable
-internal fun rememberDragAndDropTargetState(
+fun rememberDragAndDropTargetState(
     gridState: LazyGridState,
     contentOffset: Offset,
     contentListState: ContentListState,
 ): DragAndDropTargetState {
     val scope = rememberCoroutineScope()
     val autoScrollThreshold = with(LocalDensity.current) { 60.dp.toPx() }
+
     val state =
         remember(gridState, contentOffset, contentListState, autoScrollThreshold, scope) {
             DragAndDropTargetState(
@@ -68,11 +72,9 @@
                 scope = scope,
             )
         }
-    LaunchedEffect(state) {
-        for (diff in state.scrollChannel) {
-            gridState.scrollBy(diff)
-        }
-    }
+
+    LaunchedEffect(state) { state.processScrollRequests(scope) }
+
     return state
 }
 
@@ -83,7 +85,7 @@
  * @see DragEvent
  */
 @Composable
-internal fun Modifier.dragAndDropTarget(dragDropTargetState: DragAndDropTargetState): Modifier {
+fun Modifier.dragAndDropTarget(dragDropTargetState: DragAndDropTargetState): Modifier {
     val state by rememberUpdatedState(dragDropTargetState)
 
     return this then
@@ -132,13 +134,79 @@
  * other activities. [GridDragDropState] on the other hand, handles dragging of existing items in
  * the communal hub grid.
  */
-internal class DragAndDropTargetState(
+class DragAndDropTargetState(
+    state: LazyGridState,
+    contentOffset: Offset,
+    contentListState: ContentListState,
+    autoScrollThreshold: Float,
+    scope: CoroutineScope,
+) {
+    private val dragDropState: DragAndDropTargetStateInternal =
+        if (glanceableHubV2()) {
+            DragAndDropTargetStateV2(
+                state = state,
+                contentListState = contentListState,
+                scope = scope,
+                autoScrollThreshold = autoScrollThreshold,
+                contentOffset = contentOffset,
+            )
+        } else {
+            DragAndDropTargetStateV1(
+                state = state,
+                contentListState = contentListState,
+                scope = scope,
+                autoScrollThreshold = autoScrollThreshold,
+                contentOffset = contentOffset,
+            )
+        }
+
+    fun onStarted() = dragDropState.onStarted()
+
+    fun onMoved(event: DragAndDropEvent) = dragDropState.onMoved(event)
+
+    fun onDrop(event: DragAndDropEvent) = dragDropState.onDrop(event)
+
+    fun onEnded() = dragDropState.onEnded()
+
+    fun onExited() = dragDropState.onExited()
+
+    suspend fun processScrollRequests(coroutineScope: CoroutineScope) =
+        dragDropState.processScrollRequests(coroutineScope)
+}
+
+/**
+ * A private interface defining the API for handling drag-and-drop operations. There will be two
+ * implementations of this interface: V1 for devices that do not have the glanceable_hub_v2 flag
+ * enabled, and V2 for devices that do have that flag enabled.
+ *
+ * TODO(b/400789179): Remove this interface and the V1 implementation once glanceable_hub_v2 has
+ *   shipped.
+ */
+private interface DragAndDropTargetStateInternal {
+    fun onStarted() = Unit
+
+    fun onMoved(event: DragAndDropEvent) = Unit
+
+    fun onDrop(event: DragAndDropEvent): Boolean = false
+
+    fun onEnded() = Unit
+
+    fun onExited() = Unit
+
+    suspend fun processScrollRequests(coroutineScope: CoroutineScope) = Unit
+}
+
+/**
+ * The V1 implementation of DragAndDropTargetStateInternal to be used when the glanceable_hub_v2
+ * flag is disabled.
+ */
+private class DragAndDropTargetStateV1(
     private val state: LazyGridState,
     private val contentOffset: Offset,
     private val contentListState: ContentListState,
     private val autoScrollThreshold: Float,
     private val scope: CoroutineScope,
-) {
+) : DragAndDropTargetStateInternal {
     /**
      * The placeholder item that is treated as if it is being dragged across the grid. It is added
      * to grid once drag and drop event is started and removed when event ends.
@@ -147,15 +215,21 @@
     private var placeHolderIndex: Int? = null
     private var previousTargetItemKey: Any? = null
 
-    internal val scrollChannel = Channel<Float>()
+    private val scrollChannel = Channel<Float>()
 
-    fun onStarted() {
+    override suspend fun processScrollRequests(coroutineScope: CoroutineScope) {
+        for (diff in scrollChannel) {
+            state.scrollBy(diff)
+        }
+    }
+
+    override fun onStarted() {
         // assume item will be added to the end.
         contentListState.list.add(placeHolder)
         placeHolderIndex = contentListState.list.size - 1
     }
 
-    fun onMoved(event: DragAndDropEvent) {
+    override fun onMoved(event: DragAndDropEvent) {
         val dragOffset = event.toOffset()
 
         val targetItem =
@@ -201,7 +275,7 @@
         }
     }
 
-    fun onDrop(event: DragAndDropEvent): Boolean {
+    override fun onDrop(event: DragAndDropEvent): Boolean {
         return placeHolderIndex?.let { dropIndex ->
             val widgetExtra = event.maybeWidgetExtra() ?: return false
             val (componentName, user) = widgetExtra
@@ -219,13 +293,13 @@
         } ?: false
     }
 
-    fun onEnded() {
+    override fun onEnded() {
         placeHolderIndex = null
         previousTargetItemKey = null
         contentListState.list.remove(placeHolder)
     }
 
-    fun onExited() {
+    override fun onExited() {
         onEnded()
     }
 
@@ -257,16 +331,186 @@
             contentListState.onMove(currentIndex, index)
         }
     }
+}
 
+/**
+ * The V2 implementation of DragAndDropTargetStateInternal to be used when the glanceable_hub_v2
+ * flag is enabled.
+ */
+private class DragAndDropTargetStateV2(
+    private val state: LazyGridState,
+    private val contentOffset: Offset,
+    private val contentListState: ContentListState,
+    private val autoScrollThreshold: Float,
+    private val scope: CoroutineScope,
+) : DragAndDropTargetStateInternal {
     /**
-     * Parses and returns the intent extra associated with the widget that is dropped into the grid.
-     *
-     * Returns null if the drop event didn't include intent information.
+     * The placeholder item that is treated as if it is being dragged across the grid. It is added
+     * to grid once drag and drop event is started and removed when event ends.
      */
-    private fun DragAndDropEvent.maybeWidgetExtra(): WidgetPickerIntentUtils.WidgetExtra? {
-        val clipData = this.toAndroidDragEvent().clipData.takeIf { it.itemCount != 0 }
-        return clipData?.getItemAt(0)?.intent?.let { intent -> getWidgetExtraFromIntent(intent) }
+    private var placeHolder = CommunalContentModel.WidgetPlaceholder()
+    private var placeHolderIndex: Int? = null
+    private var previousTargetItemKey: Any? = null
+    private var dragOffset = Offset.Zero
+    private var columnWidth = 0
+
+    private val scrollChannel = Channel<Float>()
+
+    override suspend fun processScrollRequests(coroutineScope: CoroutineScope) {
+        while (true) {
+            val amount = scrollChannel.receive()
+
+            if (state.isScrollInProgress) {
+                // Ignore overscrolling if a scroll is already in progress (but we still want to
+                // consume the scroll event so that we don't end up processing a bunch of old
+                // events after scrolling has finished).
+                continue
+            }
+
+            // Perform the rest of the drag operation after scrolling has finished (or immediately
+            // if there will be no scrolling).
+            if (amount != 0f) {
+                scope.launch {
+                    state.animateScrollBy(amount, tween(delayMillis = 250, durationMillis = 1000))
+                    performDragAction()
+                }
+            } else {
+                performDragAction()
+            }
+        }
     }
 
-    private fun DragAndDropEvent.toOffset() = this.toAndroidDragEvent().run { Offset(x, y) }
+    override fun onStarted() {
+        // assume item will be added to the end.
+        contentListState.list.add(placeHolder)
+        placeHolderIndex = contentListState.list.size - 1
+
+        // Use the width of the first item as the column width.
+        columnWidth =
+            state.layoutInfo.visibleItemsInfo.first().size.width +
+                state.layoutInfo.beforeContentPadding +
+                state.layoutInfo.afterContentPadding
+    }
+
+    override fun onMoved(event: DragAndDropEvent) {
+        dragOffset = event.toOffset()
+        scrollChannel.trySend(computeAutoscroll(dragOffset))
+    }
+
+    override fun onDrop(event: DragAndDropEvent): Boolean {
+        return placeHolderIndex?.let { dropIndex ->
+            val widgetExtra = event.maybeWidgetExtra() ?: return false
+            val (componentName, user) = widgetExtra
+            if (componentName != null && user != null) {
+                // Placeholder isn't removed yet to allow the setting the right rank for items
+                // before adding in the new item.
+                contentListState.onSaveList(
+                    newItemComponentName = componentName,
+                    newItemUser = user,
+                    newItemIndex = dropIndex,
+                )
+                return@let true
+            }
+            return false
+        } ?: false
+    }
+
+    override fun onEnded() {
+        placeHolderIndex = null
+        previousTargetItemKey = null
+        contentListState.list.remove(placeHolder)
+    }
+
+    override fun onExited() {
+        onEnded()
+    }
+
+    private fun performDragAction() {
+        val targetItem =
+            state.layoutInfo.visibleItemsInfo
+                .asSequence()
+                .filter { item -> contentListState.isItemEditable(item.index) }
+                .firstItemAtOffset(dragOffset - contentOffset)
+
+        if (
+            targetItem != null &&
+                (!communalWidgetResizing() || targetItem.key != previousTargetItemKey)
+        ) {
+            if (communalWidgetResizing()) {
+                // Keep track of the previous target item, to avoid rapidly oscillating between
+                // items if the target item doesn't visually move as a result of the index change.
+                // In this case, even after the index changes, we'd still be colliding with the
+                // element, so it would be selected as the target item the next time this function
+                // runs again, which would trigger us to revert the index change we recently made.
+                previousTargetItemKey = targetItem.key
+            }
+
+            val scrollToIndex =
+                if (targetItem.index == state.firstVisibleItemIndex) {
+                    placeHolderIndex
+                } else if (placeHolderIndex == state.firstVisibleItemIndex) {
+                    targetItem.index
+                } else {
+                    null
+                }
+
+            if (scrollToIndex != null) {
+                scope.launch {
+                    state.scrollToItem(scrollToIndex, state.firstVisibleItemScrollOffset)
+                    movePlaceholderTo(targetItem.index)
+                }
+            } else {
+                movePlaceholderTo(targetItem.index)
+            }
+
+            placeHolderIndex = targetItem.index
+        } else if (targetItem == null) {
+            previousTargetItemKey = null
+        }
+    }
+
+    private fun computeAutoscroll(dragOffset: Offset): Float {
+        val orientation = state.layoutInfo.orientation
+        val distanceFromStart =
+            if (orientation == Orientation.Horizontal) {
+                dragOffset.x
+            } else {
+                dragOffset.y
+            }
+        val distanceFromEnd =
+            if (orientation == Orientation.Horizontal) {
+                state.layoutInfo.viewportEndOffset - dragOffset.x
+            } else {
+                state.layoutInfo.viewportEndOffset - dragOffset.y
+            }
+
+        return when {
+            distanceFromEnd < autoScrollThreshold -> {
+                (columnWidth - state.layoutInfo.beforeContentPadding).toFloat()
+            }
+            distanceFromStart < autoScrollThreshold -> {
+                -(columnWidth - state.layoutInfo.afterContentPadding).toFloat()
+            }
+            else -> 0f
+        }
+    }
+
+    private fun movePlaceholderTo(index: Int) {
+        val currentIndex = contentListState.list.indexOf(placeHolder)
+        if (currentIndex != index) {
+            contentListState.swapItems(currentIndex, index)
+        }
+    }
 }
+
+/**
+ * Parses and returns the intent extra associated with the widget that is dropped into the grid.
+ *
+ * Returns null if the drop event didn't include intent information.
+ */
+private fun DragAndDropEvent.maybeWidgetExtra(): WidgetPickerIntentUtils.WidgetExtra? {
+    val clipData = this.toAndroidDragEvent().clipData.takeIf { it.itemCount != 0 }
+    return clipData?.getItemAt(0)?.intent?.let { intent -> getWidgetExtraFromIntent(intent) }
+}
+
+private fun DragAndDropEvent.toOffset() = this.toAndroidDragEvent().run { Offset(x, y) }
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 c972d3e..2a5adde 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
@@ -19,7 +19,10 @@
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.animateScrollBy
 import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
 import androidx.compose.foundation.gestures.scrollBy
 import androidx.compose.foundation.layout.Box
@@ -37,13 +40,16 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.unit.IntRect
 import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.round
 import androidx.compose.ui.unit.toOffset
 import androidx.compose.ui.unit.toSize
 import com.android.systemui.Flags.communalWidgetResizing
+import com.android.systemui.Flags.glanceableHubV2
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
@@ -62,22 +68,22 @@
     contentListState: ContentListState,
     updateDragPositionForRemove: (boundingBox: IntRect) -> Boolean,
 ): GridDragDropState {
-    val scope = rememberCoroutineScope()
+    val coroutineScope = rememberCoroutineScope()
+    val autoScrollThreshold = with(LocalDensity.current) { 60.dp.toPx() }
+
     val state =
         remember(gridState, contentListState, updateDragPositionForRemove) {
             GridDragDropState(
-                state = gridState,
+                gridState = gridState,
                 contentListState = contentListState,
-                scope = scope,
+                coroutineScope = coroutineScope,
+                autoScrollThreshold = autoScrollThreshold,
                 updateDragPositionForRemove = updateDragPositionForRemove,
             )
         }
-    LaunchedEffect(state) {
-        while (true) {
-            val diff = state.scrollChannel.receive()
-            gridState.scrollBy(diff)
-        }
-    }
+
+    LaunchedEffect(state) { state.processScrollRequests(coroutineScope) }
+
     return state
 }
 
@@ -89,36 +95,86 @@
  * to remove the dragged item if condition met and call [ContentListState.onSaveList] to persist any
  * change in ordering.
  */
-class GridDragDropState
-internal constructor(
-    private val state: LazyGridState,
-    private val contentListState: ContentListState,
-    private val scope: CoroutineScope,
+class GridDragDropState(
+    val gridState: LazyGridState,
+    contentListState: ContentListState,
+    coroutineScope: CoroutineScope,
+    autoScrollThreshold: Float,
     private val updateDragPositionForRemove: (draggingBoundingBox: IntRect) -> Boolean,
 ) {
+    private val dragDropState: GridDragDropStateInternal =
+        if (glanceableHubV2()) {
+            GridDragDropStateV2(
+                gridState = gridState,
+                contentListState = contentListState,
+                scope = coroutineScope,
+                autoScrollThreshold = autoScrollThreshold,
+                updateDragPositionForRemove = updateDragPositionForRemove,
+            )
+        } else {
+            GridDragDropStateV1(
+                gridState = gridState,
+                contentListState = contentListState,
+                scope = coroutineScope,
+                updateDragPositionForRemove = updateDragPositionForRemove,
+            )
+        }
+
+    val draggingItemKey: String?
+        get() = dragDropState.draggingItemKey
+
+    val isDraggingToRemove: Boolean
+        get() = dragDropState.isDraggingToRemove
+
+    val draggingItemOffset: Offset
+        get() = dragDropState.draggingItemOffset
+
+    /**
+     * Called when dragging is initiated.
+     *
+     * @return {@code True} if dragging a grid item, {@code False} otherwise.
+     */
+    fun onDragStart(
+        offset: Offset,
+        screenWidth: Int,
+        layoutDirection: LayoutDirection,
+        contentOffset: Offset,
+    ): Boolean = dragDropState.onDragStart(offset, screenWidth, layoutDirection, contentOffset)
+
+    fun onDragInterrupted() = dragDropState.onDragInterrupted()
+
+    fun onDrag(offset: Offset, layoutDirection: LayoutDirection) =
+        dragDropState.onDrag(offset, layoutDirection)
+
+    suspend fun processScrollRequests(coroutineScope: CoroutineScope) =
+        dragDropState.processScrollRequests(coroutineScope)
+}
+
+/**
+ * A private base class defining the API for handling drag-and-drop operations. There will be two
+ * implementations of this class: V1 for devices that do not have the glanceable_hub_v2 flag
+ * enabled, and V2 for devices that do have that flag enabled.
+ *
+ * TODO(b/400789179): Remove this class and the V1 implementation once glanceable_hub_v2 has
+ *   shipped.
+ */
+private open class GridDragDropStateInternal(protected val state: LazyGridState) {
     var draggingItemKey by mutableStateOf<String?>(null)
-        private set
+        protected set
 
     var isDraggingToRemove by mutableStateOf(false)
-        private set
+        protected set
 
-    internal val scrollChannel = Channel<Float>()
+    var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
+    var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
 
-    private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
-    private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
-
-    private val spacer = CommunalContentModel.Spacer(CommunalContentSize.Responsive(1))
-    private var spacerIndex: Int? = null
-
-    private var previousTargetItemKey: Any? = null
-
-    internal val draggingItemOffset: Offset
+    val draggingItemOffset: Offset
         get() =
             draggingItemLayoutInfo?.let { item ->
                 draggingItemInitialOffset + draggingItemDraggedDelta - item.offset.toOffset()
             } ?: Offset.Zero
 
-    private val draggingItemLayoutInfo: LazyGridItemInfo?
+    val draggingItemLayoutInfo: LazyGridItemInfo?
         get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.key == draggingItemKey }
 
     /**
@@ -126,7 +182,45 @@
      *
      * @return {@code True} if dragging a grid item, {@code False} otherwise.
      */
-    internal fun onDragStart(
+    open fun onDragStart(
+        offset: Offset,
+        screenWidth: Int,
+        layoutDirection: LayoutDirection,
+        contentOffset: Offset,
+    ): Boolean = false
+
+    open fun onDragInterrupted() = Unit
+
+    open fun onDrag(offset: Offset, layoutDirection: LayoutDirection) = Unit
+
+    open suspend fun processScrollRequests(coroutineScope: CoroutineScope) = Unit
+}
+
+/**
+ * The V1 implementation of GridDragDropStateInternal to be used when the glanceable_hub_v2 flag is
+ * disabled.
+ */
+private class GridDragDropStateV1(
+    val gridState: LazyGridState,
+    private val contentListState: ContentListState,
+    private val scope: CoroutineScope,
+    private val updateDragPositionForRemove: (draggingBoundingBox: IntRect) -> Boolean,
+) : GridDragDropStateInternal(gridState) {
+    private val scrollChannel = Channel<Float>()
+
+    private val spacer = CommunalContentModel.Spacer(CommunalContentSize.Responsive(1))
+    private var spacerIndex: Int? = null
+
+    private var previousTargetItemKey: Any? = null
+
+    override suspend fun processScrollRequests(coroutineScope: CoroutineScope) {
+        while (true) {
+            val diff = scrollChannel.receive()
+            state.scrollBy(diff)
+        }
+    }
+
+    override fun onDragStart(
         offset: Offset,
         screenWidth: Int,
         layoutDirection: LayoutDirection,
@@ -162,7 +256,7 @@
         return false
     }
 
-    internal fun onDragInterrupted() {
+    override fun onDragInterrupted() {
         draggingItemKey?.let {
             if (isDraggingToRemove) {
                 contentListState.onRemove(
@@ -185,7 +279,7 @@
         }
     }
 
-    internal fun onDrag(offset: Offset, layoutDirection: LayoutDirection) {
+    override fun onDrag(offset: Offset, layoutDirection: LayoutDirection) {
         // Adjust offset to match the layout direction
         draggingItemDraggedDelta +=
             Offset(offset.x.directional(LayoutDirection.Ltr, layoutDirection), offset.y)
@@ -282,6 +376,249 @@
     }
 }
 
+/**
+ * The V2 implementation of GridDragDropStateInternal to be used when the glanceable_hub_v2 flag is
+ * enabled.
+ */
+private class GridDragDropStateV2(
+    val gridState: LazyGridState,
+    private val contentListState: ContentListState,
+    private val scope: CoroutineScope,
+    private val autoScrollThreshold: Float,
+    private val updateDragPositionForRemove: (draggingBoundingBox: IntRect) -> Boolean,
+) : GridDragDropStateInternal(gridState) {
+
+    private val scrollChannel = Channel<Float>(Channel.UNLIMITED)
+
+    // Used to keep track of the dragging item during scrolling (because it might be off screen
+    // and no longer in the list of visible items).
+    private var draggingItemWhileScrolling: LazyGridItemInfo? by mutableStateOf(null)
+
+    private val spacer = CommunalContentModel.Spacer(CommunalContentSize.Responsive(1))
+    private var spacerIndex: Int? = null
+
+    private var previousTargetItemKey: Any? = null
+
+    // Basically, the location of the user's finger on the screen.
+    private var currentDragPositionOnScreen by mutableStateOf(Offset.Zero)
+    // The offset of the grid from the top of the screen.
+    private var contentOffset = Offset.Zero
+
+    // The width of one column in the grid (needed in order to auto-scroll one column at a time).
+    private var columnWidth = 0
+
+    override suspend fun processScrollRequests(coroutineScope: CoroutineScope) {
+        while (true) {
+            val amount = scrollChannel.receive()
+
+            if (state.isScrollInProgress) {
+                // Ignore overscrolling if a scroll is already in progress (but we still want to
+                // consume the scroll event so that we don't end up processing a bunch of old
+                // events after scrolling has finished).
+                continue
+            }
+
+            // We perform the rest of the drag action after scrolling has finished (or immediately
+            // if there will be no scrolling).
+            if (amount != 0f) {
+                coroutineScope.launch {
+                    state.animateScrollBy(amount, tween(delayMillis = 250, durationMillis = 1000))
+                    performDragAction()
+                }
+            } else {
+                performDragAction()
+            }
+        }
+    }
+
+    override fun onDragStart(
+        offset: Offset,
+        screenWidth: Int,
+        layoutDirection: LayoutDirection,
+        contentOffset: Offset,
+    ): Boolean {
+        val normalizedOffset =
+            Offset(
+                if (layoutDirection == LayoutDirection.Ltr) offset.x else screenWidth - offset.x,
+                offset.y,
+            )
+
+        currentDragPositionOnScreen = normalizedOffset
+        this.contentOffset = contentOffset
+
+        state.layoutInfo.visibleItemsInfo
+            .filter { item -> contentListState.isItemEditable(item.index) }
+            // grid item offset is based off grid content container so we need to deduct
+            // before content padding from the initial pointer position
+            .firstItemAtOffset(normalizedOffset - contentOffset)
+            ?.apply {
+                draggingItemKey = key as String
+                draggingItemWhileScrolling = this
+                draggingItemInitialOffset = this.offset.toOffset()
+                columnWidth =
+                    this.size.width +
+                        state.layoutInfo.beforeContentPadding +
+                        state.layoutInfo.afterContentPadding
+                // Add a spacer after the last widget if it is larger than the dragging widget.
+                // This allows overscrolling, enabling the dragging widget to be placed beyond it.
+                val lastWidget = contentListState.list.lastOrNull { it.isWidgetContent() }
+                if (
+                    lastWidget != null &&
+                        draggingItemLayoutInfo != null &&
+                        lastWidget.size.span > draggingItemLayoutInfo!!.span
+                ) {
+                    contentListState.list.add(spacer)
+                    spacerIndex = contentListState.list.size - 1
+                }
+                return true
+            }
+
+        return false
+    }
+
+    override fun onDragInterrupted() {
+        draggingItemKey?.let {
+            if (isDraggingToRemove) {
+                contentListState.onRemove(
+                    contentListState.list.indexOfFirst { it.key == draggingItemKey }
+                )
+                isDraggingToRemove = false
+                updateDragPositionForRemove(IntRect.Zero)
+            }
+            // persist list editing changes on dragging ends
+            contentListState.onSaveList()
+            draggingItemKey = null
+        }
+        previousTargetItemKey = null
+        draggingItemDraggedDelta = Offset.Zero
+        draggingItemInitialOffset = Offset.Zero
+        currentDragPositionOnScreen = Offset.Zero
+        draggingItemWhileScrolling = null
+        // Remove spacer, if any, when a drag gesture finishes.
+        spacerIndex?.let {
+            contentListState.list.removeAt(it)
+            spacerIndex = null
+        }
+    }
+
+    override fun onDrag(offset: Offset, layoutDirection: LayoutDirection) {
+        // Adjust offset to match the layout direction
+        val delta = Offset(offset.x.directional(LayoutDirection.Ltr, layoutDirection), offset.y)
+        draggingItemDraggedDelta += delta
+        currentDragPositionOnScreen += delta
+
+        scrollChannel.trySend(computeAutoscroll(currentDragPositionOnScreen))
+    }
+
+    fun performDragAction() {
+        val draggingItem = draggingItemLayoutInfo ?: draggingItemWhileScrolling
+        if (draggingItem == null) {
+            return
+        }
+
+        val draggingBoundingBox =
+            IntRect(draggingItem.offset + draggingItemOffset.round(), draggingItem.size)
+        val curDragPositionInGrid = (currentDragPositionOnScreen - contentOffset)
+
+        val targetItem =
+            if (communalWidgetResizing()) {
+                val lastVisibleItemIndex = state.layoutInfo.visibleItemsInfo.last().index
+                state.layoutInfo.visibleItemsInfo.findLast(
+                    fun(item): Boolean {
+                        val itemBoundingBox = IntRect(item.offset, item.size)
+                        return draggingItemKey != item.key &&
+                            contentListState.isItemEditable(item.index) &&
+                            itemBoundingBox.contains(curDragPositionInGrid.round()) &&
+                            // If we swap with the last visible item, and that item doesn't fit
+                            // in the gap created by moving the current item, then the current item
+                            // will get placed after the last visible item. In this case, it gets
+                            // placed outside of the viewport. We avoid this here, so the user
+                            // has to scroll first before the swap can happen.
+                            (item.index != lastVisibleItemIndex || item.span <= draggingItem.span)
+                    }
+                )
+            } else {
+                state.layoutInfo.visibleItemsInfo
+                    .asSequence()
+                    .filter { item -> contentListState.isItemEditable(item.index) }
+                    .filter { item -> draggingItem.index != item.index }
+                    .firstItemAtOffset(curDragPositionInGrid)
+            }
+
+        if (
+            targetItem != null &&
+                (!communalWidgetResizing() || targetItem.key != previousTargetItemKey)
+        ) {
+            val scrollToIndex =
+                if (targetItem.index == state.firstVisibleItemIndex) {
+                    draggingItem.index
+                } else if (draggingItem.index == state.firstVisibleItemIndex) {
+                    targetItem.index
+                } else {
+                    null
+                }
+            if (communalWidgetResizing()) {
+                // Keep track of the previous target item, to avoid rapidly oscillating between
+                // items if the target item doesn't visually move as a result of the index change.
+                // In this case, even after the index changes, we'd still be colliding with the
+                // element, so it would be selected as the target item the next time this function
+                // runs again, which would trigger us to revert the index change we recently made.
+                previousTargetItemKey = targetItem.key
+            }
+            if (scrollToIndex != null) {
+                scope.launch {
+                    // this is needed to neutralize automatic keeping the first item first.
+                    state.scrollToItem(scrollToIndex, state.firstVisibleItemScrollOffset)
+                    contentListState.swapItems(draggingItem.index, targetItem.index)
+                }
+            } else {
+                contentListState.swapItems(draggingItem.index, targetItem.index)
+            }
+            draggingItemWhileScrolling = targetItem
+            isDraggingToRemove = false
+        } else if (targetItem == null) {
+            isDraggingToRemove = checkForRemove(draggingBoundingBox)
+            previousTargetItemKey = null
+        }
+    }
+
+    /** Calculate the amount dragged out of bound on both sides. Returns 0f if not overscrolled. */
+    private fun computeAutoscroll(dragOffset: Offset): Float {
+        val orientation = state.layoutInfo.orientation
+        val distanceFromStart =
+            if (orientation == Orientation.Horizontal) {
+                dragOffset.x
+            } else {
+                dragOffset.y
+            }
+        val distanceFromEnd =
+            if (orientation == Orientation.Horizontal) {
+                state.layoutInfo.viewportEndOffset - dragOffset.x
+            } else {
+                state.layoutInfo.viewportEndOffset - dragOffset.y
+            }
+
+        return when {
+            distanceFromEnd < autoScrollThreshold -> {
+                (columnWidth - state.layoutInfo.beforeContentPadding).toFloat()
+            }
+            distanceFromStart < autoScrollThreshold -> {
+                -(columnWidth - state.layoutInfo.afterContentPadding).toFloat()
+            }
+            else -> 0f
+        }
+    }
+
+    /** Calls the callback with the updated drag position and returns whether to remove the item. */
+    private fun checkForRemove(draggingItemBoundingBox: IntRect): Boolean {
+        return if (draggingItemDraggedDelta.y < 0) {
+            updateDragPositionForRemove(draggingItemBoundingBox)
+        } else {
+            false
+        }
+    }
+}
+
 fun Modifier.dragContainer(
     dragDropState: GridDragDropState,
     layoutDirection: LayoutDirection,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 216f0a7..7782705 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -73,7 +73,7 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.compose.animation.Expandable
 import com.android.compose.animation.scene.ContentScope
-import com.android.compose.modifiers.fadingBackground
+import com.android.compose.modifiers.animatedBackground
 import com.android.compose.theme.colorAttr
 import com.android.systemui.Flags.notificationShadeBlur
 import com.android.systemui.animation.Expandable
@@ -172,8 +172,8 @@
     val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius)
     val backgroundModifier =
         remember(backgroundColor, backgroundAlphaValue, backgroundTopRadius) {
-            Modifier.fadingBackground(
-                backgroundColor,
+            Modifier.animatedBackground(
+                { backgroundColor },
                 backgroundAlphaValue,
                 RoundedCornerShape(topStart = backgroundTopRadius, topEnd = backgroundTopRadius),
             )
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 061fdd9..0a71148 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -356,7 +356,8 @@
                                     modifier = Modifier.padding(horizontal = 16.dp),
                                 )
                             }
-                        else -> CollapsedShadeHeader(viewModel = headerViewModel)
+                        else ->
+                            CollapsedShadeHeader(viewModel = headerViewModel, isSplitShade = false)
                     }
                     Spacer(modifier = Modifier.height(16.dp))
                     // This view has its own horizontal padding
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index a021626..8f0fb20 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -52,6 +52,7 @@
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
+import com.android.systemui.brightness.ui.compose.ContainerColors
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.lifecycle.rememberViewModel
@@ -257,15 +258,18 @@
                 modifier = Modifier.padding(horizontal = QuickSettingsShade.Dimensions.Padding),
             )
 
-            BrightnessSliderContainer(
-                viewModel = viewModel.brightnessSliderViewModel,
-                containerColor = OverlayShade.Colors.PanelBackground,
-                modifier =
-                    Modifier.systemGestureExclusionInShade(
-                            enabled = { layoutState.transitionState is TransitionState.Idle }
-                        )
-                        .fillMaxWidth(),
-            )
+            Box(
+                Modifier.systemGestureExclusionInShade(
+                    enabled = { layoutState.transitionState is TransitionState.Idle }
+                )
+            ) {
+                BrightnessSliderContainer(
+                    viewModel = viewModel.brightnessSliderViewModel,
+                    containerColors =
+                        ContainerColors.singleColor(OverlayShade.Colors.PanelBackground),
+                    modifier = Modifier.fillMaxWidth(),
+                )
+            }
 
             Box {
                 GridAnchor()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 7015f79..0daef46 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -101,7 +101,7 @@
         rememberActivated(traceName = "sceneJankMonitor") { sceneJankMonitorFactory.create() }
 
     val hapticFeedback = LocalHapticFeedback.current
-    val shadeExpansionMotion = OverlayShade.rememberShadeExpansionMotion()
+    val shadeExpansionMotion = OverlayShade.rememberShadeExpansionMotion(isFullWidthShade())
     val sceneTransitions =
         remember(hapticFeedback, shadeExpansionMotion) {
             transitionsBuilder.build(
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 9b45ef6..2f5a030 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
@@ -6,7 +6,7 @@
 import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
 import com.android.compose.animation.scene.transitions
 import com.android.internal.jank.Cuj
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
@@ -50,7 +50,7 @@
  */
 class SceneContainerTransitions : SceneContainerTransitionsBuilder {
     override fun build(
-        shadeExpansionMotion: EdgeContainerExpansionSpec,
+        shadeExpansionMotion: VerticalExpandContainerSpec,
         revealHaptics: ContainerRevealHaptics,
     ): SceneTransitions {
         return transitions {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
index eb5548d..4c9c23a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitionsBuilder.kt
@@ -19,7 +19,7 @@
 import com.android.compose.animation.scene.SceneTransitions
 import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
 import com.android.compose.animation.scene.transitions
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
 
 /**
  * Builder of the comprehensive definition of all transitions between scenes and overlays in the
@@ -29,7 +29,7 @@
 
     /** Build the [SceneContainer] transitions spec. */
     fun build(
-        shadeExpansionMotion: EdgeContainerExpansionSpec,
+        shadeExpansionMotion: VerticalExpandContainerSpec,
         revealHaptics: ContainerRevealHaptics,
     ): SceneTransitions
 }
@@ -42,7 +42,7 @@
     private val transitions: SceneTransitions = transitions { /* No transitions */ }
 ) : SceneContainerTransitionsBuilder {
     override fun build(
-        shadeExpansionMotion: EdgeContainerExpansionSpec,
+        shadeExpansionMotion: VerticalExpandContainerSpec,
         revealHaptics: ContainerRevealHaptics,
     ): SceneTransitions = transitions
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 9b4b91e..85aad9b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -20,7 +20,7 @@
 import com.android.compose.animation.scene.TransitionBuilder
 import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
 import com.android.compose.animation.scene.reveal.verticalContainerReveal
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
 import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
 import com.android.systemui.notifications.ui.composable.NotificationsShade
 import com.android.systemui.scene.shared.model.Overlays
@@ -29,7 +29,7 @@
 
 fun TransitionBuilder.toNotificationsShadeTransition(
     durationScale: Double = 1.0,
-    shadeExpansionMotion: EdgeContainerExpansionSpec,
+    shadeExpansionMotion: VerticalExpandContainerSpec,
     revealHaptics: ContainerRevealHaptics,
 ) {
     spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
index 47dd85f..8f0447d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
@@ -20,14 +20,14 @@
 import com.android.compose.animation.scene.TransitionBuilder
 import com.android.compose.animation.scene.reveal.ContainerRevealHaptics
 import com.android.compose.animation.scene.reveal.verticalContainerReveal
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
 import com.android.systemui.qs.ui.composable.QuickSettingsShade
 import com.android.systemui.shade.ui.composable.OverlayShade
 import kotlin.time.Duration.Companion.milliseconds
 
 fun TransitionBuilder.toQuickSettingsShadeTransition(
     durationScale: Double = 1.0,
-    shadeExpansionMotion: EdgeContainerExpansionSpec,
+    shadeExpansionMotion: VerticalExpandContainerSpec,
     revealHaptics: ContainerRevealHaptics,
 ) {
     spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
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 3446081..068218a 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
@@ -53,8 +53,8 @@
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
-import com.android.mechanics.behavior.edgeContainerExpansionBackground
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
+import com.android.mechanics.behavior.verticalExpandContainerBackground
 import com.android.systemui.res.R
 import com.android.systemui.shade.ui.composable.OverlayShade.rememberShadeExpansionMotion
 
@@ -114,9 +114,9 @@
         modifier =
             modifier
                 .disableSwipesWhenScrolling()
-                .edgeContainerExpansionBackground(
-                    OverlayShade.Colors.PanelBackground,
-                    rememberShadeExpansionMotion(),
+                .verticalExpandContainerBackground(
+                    backgroundColor = OverlayShade.Colors.PanelBackground,
+                    spec = rememberShadeExpansionMotion(isFullWidthShade()),
                 )
     ) {
         Column {
@@ -202,8 +202,10 @@
     }
 
     @Composable
-    fun rememberShadeExpansionMotion(): EdgeContainerExpansionSpec {
+    fun rememberShadeExpansionMotion(isFullWidth: Boolean): VerticalExpandContainerSpec {
         val radius = Dimensions.PanelCornerRadius
-        return remember(radius) { EdgeContainerExpansionSpec(radius = radius) }
+        return remember(radius, isFullWidth) {
+            VerticalExpandContainerSpec(isFloating = !isFullWidth, radius = radius)
+        }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 23baeac..86c8fc3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -127,6 +127,7 @@
 @Composable
 fun ContentScope.CollapsedShadeHeader(
     viewModel: ShadeHeaderViewModel,
+    isSplitShade: Boolean,
     modifier: Modifier = Modifier,
 ) {
     val cutoutLocation = LocalDisplayCutout.current.location
@@ -141,8 +142,6 @@
             }
         }
 
-    val isShadeLayoutWide = viewModel.isShadeLayoutWide
-
     val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
 
     // This layout assumes it is globally positioned at (0, 0) and is the same size as the screen.
@@ -154,7 +153,7 @@
                 horizontalArrangement = Arrangement.spacedBy(5.dp),
                 modifier = Modifier.padding(horizontal = horizontalPadding),
             ) {
-                Clock(scale = 1f, onClick = viewModel::onClockClicked)
+                Clock(onClick = viewModel::onClockClicked)
                 VariableDayDate(
                     longerDateText = viewModel.longerDateText,
                     shorterDateText = viewModel.shorterDateText,
@@ -184,11 +183,11 @@
                         Modifier.element(ShadeHeader.Elements.CollapsedContentEnd)
                             .padding(horizontal = horizontalPadding),
                 ) {
-                    if (isShadeLayoutWide) {
+                    if (isSplitShade) {
                         ShadeCarrierGroup(viewModel = viewModel)
                     }
                     SystemIconChip(
-                        onClick = viewModel::onSystemIconChipClicked.takeIf { isShadeLayoutWide }
+                        onClick = viewModel::onSystemIconChipClicked.takeIf { isSplitShade }
                     ) {
                         StatusIcons(
                             viewModel = viewModel,
@@ -233,13 +232,11 @@
                     .defaultMinSize(minHeight = ShadeHeader.Dimensions.ExpandedHeight),
         ) {
             Box(modifier = Modifier.fillMaxWidth()) {
-                Box {
-                    Clock(
-                        scale = 2.57f,
-                        onClick = viewModel::onClockClicked,
-                        modifier = Modifier.align(Alignment.CenterStart),
-                    )
-                }
+                Clock(
+                    onClick = viewModel::onClockClicked,
+                    modifier = Modifier.align(Alignment.CenterStart),
+                    scale = 2.57f,
+                )
                 Box(
                     modifier =
                         Modifier.element(ShadeHeader.Elements.ShadeCarrierGroup).fillMaxWidth()
@@ -291,8 +288,6 @@
     val horizontalPadding =
         max(LocalScreenCornerRadius.current / 2f, Shade.Dimensions.HorizontalPadding)
 
-    val isShadeLayoutWide = viewModel.isShadeLayoutWide
-
     val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
 
     // This layout assumes it is globally positioned at (0, 0) and is the same size as the screen.
@@ -301,16 +296,15 @@
         startContent = {
             Row(
                 verticalAlignment = Alignment.CenterVertically,
+                horizontalArrangement = Arrangement.spacedBy(5.dp),
                 modifier = Modifier.padding(horizontal = horizontalPadding),
             ) {
                 val chipHighlight = viewModel.notificationsChipHighlight
-                if (isShadeLayoutWide) {
+                if (viewModel.showClock) {
                     Clock(
-                        scale = 1f,
                         onClick = viewModel::onClockClicked,
                         modifier = Modifier.padding(horizontal = 4.dp),
                     )
-                    Spacer(modifier = Modifier.width(5.dp))
                 }
                 NotificationsChip(
                     onClick = viewModel::onNotificationIconChipClicked,
@@ -437,7 +431,11 @@
 }
 
 @Composable
-private fun ContentScope.Clock(scale: Float, onClick: () -> Unit, modifier: Modifier = Modifier) {
+private fun ContentScope.Clock(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    scale: Float = 1f,
+) {
     val layoutDirection = LocalLayoutDirection.current
 
     ElementWithValues(key = ShadeHeader.Elements.Clock, modifier = modifier) {
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 5040490..885d34f 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
@@ -56,11 +56,11 @@
 import androidx.compose.ui.layout.layoutId
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.colorResource
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
+import androidx.lifecycle.compose.LocalLifecycleOwner
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ContentScope
 import com.android.compose.animation.scene.ElementKey
@@ -68,6 +68,7 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.animateContentDpAsState
+import com.android.compose.animation.scene.animateContentFloatAsState
 import com.android.compose.animation.scene.animateSceneFloatAsState
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.modifiers.padding
@@ -223,9 +224,6 @@
                 viewModel = viewModel,
                 headerViewModel = headerViewModel,
                 notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
-                createTintedIconManager = createTintedIconManager,
-                createBatteryMeterViewController = createBatteryMeterViewController,
-                statusBarIconController = statusBarIconController,
                 mediaCarouselController = mediaCarouselController,
                 mediaHost = qqsMediaHost,
                 modifier = modifier,
@@ -253,9 +251,6 @@
     viewModel: ShadeSceneContentViewModel,
     headerViewModel: ShadeHeaderViewModel,
     notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
-    createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
-    createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
-    statusBarIconController: StatusBarIconController,
     mediaCarouselController: MediaCarouselController,
     mediaHost: MediaHost,
     modifier: Modifier = Modifier,
@@ -340,6 +335,7 @@
             content = {
                 CollapsedShadeHeader(
                     viewModel = headerViewModel,
+                    isSplitShade = false,
                     modifier = Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader),
                 )
 
@@ -434,15 +430,13 @@
     val footerActionsViewModel =
         remember(lifecycleOwner, viewModel) { viewModel.getFooterActionsViewModel(lifecycleOwner) }
     val tileSquishiness by
-        animateSceneFloatAsState(
+        animateContentFloatAsState(
             value = 1f,
             key = QuickSettings.SharedValues.TilesSquishiness,
             canOverflow = false,
         )
     val unfoldTranslationXForStartSide by
         viewModel.unfoldTranslationX(isOnStartSide = true).collectAsStateWithLifecycle(0f)
-    val unfoldTranslationXForEndSide by
-        viewModel.unfoldTranslationX(isOnStartSide = false).collectAsStateWithLifecycle(0f)
 
     val notificationStackPadding = dimensionResource(id = R.dimen.notification_side_paddings)
     val navBarBottomHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
@@ -512,6 +506,7 @@
         Column(modifier = Modifier.fillMaxSize()) {
             CollapsedShadeHeader(
                 viewModel = headerViewModel,
+                isSplitShade = true,
                 modifier =
                     Modifier.then(brightnessMirrorShowingModifier)
                         .padding(horizontal = { unfoldTranslationXForStartSide.roundToInt() }),
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
index b04d89d..0b0df06c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
@@ -20,6 +20,7 @@
 import androidx.compose.animation.core.AnimationVector1D
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.runtime.withFrameNanos
 import com.android.compose.animation.scene.content.state.TransitionState
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
@@ -47,6 +48,11 @@
                 oneOffAnimation.animatable = it
             }
 
+        if (layoutState.deferTransitionProgress) {
+            // Defer the animation by one frame so that the transition progress is changed only when
+            // the expensive first composition frame is done.
+            withFrameNanos {}
+        }
         animatable.animateTo(targetProgress, animationSpec, initialVelocity)
     }
 
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 1360611..36eafa4 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
@@ -68,7 +68,7 @@
         return layoutImpl.swipeDetector.detectSwipe(change)
     }
 
-    override fun shouldConsumeNestedScroll(sign: Float): Boolean {
+    override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
         return this.enabled()
     }
 
@@ -290,6 +290,15 @@
     val isDrivingTransition: Boolean
         get() = layoutState.transitionState == swipeAnimation.contentTransition
 
+    override val isReadyToDrag: Boolean
+        get() {
+            return !layoutState.deferTransitionProgress ||
+                with(draggableHandler.layoutImpl.elementStateScope) {
+                    swipeAnimation.fromContent.targetSize() != null &&
+                        swipeAnimation.toContent.targetSize() != null
+                }
+        }
+
     init {
         check(!isDrivingTransition) { "Multiple controllers with the same SwipeTransition" }
     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 4da83c3..a8b676d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -462,7 +462,9 @@
                 // swipes.
                 .swipeToScene(horizontalDraggableHandler)
                 .swipeToScene(verticalDraggableHandler)
-                .then(LayoutElement(layoutImpl = this))
+                .then(
+                    LayoutElement(layoutImpl = this, transitionState = this.state.transitionState)
+                )
         ) {
             LookaheadScope {
                 if (_lookaheadScope == null) {
@@ -623,23 +625,28 @@
     @VisibleForTesting internal fun overlaysOrNullForTest(): Map<OverlayKey, Overlay>? = _overlays
 }
 
-private data class LayoutElement(private val layoutImpl: SceneTransitionLayoutImpl) :
-    ModifierNodeElement<LayoutNode>() {
-    override fun create(): LayoutNode = LayoutNode(layoutImpl)
+private data class LayoutElement(
+    private val layoutImpl: SceneTransitionLayoutImpl,
+    private val transitionState: TransitionState,
+) : ModifierNodeElement<LayoutNode>() {
+    override fun create(): LayoutNode = LayoutNode(layoutImpl, transitionState)
 
     override fun update(node: LayoutNode) {
         node.layoutImpl = layoutImpl
+        node.transitionState = transitionState
     }
 }
 
-private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
-    Modifier.Node(), ApproachLayoutModifierNode, LayoutAwareModifierNode {
+private class LayoutNode(
+    var layoutImpl: SceneTransitionLayoutImpl,
+    var transitionState: TransitionState,
+) : Modifier.Node(), ApproachLayoutModifierNode, LayoutAwareModifierNode {
     override fun onRemeasured(size: IntSize) {
         layoutImpl.lastSize = size
     }
 
     override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {
-        return layoutImpl.state.isTransitioning()
+        return transitionState is TransitionState.Transition.ChangeScene
     }
 
     @ExperimentalComposeUiApi
@@ -652,8 +659,7 @@
 
         val width: Int
         val height: Int
-        val transition =
-            layoutImpl.state.currentTransition as? TransitionState.Transition.ChangeScene
+        val transition = transitionState as? TransitionState.Transition.ChangeScene
         if (transition == null) {
             width = placeable.width
             height = placeable.height
@@ -662,6 +668,9 @@
             val fromSize = layoutImpl.scene(transition.fromScene).targetSize
             val toSize = layoutImpl.scene(transition.toScene).targetSize
 
+            check(fromSize != Element.SizeUnspecified) { "fromSize is unspecified " }
+            check(toSize != Element.SizeUnspecified) { "toSize is unspecified" }
+
             // Optimization: make sure we don't read state.progress if fromSize ==
             // toSize to avoid running this code every frame when the layout size does
             // not change.
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 56e8c45..4e28dd5 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
@@ -234,6 +234,10 @@
  *   `from` overlay by `to` overlay.
  * @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other
  *   [SceneTransitionLayoutState]s.
+ * @param deferTransitionProgress whether we should wait for the first composition to be done before
+ *   changing the progress of a transition. This can help reduce perceivable jank at the start of a
+ *   transition in case the first composition of a content takes a lot of time and we are going to
+ *   miss that first frame.
  */
 fun MutableSceneTransitionLayoutState(
     initialScene: SceneKey,
@@ -246,6 +250,9 @@
     canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true },
     onTransitionStart: (TransitionState.Transition) -> Unit = {},
     onTransitionEnd: (TransitionState.Transition) -> Unit = {},
+
+    // TODO(b/400688335): Turn on by default and remove this flag before flexiglass is released.
+    deferTransitionProgress: Boolean = false,
 ): MutableSceneTransitionLayoutState {
     return MutableSceneTransitionLayoutStateImpl(
         initialScene,
@@ -258,6 +265,7 @@
         canReplaceOverlay,
         onTransitionStart,
         onTransitionEnd,
+        deferTransitionProgress,
     )
 }
 
@@ -272,6 +280,9 @@
     canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true },
     onTransitionStart: (TransitionState.Transition) -> Unit = {},
     onTransitionEnd: (TransitionState.Transition) -> Unit = {},
+
+    // TODO(b/400688335): Turn on by default and remove this flag before flexiglass is released.
+    deferTransitionProgress: Boolean = false,
 ): MutableSceneTransitionLayoutState {
     val motionScheme = MaterialTheme.motionScheme
     val layoutState = remember {
@@ -286,6 +297,7 @@
             canReplaceOverlay = canReplaceOverlay,
             onTransitionStart = onTransitionStart,
             onTransitionEnd = onTransitionEnd,
+            deferTransitionProgress = deferTransitionProgress,
         )
     }
 
@@ -298,6 +310,7 @@
         layoutState.canReplaceOverlay = canReplaceOverlay
         layoutState.onTransitionStart = onTransitionStart
         layoutState.onTransitionEnd = onTransitionEnd
+        layoutState.deferTransitionProgress = deferTransitionProgress
     }
     return layoutState
 }
@@ -317,6 +330,8 @@
     },
     internal var onTransitionStart: (TransitionState.Transition) -> Unit = {},
     internal var onTransitionEnd: (TransitionState.Transition) -> Unit = {},
+    // TODO(b/400688335): Turn on by default and remove this flag before flexiglass is released.
+    internal var deferTransitionProgress: Boolean = false,
 ) : MutableSceneTransitionLayoutState {
     private val creationThread: Thread = Thread.currentThread()
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
index 2d2a8154..5d4232d8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
@@ -38,7 +38,7 @@
     }
 
     override fun ContentKey.targetSize(): IntSize? {
-        return layoutImpl.content(this).targetSize.takeIf { it != IntSize.Zero }
+        return layoutImpl.content(this).targetSize.takeIf { it != Element.SizeUnspecified }
     }
 }
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index 72ee75a..7492f37 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -32,12 +32,17 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ApproachLayoutModifierNode
+import androidx.compose.ui.layout.ApproachMeasureScope
 import androidx.compose.ui.layout.LookaheadScope
-import androidx.compose.ui.layout.approachLayout
-import androidx.compose.ui.layout.layout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.zIndex
 import com.android.compose.animation.scene.Ancestor
 import com.android.compose.animation.scene.AnimatedState
 import com.android.compose.animation.scene.ContentKey
@@ -68,8 +73,8 @@
 import com.android.compose.gesture.NestedScrollableBound
 import com.android.compose.gesture.nestedScrollController
 import com.android.compose.modifiers.thenIf
+import com.android.compose.ui.graphics.ContainerNode
 import com.android.compose.ui.graphics.ContainerState
-import com.android.compose.ui.graphics.container
 import kotlin.math.pow
 
 /** A content defined in a [SceneTransitionLayout], i.e. a scene or an overlay. */
@@ -88,9 +93,10 @@
     val containerState = ContainerState()
 
     // Important: All fields in this class should be backed by State given that contents are updated
-    // directly during composition, outside of a SideEffect.
+    // directly during composition, outside of a SideEffect, or are observed during composition,
+    // layout or drawing.
     var content by mutableStateOf(content)
-    var targetSize by mutableStateOf(IntSize.Zero)
+    var targetSize by mutableStateOf(Element.SizeUnspecified)
     var userActions by mutableStateOf(actions)
     var zIndex by mutableFloatStateOf(zIndex)
 
@@ -158,24 +164,14 @@
     fun Content(modifier: Modifier = Modifier, isInvisible: Boolean = false) {
         // If this content has a custom factory, provide it to the content so that the factory is
         // automatically used when calling rememberOverscrollEffect().
+        val isElevationPossible =
+            layoutImpl.state.isElevationPossible(content = key, element = null)
         Box(
-            modifier
-                .thenIf(isInvisible) { InvisibleModifier }
-                .zIndex(zIndex)
-                .approachLayout(
-                    isMeasurementApproachInProgress = { layoutImpl.state.isTransitioning() }
-                ) { measurable, constraints ->
-                    // TODO(b/353679003): Use the ModifierNode API to set this *before* the
-                    // approach
-                    // pass is started.
-                    targetSize = lookaheadSize
-                    val placeable = measurable.measure(constraints)
-                    layout(placeable.width, placeable.height) { placeable.place(0, 0) }
-                }
-                .thenIf(layoutImpl.state.isElevationPossible(content = key, element = null)) {
-                    Modifier.container(containerState)
-                }
-                .thenIf(layoutImpl.implicitTestTags) { Modifier.testTag(key.testTag) }
+            modifier.then(ContentElement(this, isElevationPossible, isInvisible)).thenIf(
+                layoutImpl.implicitTestTags
+            ) {
+                Modifier.testTag(key.testTag)
+            }
         ) {
             CompositionLocalProvider(LocalOverscrollFactory provides lastFactory) {
                 scope.content()
@@ -194,6 +190,80 @@
     }
 }
 
+private data class ContentElement(
+    private val content: Content,
+    private val isElevationPossible: Boolean,
+    private val isInvisible: Boolean,
+) : ModifierNodeElement<ContentNode>() {
+    override fun create(): ContentNode = ContentNode(content, isElevationPossible, isInvisible)
+
+    override fun update(node: ContentNode) {
+        node.update(content, isElevationPossible, isInvisible)
+    }
+}
+
+private class ContentNode(
+    private var content: Content,
+    private var isElevationPossible: Boolean,
+    private var isInvisible: Boolean,
+) : DelegatingNode(), ApproachLayoutModifierNode {
+    private var containerDelegate = containerDelegate(isElevationPossible)
+
+    private fun containerDelegate(isElevationPossible: Boolean): ContainerNode? {
+        return if (isElevationPossible) delegate(ContainerNode(content.containerState)) else null
+    }
+
+    override fun onDetach() {
+        this.content.targetSize = Element.SizeUnspecified
+    }
+
+    fun update(content: Content, isElevationPossible: Boolean, isInvisible: Boolean) {
+        if (content != this.content) {
+            this.content.targetSize = Element.SizeUnspecified
+            this.content = content
+        }
+
+        if (content != this.content || isElevationPossible != this.isElevationPossible) {
+            this.isElevationPossible = isElevationPossible
+
+            containerDelegate?.let { undelegate(it) }
+            containerDelegate = containerDelegate(isElevationPossible)
+        }
+
+        this.isInvisible = isInvisible
+    }
+
+    override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean = false
+
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+    ): MeasureResult {
+        check(isLookingAhead)
+        return measurable.measure(constraints).run {
+            content.targetSize = IntSize(width, height)
+            layout(width, height) {
+                if (!isInvisible) {
+                    place(0, 0, zIndex = content.zIndex)
+                }
+            }
+        }
+    }
+
+    override fun ApproachMeasureScope.approachMeasure(
+        measurable: Measurable,
+        constraints: Constraints,
+    ): MeasureResult {
+        return measurable.measure(constraints).run {
+            layout(width, height) {
+                if (!isInvisible) {
+                    place(0, 0, zIndex = content.zIndex)
+                }
+            }
+        }
+    }
+}
+
 internal class ContentEffects(factory: OverscrollFactory) {
     val overscrollEffect = factory.createOverscrollEffect()
     val gestureEffect = GestureEffect(overscrollEffect)
@@ -307,8 +377,3 @@
         )
     }
 }
-
-private val InvisibleModifier =
-    Modifier.layout { measurable, constraints ->
-        measurable.measure(constraints).run { layout(width, height) {} }
-    }
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 72f9bd5..734de34 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
@@ -29,7 +29,7 @@
 import com.android.compose.animation.scene.transformation.PropertyTransformation
 import com.android.compose.animation.scene.transformation.PropertyTransformationScope
 import com.android.mechanics.MotionValue
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
 import kotlinx.coroutines.CoroutineScope
 
 interface ContainerRevealHaptics {
@@ -53,7 +53,7 @@
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 fun TransitionBuilder.verticalContainerReveal(
     container: ElementKey,
-    motionSpec: EdgeContainerExpansionSpec,
+    motionSpec: VerticalExpandContainerSpec,
     haptics: ContainerRevealHaptics,
 ) {
     // Make the swipe distance be exactly the target height of the container.
diff --git a/packages/SystemUI/compose/scene/tests/Android.bp b/packages/SystemUI/compose/scene/tests/Android.bp
index 2ab27af..d63450b 100644
--- a/packages/SystemUI/compose/scene/tests/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/Android.bp
@@ -42,6 +42,7 @@
         "PlatformMotionTestingCompose",
         "androidx.test.runner",
         "androidx.test.ext.junit",
+        "platform-parametric-runner-lib",
 
         "androidx.compose.runtime_runtime",
         "androidx.compose.ui_ui-test-junit4",
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
new file mode 100644
index 0000000..770f859
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
@@ -0,0 +1,624 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880,
+    896,
+    912,
+    928,
+    944
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 8.4,
+          "y": 5.2
+        },
+        {
+          "x": 11.2,
+          "y": 5.2
+        },
+        {
+          "x": 13.6,
+          "y": 5.2
+        },
+        {
+          "x": 15.6,
+          "y": 5.2
+        },
+        {
+          "x": 16.8,
+          "y": 5.2
+        },
+        {
+          "x": 17.6,
+          "y": 5.2
+        },
+        {
+          "x": 18.4,
+          "y": 5.2
+        },
+        {
+          "x": 18.8,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 292.8
+        },
+        {
+          "width": 150,
+          "height": 292.8
+        },
+        {
+          "width": 150,
+          "height": 286
+        },
+        {
+          "width": 150,
+          "height": 279.6
+        },
+        {
+          "width": 150,
+          "height": 273.2
+        },
+        {
+          "width": 150,
+          "height": 266.8
+        },
+        {
+          "width": 150,
+          "height": 260.4
+        },
+        {
+          "width": 150,
+          "height": 254
+        },
+        {
+          "width": 150,
+          "height": 247.6
+        },
+        {
+          "width": 150,
+          "height": 241.2
+        },
+        {
+          "width": 150,
+          "height": 241.2
+        },
+        {
+          "width": 150,
+          "height": 234.4
+        },
+        {
+          "width": 150,
+          "height": 228
+        },
+        {
+          "width": 150,
+          "height": 221.6
+        },
+        {
+          "width": 150,
+          "height": 215.2
+        },
+        {
+          "width": 150,
+          "height": 208.8
+        },
+        {
+          "width": 150,
+          "height": 202
+        },
+        {
+          "width": 150,
+          "height": 195.6
+        },
+        {
+          "width": 150,
+          "height": 189.2
+        },
+        {
+          "width": 150,
+          "height": 189.2
+        },
+        {
+          "width": 150,
+          "height": 182.8
+        },
+        {
+          "width": 150,
+          "height": 176.4
+        },
+        {
+          "width": 150,
+          "height": 170
+        },
+        {
+          "width": 150,
+          "height": 163.6
+        },
+        {
+          "width": 150,
+          "height": 157.2
+        },
+        {
+          "width": 150,
+          "height": 150.8
+        },
+        {
+          "width": 150,
+          "height": 144.4
+        },
+        {
+          "width": 150,
+          "height": 137.6
+        },
+        {
+          "width": 150,
+          "height": 137.6
+        },
+        {
+          "width": 150,
+          "height": 131.2
+        },
+        {
+          "width": 150,
+          "height": 124.8
+        },
+        {
+          "width": 150,
+          "height": 118.4
+        },
+        {
+          "width": 150,
+          "height": 112
+        },
+        {
+          "width": 150,
+          "height": 112
+        },
+        {
+          "width": 150,
+          "height": 99.2
+        },
+        {
+          "width": 150,
+          "height": 81.2
+        },
+        {
+          "width": 144,
+          "height": 62.8
+        },
+        {
+          "width": 138,
+          "height": 46.4
+        },
+        {
+          "width": 133.2,
+          "height": 32
+        },
+        {
+          "width": 129.6,
+          "height": 20.4
+        },
+        {
+          "width": 127.2,
+          "height": 12
+        },
+        {
+          "width": 125.2,
+          "height": 6.4
+        },
+        {
+          "width": 124,
+          "height": 2.8
+        },
+        {
+          "width": 123.2,
+          "height": 0.4
+        },
+        {
+          "width": 122.4,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99781144,
+        0.87040234,
+        0.6695792,
+        0.48078007,
+        0.33033127,
+        0.22004372,
+        0.1432175,
+        0.09153092,
+        0.057634592,
+        0.035840213,
+        0.022048414,
+        0.013435662,
+        0.008117795,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
new file mode 100644
index 0000000..6b7de56
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
@@ -0,0 +1,644 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880,
+    896,
+    912,
+    928,
+    944,
+    960,
+    976
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 6.8,
+          "y": 5.2
+        },
+        {
+          "x": 10.4,
+          "y": 5.2
+        },
+        {
+          "x": 13.2,
+          "y": 5.2
+        },
+        {
+          "x": 15.2,
+          "y": 5.2
+        },
+        {
+          "x": 16.8,
+          "y": 5.2
+        },
+        {
+          "x": 17.6,
+          "y": 5.2
+        },
+        {
+          "x": 18.4,
+          "y": 5.2
+        },
+        {
+          "x": 18.8,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 297.6
+        },
+        {
+          "width": 150,
+          "height": 292.4
+        },
+        {
+          "width": 150,
+          "height": 287.6
+        },
+        {
+          "width": 150,
+          "height": 282.8
+        },
+        {
+          "width": 150,
+          "height": 278
+        },
+        {
+          "width": 150,
+          "height": 273.2
+        },
+        {
+          "width": 150,
+          "height": 268.4
+        },
+        {
+          "width": 150,
+          "height": 263.6
+        },
+        {
+          "width": 150,
+          "height": 258.8
+        },
+        {
+          "width": 150,
+          "height": 258.8
+        },
+        {
+          "width": 150,
+          "height": 253.6
+        },
+        {
+          "width": 150,
+          "height": 248.8
+        },
+        {
+          "width": 150,
+          "height": 244
+        },
+        {
+          "width": 150,
+          "height": 239.2
+        },
+        {
+          "width": 150,
+          "height": 234.4
+        },
+        {
+          "width": 150,
+          "height": 229.6
+        },
+        {
+          "width": 150,
+          "height": 224.8
+        },
+        {
+          "width": 150,
+          "height": 220
+        },
+        {
+          "width": 150,
+          "height": 220
+        },
+        {
+          "width": 150,
+          "height": 214.8
+        },
+        {
+          "width": 150,
+          "height": 210
+        },
+        {
+          "width": 150,
+          "height": 205.2
+        },
+        {
+          "width": 150,
+          "height": 200.4
+        },
+        {
+          "width": 150,
+          "height": 195.6
+        },
+        {
+          "width": 150,
+          "height": 190.8
+        },
+        {
+          "width": 150,
+          "height": 186
+        },
+        {
+          "width": 150,
+          "height": 181.2
+        },
+        {
+          "width": 150,
+          "height": 181.2
+        },
+        {
+          "width": 150,
+          "height": 176.4
+        },
+        {
+          "width": 150,
+          "height": 171.6
+        },
+        {
+          "width": 150,
+          "height": 166.8
+        },
+        {
+          "width": 150,
+          "height": 161.6
+        },
+        {
+          "width": 150,
+          "height": 161.6
+        },
+        {
+          "width": 150,
+          "height": 147.2
+        },
+        {
+          "width": 150,
+          "height": 122
+        },
+        {
+          "width": 150,
+          "height": 95.2
+        },
+        {
+          "width": 146.8,
+          "height": 70.8
+        },
+        {
+          "width": 139.6,
+          "height": 50.8
+        },
+        {
+          "width": 134,
+          "height": 34
+        },
+        {
+          "width": 130,
+          "height": 19.2
+        },
+        {
+          "width": 127.2,
+          "height": 9.2
+        },
+        {
+          "width": 125.2,
+          "height": 2.8
+        },
+        {
+          "width": 123.6,
+          "height": 0
+        },
+        {
+          "width": 122.8,
+          "height": 0
+        },
+        {
+          "width": 122.4,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99979615,
+        0.8860379,
+        0.6869267,
+        0.4955439,
+        0.34154767,
+        0.22803628,
+        0.14868057,
+        0.09515619,
+        0.059987247,
+        0.037340224,
+        0.02299112,
+        0.01402092,
+        0.008477271,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
new file mode 100644
index 0000000..13f75d2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragOpen.json
@@ -0,0 +1,544 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 18,
+          "y": 5.2
+        },
+        {
+          "x": 18,
+          "y": 5.2
+        },
+        {
+          "x": 16.8,
+          "y": 5.2
+        },
+        {
+          "x": 16,
+          "y": 5.2
+        },
+        {
+          "x": 14.8,
+          "y": 5.2
+        },
+        {
+          "x": 13.6,
+          "y": 5.2
+        },
+        {
+          "x": 12.4,
+          "y": 5.2
+        },
+        {
+          "x": 11.2,
+          "y": 5.2
+        },
+        {
+          "x": 10.4,
+          "y": 5.2
+        },
+        {
+          "x": 9.2,
+          "y": 5.2
+        },
+        {
+          "x": 9.2,
+          "y": 5.2
+        },
+        {
+          "x": 8,
+          "y": 5.2
+        },
+        {
+          "x": 6.8,
+          "y": 5.2
+        },
+        {
+          "x": 5.6,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 124.4,
+          "height": 1.6
+        },
+        {
+          "width": 124.4,
+          "height": 1.6
+        },
+        {
+          "width": 126.8,
+          "height": 3.2
+        },
+        {
+          "width": 128.8,
+          "height": 4.8
+        },
+        {
+          "width": 131.2,
+          "height": 6.4
+        },
+        {
+          "width": 133.6,
+          "height": 8
+        },
+        {
+          "width": 135.6,
+          "height": 9.6
+        },
+        {
+          "width": 138,
+          "height": 11.2
+        },
+        {
+          "width": 140,
+          "height": 12.8
+        },
+        {
+          "width": 142.4,
+          "height": 14.4
+        },
+        {
+          "width": 142.4,
+          "height": 14.4
+        },
+        {
+          "width": 144.8,
+          "height": 16.4
+        },
+        {
+          "width": 147.2,
+          "height": 18
+        },
+        {
+          "width": 149.2,
+          "height": 19.6
+        },
+        {
+          "width": 150,
+          "height": 25.6
+        },
+        {
+          "width": 150,
+          "height": 36.4
+        },
+        {
+          "width": 150,
+          "height": 45.6
+        },
+        {
+          "width": 150,
+          "height": 59.2
+        },
+        {
+          "width": 150,
+          "height": 72.8
+        },
+        {
+          "width": 150,
+          "height": 79.6
+        },
+        {
+          "width": 150,
+          "height": 92.8
+        },
+        {
+          "width": 150,
+          "height": 104.4
+        },
+        {
+          "width": 150,
+          "height": 115.2
+        },
+        {
+          "width": 150,
+          "height": 125.2
+        },
+        {
+          "width": 150,
+          "height": 134.8
+        },
+        {
+          "width": 150,
+          "height": 143.2
+        },
+        {
+          "width": 150,
+          "height": 151.2
+        },
+        {
+          "width": 150,
+          "height": 158.8
+        },
+        {
+          "width": 150,
+          "height": 160
+        },
+        {
+          "width": 150,
+          "height": 167.2
+        },
+        {
+          "width": 150,
+          "height": 174.4
+        },
+        {
+          "width": 150,
+          "height": 180.8
+        },
+        {
+          "width": 150,
+          "height": 187.6
+        },
+        {
+          "width": 150,
+          "height": 188
+        },
+        {
+          "width": 150,
+          "height": 200.4
+        },
+        {
+          "width": 150,
+          "height": 218.4
+        },
+        {
+          "width": 150,
+          "height": 236.8
+        },
+        {
+          "width": 150,
+          "height": 253.2
+        },
+        {
+          "width": 150,
+          "height": 266.8
+        },
+        {
+          "width": 150,
+          "height": 277.2
+        },
+        {
+          "width": 150,
+          "height": 284.8
+        },
+        {
+          "width": 150,
+          "height": 290
+        },
+        {
+          "width": 150,
+          "height": 294
+        },
+        {
+          "width": 150,
+          "height": 296.4
+        },
+        {
+          "width": 150,
+          "height": 298
+        },
+        {
+          "width": 150,
+          "height": 298.8
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 300
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0,
+        0,
+        0.0067873597,
+        0.06125766,
+        0.19080031,
+        0.39327443,
+        0.5711931,
+        0.7085583,
+        0.8074065,
+        0.8754226,
+        0.9207788,
+        0.95032376,
+        0.9692185,
+        0.98112255,
+        0.9885286,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
new file mode 100644
index 0000000..015df8f
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
@@ -0,0 +1,434 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 6.4,
+          "y": 5.2
+        },
+        {
+          "x": 10.4,
+          "y": 5.2
+        },
+        {
+          "x": 13.6,
+          "y": 5.2
+        },
+        {
+          "x": 15.6,
+          "y": 5.2
+        },
+        {
+          "x": 17.2,
+          "y": 5.2
+        },
+        {
+          "x": 18,
+          "y": 5.2
+        },
+        {
+          "x": 18.8,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 288.8
+        },
+        {
+          "width": 150,
+          "height": 278.8
+        },
+        {
+          "width": 150,
+          "height": 266
+        },
+        {
+          "width": 150,
+          "height": 252
+        },
+        {
+          "width": 150,
+          "height": 252
+        },
+        {
+          "width": 150,
+          "height": 224
+        },
+        {
+          "width": 150,
+          "height": 183.2
+        },
+        {
+          "width": 150,
+          "height": 141.6
+        },
+        {
+          "width": 150,
+          "height": 104.4
+        },
+        {
+          "width": 148,
+          "height": 74.4
+        },
+        {
+          "width": 139.6,
+          "height": 51.2
+        },
+        {
+          "width": 133.6,
+          "height": 32.4
+        },
+        {
+          "width": 129.6,
+          "height": 15.6
+        },
+        {
+          "width": 126.4,
+          "height": 5.2
+        },
+        {
+          "width": 124.4,
+          "height": 0
+        },
+        {
+          "width": 123.2,
+          "height": 0
+        },
+        {
+          "width": 122.4,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99532944,
+        0.8594856,
+        0.65783304,
+        0.47089338,
+        0.32286334,
+        0.21474117,
+        0.139602,
+        0.089136004,
+        0.056082606,
+        0.03485179,
+        0.02142787,
+        0.013050735,
+        0.007881463,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
similarity index 61%
copy from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json
copy to packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
index d8bf48d..f202fcd 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingOpen.json
@@ -32,8 +32,7 @@
     464,
     480,
     496,
-    512,
-    528
+    512
   ],
   "features": [
     {
@@ -65,108 +64,104 @@
           "type": "not_found"
         },
         {
-          "x": 62.4,
-          "y": 50
+          "x": 17.6,
+          "y": 5.2
         },
         {
-          "x": 61.2,
-          "y": 50
+          "x": 16.4,
+          "y": 5.2
         },
         {
-          "x": 59.2,
-          "y": 50
+          "x": 14.4,
+          "y": 5.2
         },
         {
-          "x": 57.2,
-          "y": 50
+          "x": 12.4,
+          "y": 5.2
         },
         {
-          "x": 54.8,
-          "y": 50
+          "x": 10,
+          "y": 5.2
         },
         {
-          "x": 52.4,
-          "y": 50
+          "x": 7.6,
+          "y": 5.2
         },
         {
-          "x": 52.4,
-          "y": 50
+          "x": 7.6,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         }
       ]
     },
@@ -199,108 +194,104 @@
           "type": "not_found"
         },
         {
-          "width": 163.2,
+          "width": 125.2,
           "height": 2
         },
         {
-          "width": 166,
+          "width": 128,
           "height": 4.4
         },
         {
-          "width": 170,
+          "width": 132,
           "height": 6.8
         },
         {
-          "width": 174,
+          "width": 136,
           "height": 10
         },
         {
-          "width": 178.4,
+          "width": 140.4,
           "height": 13.2
         },
         {
-          "width": 183.6,
+          "width": 145.6,
           "height": 16.8
         },
         {
-          "width": 183.6,
+          "width": 145.6,
           "height": 16.8
         },
         {
-          "width": 188,
-          "height": 42.4
+          "width": 150,
+          "height": 36.8
         },
         {
-          "width": 188,
-          "height": 100
+          "width": 150,
+          "height": 81.2
         },
         {
-          "width": 188,
-          "height": 161.6
+          "width": 150,
+          "height": 126.8
         },
         {
-          "width": 188,
-          "height": 218
+          "width": 150,
+          "height": 168
         },
         {
-          "width": 188,
-          "height": 265.6
+          "width": 150,
+          "height": 202.8
         },
         {
-          "width": 188,
-          "height": 303.6
+          "width": 150,
+          "height": 230
         },
         {
-          "width": 188,
-          "height": 332.4
+          "width": 150,
+          "height": 250.8
         },
         {
-          "width": 188,
-          "height": 354
+          "width": 150,
+          "height": 266.4
         },
         {
-          "width": 188,
-          "height": 369.2
+          "width": 150,
+          "height": 277.6
         },
         {
-          "width": 188,
-          "height": 380
+          "width": 150,
+          "height": 285.2
         },
         {
-          "width": 188,
-          "height": 387.2
+          "width": 150,
+          "height": 290.4
         },
         {
-          "width": 188,
-          "height": 392
+          "width": 150,
+          "height": 294
         },
         {
-          "width": 188,
-          "height": 395.2
+          "width": 150,
+          "height": 296.4
         },
         {
-          "width": 188,
-          "height": 397.6
+          "width": 150,
+          "height": 298
         },
         {
-          "width": 188,
-          "height": 398.4
+          "width": 150,
+          "height": 298.8
         },
         {
-          "width": 188,
-          "height": 398.8
+          "width": 150,
+          "height": 299.2
         },
         {
-          "width": 188,
-          "height": 399.2
+          "width": 150,
+          "height": 299.6
         },
         {
-          "width": 188,
-          "height": 399.6
-        },
-        {
-          "width": 188,
-          "height": 399.6
+          "width": 150,
+          "height": 299.6
         }
       ]
     },
@@ -356,7 +347,6 @@
         1,
         1,
         1,
-        1,
         1
       ]
     }
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
new file mode 100644
index 0000000..4c57bda
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -0,0 +1,724 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880,
+    896,
+    912,
+    928,
+    944,
+    960,
+    976,
+    992,
+    1008,
+    1024,
+    1040,
+    1056,
+    1072,
+    1088,
+    1104
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 18,
+          "y": 5.2
+        },
+        {
+          "x": 17.2,
+          "y": 5.2
+        },
+        {
+          "x": 16.4,
+          "y": 5.2
+        },
+        {
+          "x": 15.6,
+          "y": 5.2
+        },
+        {
+          "x": 14.8,
+          "y": 5.2
+        },
+        {
+          "x": 14,
+          "y": 5.2
+        },
+        {
+          "x": 13.2,
+          "y": 5.2
+        },
+        {
+          "x": 12.4,
+          "y": 5.2
+        },
+        {
+          "x": 11.6,
+          "y": 5.2
+        },
+        {
+          "x": 10.8,
+          "y": 5.2
+        },
+        {
+          "x": 10.4,
+          "y": 5.2
+        },
+        {
+          "x": 9.6,
+          "y": 5.2
+        },
+        {
+          "x": 8.8,
+          "y": 5.2
+        },
+        {
+          "x": 8.4,
+          "y": 5.2
+        },
+        {
+          "x": 8,
+          "y": 5.2
+        },
+        {
+          "x": 7.2,
+          "y": 5.2
+        },
+        {
+          "x": 6.8,
+          "y": 5.2
+        },
+        {
+          "x": 6.4,
+          "y": 5.2
+        },
+        {
+          "x": 6,
+          "y": 5.2
+        },
+        {
+          "x": 5.6,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.6,
+          "y": 5.2
+        },
+        {
+          "x": 6,
+          "y": 5.2
+        },
+        {
+          "x": 6.4,
+          "y": 5.2
+        },
+        {
+          "x": 6.8,
+          "y": 5.2
+        },
+        {
+          "x": 7.2,
+          "y": 5.2
+        },
+        {
+          "x": 8,
+          "y": 5.2
+        },
+        {
+          "x": 8.4,
+          "y": 5.2
+        },
+        {
+          "x": 8.8,
+          "y": 5.2
+        },
+        {
+          "x": 9.6,
+          "y": 5.2
+        },
+        {
+          "x": 10.4,
+          "y": 5.2
+        },
+        {
+          "x": 10.8,
+          "y": 5.2
+        },
+        {
+          "x": 11.6,
+          "y": 5.2
+        },
+        {
+          "x": 12.4,
+          "y": 5.2
+        },
+        {
+          "x": 13.2,
+          "y": 5.2
+        },
+        {
+          "x": 14,
+          "y": 5.2
+        },
+        {
+          "x": 14.8,
+          "y": 5.2
+        },
+        {
+          "x": 15.6,
+          "y": 5.2
+        },
+        {
+          "x": 16.4,
+          "y": 5.2
+        },
+        {
+          "x": 17.2,
+          "y": 5.2
+        },
+        {
+          "x": 18,
+          "y": 5.2
+        },
+        {
+          "x": 18.8,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 124.4,
+          "height": 1.6
+        },
+        {
+          "width": 126,
+          "height": 2.8
+        },
+        {
+          "width": 128,
+          "height": 4
+        },
+        {
+          "width": 129.6,
+          "height": 5.2
+        },
+        {
+          "width": 131.2,
+          "height": 6.4
+        },
+        {
+          "width": 132.8,
+          "height": 7.6
+        },
+        {
+          "width": 134.4,
+          "height": 8.8
+        },
+        {
+          "width": 136,
+          "height": 10
+        },
+        {
+          "width": 137.2,
+          "height": 10.8
+        },
+        {
+          "width": 138.8,
+          "height": 12
+        },
+        {
+          "width": 140,
+          "height": 12.8
+        },
+        {
+          "width": 141.2,
+          "height": 13.6
+        },
+        {
+          "width": 142.8,
+          "height": 14.8
+        },
+        {
+          "width": 144,
+          "height": 15.6
+        },
+        {
+          "width": 144.8,
+          "height": 16.4
+        },
+        {
+          "width": 146,
+          "height": 17.2
+        },
+        {
+          "width": 146.8,
+          "height": 17.6
+        },
+        {
+          "width": 148,
+          "height": 18.4
+        },
+        {
+          "width": 148.8,
+          "height": 19.2
+        },
+        {
+          "width": 149.6,
+          "height": 19.6
+        },
+        {
+          "width": 150,
+          "height": 21.2
+        },
+        {
+          "width": 150,
+          "height": 24.8
+        },
+        {
+          "width": 150,
+          "height": 30
+        },
+        {
+          "width": 150,
+          "height": 38
+        },
+        {
+          "width": 150,
+          "height": 46
+        },
+        {
+          "width": 150,
+          "height": 54
+        },
+        {
+          "width": 150,
+          "height": 61.2
+        },
+        {
+          "width": 150,
+          "height": 66.8
+        },
+        {
+          "width": 150,
+          "height": 71.6
+        },
+        {
+          "width": 150,
+          "height": 75.6
+        },
+        {
+          "width": 150,
+          "height": 78
+        },
+        {
+          "width": 150,
+          "height": 79.6
+        },
+        {
+          "width": 150,
+          "height": 80.8
+        },
+        {
+          "width": 150,
+          "height": 80.8
+        },
+        {
+          "width": 150,
+          "height": 80.4
+        },
+        {
+          "width": 150,
+          "height": 79.6
+        },
+        {
+          "width": 149.6,
+          "height": 78
+        },
+        {
+          "width": 148.8,
+          "height": 76.4
+        },
+        {
+          "width": 148,
+          "height": 74
+        },
+        {
+          "width": 146.8,
+          "height": 71.6
+        },
+        {
+          "width": 146,
+          "height": 69.2
+        },
+        {
+          "width": 144.8,
+          "height": 66
+        },
+        {
+          "width": 144,
+          "height": 62.8
+        },
+        {
+          "width": 142.8,
+          "height": 59.2
+        },
+        {
+          "width": 141.2,
+          "height": 55.6
+        },
+        {
+          "width": 140,
+          "height": 52
+        },
+        {
+          "width": 138.8,
+          "height": 48
+        },
+        {
+          "width": 137.2,
+          "height": 44
+        },
+        {
+          "width": 136,
+          "height": 40
+        },
+        {
+          "width": 134.4,
+          "height": 37.6
+        },
+        {
+          "width": 132.8,
+          "height": 38
+        },
+        {
+          "width": 131.2,
+          "height": 30.4
+        },
+        {
+          "width": 129.6,
+          "height": 25.2
+        },
+        {
+          "width": 128,
+          "height": 20.4
+        },
+        {
+          "width": 126,
+          "height": 16
+        },
+        {
+          "width": 124.4,
+          "height": 12.4
+        },
+        {
+          "width": 122.8,
+          "height": 9.2
+        },
+        {
+          "width": 122,
+          "height": 6.8
+        },
+        {
+          "width": 122,
+          "height": 5.2
+        },
+        {
+          "width": 122,
+          "height": 3.6
+        },
+        {
+          "width": 122,
+          "height": 2.4
+        },
+        {
+          "width": 122,
+          "height": 1.6
+        },
+        {
+          "width": 122,
+          "height": 0.8
+        },
+        {
+          "width": 122,
+          "height": 0.4
+        },
+        {
+          "width": 122,
+          "height": 0.4
+        },
+        {
+          "width": 122,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0,
+        0,
+        0.012518823,
+        0.0741024,
+        0.22542936,
+        0.42628878,
+        0.5976642,
+        0.7280313,
+        0.82100236,
+        0.8845844,
+        0.9267946,
+        0.95419544,
+        0.9716705,
+        0.98265487,
+        0.98947525,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.9944124,
+        0.9417388,
+        0.81841844,
+        0.61578125,
+        0.43616113,
+        0.29689062,
+        0.19641556,
+        0.12716138,
+        0.080922,
+        0.050773032,
+        0.031477194,
+        0.019312754,
+        0.011740657,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
new file mode 100644
index 0000000..26c80e3
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealCloseTransition.json
@@ -0,0 +1,314 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 7.2,
+          "y": 5.2
+        },
+        {
+          "x": 11.2,
+          "y": 5.2
+        },
+        {
+          "x": 14,
+          "y": 5.2
+        },
+        {
+          "x": 16,
+          "y": 5.2
+        },
+        {
+          "x": 17.6,
+          "y": 5.2
+        },
+        {
+          "x": 18.4,
+          "y": 5.2
+        },
+        {
+          "x": 18.8,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 278.8
+        },
+        {
+          "width": 150,
+          "height": 234.8
+        },
+        {
+          "width": 150,
+          "height": 185.2
+        },
+        {
+          "width": 150,
+          "height": 138.8
+        },
+        {
+          "width": 150,
+          "height": 100.4
+        },
+        {
+          "width": 146.4,
+          "height": 69.6
+        },
+        {
+          "width": 138.4,
+          "height": 46.8
+        },
+        {
+          "width": 132.4,
+          "height": 28
+        },
+        {
+          "width": 128.4,
+          "height": 13.2
+        },
+        {
+          "width": 125.6,
+          "height": 4
+        },
+        {
+          "width": 124,
+          "height": 0
+        },
+        {
+          "width": 122.8,
+          "height": 0
+        },
+        {
+          "width": 122.4,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 122,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.9762947,
+        0.8118515,
+        0.60931784,
+        0.43090785,
+        0.29299664,
+        0.19368339,
+        0.12531388,
+        0.079705715,
+        0.049988627,
+        0.030979574,
+        0.019001365,
+        0.011548042,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json
new file mode 100644
index 0000000..7a02d36
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_triggeredRevealOpenTransition.json
@@ -0,0 +1,224 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 19.2,
+          "y": 5.2
+        },
+        {
+          "x": 15.6,
+          "y": 5.2
+        },
+        {
+          "x": 8,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 122,
+          "height": 0
+        },
+        {
+          "width": 129.2,
+          "height": 5.2
+        },
+        {
+          "width": 144.4,
+          "height": 16
+        },
+        {
+          "width": 150,
+          "height": 62
+        },
+        {
+          "width": 150,
+          "height": 118.4
+        },
+        {
+          "width": 150,
+          "height": 166
+        },
+        {
+          "width": 150,
+          "height": 204
+        },
+        {
+          "width": 150,
+          "height": 233.2
+        },
+        {
+          "width": 150,
+          "height": 254.4
+        },
+        {
+          "width": 150,
+          "height": 270
+        },
+        {
+          "width": 150,
+          "height": 280.8
+        },
+        {
+          "width": 150,
+          "height": 288
+        },
+        {
+          "width": 150,
+          "height": 292.8
+        },
+        {
+          "width": 150,
+          "height": 296
+        },
+        {
+          "width": 150,
+          "height": 298
+        },
+        {
+          "width": 150,
+          "height": 298.8
+        },
+        {
+          "width": 150,
+          "height": 299.2
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0.0951103,
+        0.2911651,
+        0.48551244,
+        0.6439433,
+        0.76157355,
+        0.8441935,
+        0.9001033,
+        0.9369305,
+        0.96069145,
+        0.97577035,
+        0.98520935,
+        0.9910494,
+        1,
+        1,
+        1,
+        1,
+        1
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
new file mode 100644
index 0000000..5ac0621
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
@@ -0,0 +1,584 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 292.8
+        },
+        {
+          "width": 150,
+          "height": 292.8
+        },
+        {
+          "width": 150,
+          "height": 286
+        },
+        {
+          "width": 150,
+          "height": 279.6
+        },
+        {
+          "width": 150,
+          "height": 273.2
+        },
+        {
+          "width": 150,
+          "height": 266.8
+        },
+        {
+          "width": 150,
+          "height": 260.4
+        },
+        {
+          "width": 150,
+          "height": 254
+        },
+        {
+          "width": 150,
+          "height": 247.6
+        },
+        {
+          "width": 150,
+          "height": 241.2
+        },
+        {
+          "width": 150,
+          "height": 241.2
+        },
+        {
+          "width": 150,
+          "height": 234.4
+        },
+        {
+          "width": 150,
+          "height": 228
+        },
+        {
+          "width": 150,
+          "height": 221.6
+        },
+        {
+          "width": 150,
+          "height": 215.2
+        },
+        {
+          "width": 150,
+          "height": 208.8
+        },
+        {
+          "width": 150,
+          "height": 202
+        },
+        {
+          "width": 150,
+          "height": 195.6
+        },
+        {
+          "width": 150,
+          "height": 189.2
+        },
+        {
+          "width": 150,
+          "height": 189.2
+        },
+        {
+          "width": 150,
+          "height": 182.8
+        },
+        {
+          "width": 150,
+          "height": 176.4
+        },
+        {
+          "width": 150,
+          "height": 170
+        },
+        {
+          "width": 150,
+          "height": 163.6
+        },
+        {
+          "width": 150,
+          "height": 157.2
+        },
+        {
+          "width": 150,
+          "height": 150.8
+        },
+        {
+          "width": 150,
+          "height": 144.4
+        },
+        {
+          "width": 150,
+          "height": 137.6
+        },
+        {
+          "width": 150,
+          "height": 137.6
+        },
+        {
+          "width": 150,
+          "height": 131.2
+        },
+        {
+          "width": 150,
+          "height": 124.8
+        },
+        {
+          "width": 150,
+          "height": 118.4
+        },
+        {
+          "width": 150,
+          "height": 112
+        },
+        {
+          "width": 150,
+          "height": 112
+        },
+        {
+          "width": 150,
+          "height": 99.2
+        },
+        {
+          "width": 150,
+          "height": 84.4
+        },
+        {
+          "width": 150,
+          "height": 70.8
+        },
+        {
+          "width": 150,
+          "height": 58
+        },
+        {
+          "width": 150,
+          "height": 46.4
+        },
+        {
+          "width": 150,
+          "height": 36.4
+        },
+        {
+          "width": 150,
+          "height": 28
+        },
+        {
+          "width": 150,
+          "height": 20.8
+        },
+        {
+          "width": 150,
+          "height": 15.6
+        },
+        {
+          "width": 150,
+          "height": 11.2
+        },
+        {
+          "width": 150,
+          "height": 8
+        },
+        {
+          "width": 150,
+          "height": 5.6
+        },
+        {
+          "width": 150,
+          "height": 3.6
+        },
+        {
+          "width": 150,
+          "height": 2.4
+        },
+        {
+          "width": 150,
+          "height": 1.2
+        },
+        {
+          "width": 150,
+          "height": 0.8
+        },
+        {
+          "width": 150,
+          "height": 0.4
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99781144,
+        0.87040234,
+        0.6695792,
+        0.48078007,
+        0.33033127,
+        0.22004372,
+        0.1432175,
+        0.09153092,
+        0.057634592,
+        0.035840213,
+        0.022048414,
+        0.013435662,
+        0.008117795,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
new file mode 100644
index 0000000..1cae67b
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
@@ -0,0 +1,624 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880,
+    896,
+    912,
+    928,
+    944
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 297.6
+        },
+        {
+          "width": 150,
+          "height": 292.4
+        },
+        {
+          "width": 150,
+          "height": 287.6
+        },
+        {
+          "width": 150,
+          "height": 282.8
+        },
+        {
+          "width": 150,
+          "height": 278
+        },
+        {
+          "width": 150,
+          "height": 273.2
+        },
+        {
+          "width": 150,
+          "height": 268.4
+        },
+        {
+          "width": 150,
+          "height": 263.6
+        },
+        {
+          "width": 150,
+          "height": 258.8
+        },
+        {
+          "width": 150,
+          "height": 258.8
+        },
+        {
+          "width": 150,
+          "height": 253.6
+        },
+        {
+          "width": 150,
+          "height": 248.8
+        },
+        {
+          "width": 150,
+          "height": 244
+        },
+        {
+          "width": 150,
+          "height": 239.2
+        },
+        {
+          "width": 150,
+          "height": 234.4
+        },
+        {
+          "width": 150,
+          "height": 229.6
+        },
+        {
+          "width": 150,
+          "height": 224.8
+        },
+        {
+          "width": 150,
+          "height": 220
+        },
+        {
+          "width": 150,
+          "height": 220
+        },
+        {
+          "width": 150,
+          "height": 214.8
+        },
+        {
+          "width": 150,
+          "height": 210
+        },
+        {
+          "width": 150,
+          "height": 205.2
+        },
+        {
+          "width": 150,
+          "height": 200.4
+        },
+        {
+          "width": 150,
+          "height": 195.6
+        },
+        {
+          "width": 150,
+          "height": 190.8
+        },
+        {
+          "width": 150,
+          "height": 186
+        },
+        {
+          "width": 150,
+          "height": 181.2
+        },
+        {
+          "width": 150,
+          "height": 181.2
+        },
+        {
+          "width": 150,
+          "height": 176.4
+        },
+        {
+          "width": 150,
+          "height": 171.6
+        },
+        {
+          "width": 150,
+          "height": 166.8
+        },
+        {
+          "width": 150,
+          "height": 161.6
+        },
+        {
+          "width": 150,
+          "height": 161.6
+        },
+        {
+          "width": 150,
+          "height": 147.2
+        },
+        {
+          "width": 150,
+          "height": 122
+        },
+        {
+          "width": 150,
+          "height": 95.2
+        },
+        {
+          "width": 150,
+          "height": 70.8
+        },
+        {
+          "width": 150,
+          "height": 51.6
+        },
+        {
+          "width": 150,
+          "height": 36.8
+        },
+        {
+          "width": 150,
+          "height": 25.6
+        },
+        {
+          "width": 150,
+          "height": 17.2
+        },
+        {
+          "width": 150,
+          "height": 11.2
+        },
+        {
+          "width": 150,
+          "height": 6.8
+        },
+        {
+          "width": 150,
+          "height": 4
+        },
+        {
+          "width": 150,
+          "height": 2
+        },
+        {
+          "width": 150,
+          "height": 0.8
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99979615,
+        0.8860379,
+        0.6869267,
+        0.4955439,
+        0.34154767,
+        0.22803628,
+        0.14868057,
+        0.09515619,
+        0.059987247,
+        0.037340224,
+        0.02299112,
+        0.01402092,
+        0.008477271,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
new file mode 100644
index 0000000..1bf6a62
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragOpen.json
@@ -0,0 +1,544 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 150,
+          "height": 1.6
+        },
+        {
+          "width": 150,
+          "height": 1.6
+        },
+        {
+          "width": 150,
+          "height": 3.2
+        },
+        {
+          "width": 150,
+          "height": 4.8
+        },
+        {
+          "width": 150,
+          "height": 6.4
+        },
+        {
+          "width": 150,
+          "height": 8
+        },
+        {
+          "width": 150,
+          "height": 9.6
+        },
+        {
+          "width": 150,
+          "height": 11.2
+        },
+        {
+          "width": 150,
+          "height": 12.8
+        },
+        {
+          "width": 150,
+          "height": 14.4
+        },
+        {
+          "width": 150,
+          "height": 14.4
+        },
+        {
+          "width": 150,
+          "height": 16.4
+        },
+        {
+          "width": 150,
+          "height": 18
+        },
+        {
+          "width": 150,
+          "height": 19.6
+        },
+        {
+          "width": 150,
+          "height": 20.8
+        },
+        {
+          "width": 150,
+          "height": 22.8
+        },
+        {
+          "width": 150,
+          "height": 24.4
+        },
+        {
+          "width": 150,
+          "height": 26
+        },
+        {
+          "width": 150,
+          "height": 27.6
+        },
+        {
+          "width": 150,
+          "height": 27.6
+        },
+        {
+          "width": 150,
+          "height": 29.2
+        },
+        {
+          "width": 150,
+          "height": 30.8
+        },
+        {
+          "width": 150,
+          "height": 32.4
+        },
+        {
+          "width": 150,
+          "height": 34
+        },
+        {
+          "width": 150,
+          "height": 40.4
+        },
+        {
+          "width": 150,
+          "height": 52.4
+        },
+        {
+          "width": 150,
+          "height": 64.8
+        },
+        {
+          "width": 150,
+          "height": 83.2
+        },
+        {
+          "width": 150,
+          "height": 96
+        },
+        {
+          "width": 150,
+          "height": 114.8
+        },
+        {
+          "width": 150,
+          "height": 132
+        },
+        {
+          "width": 150,
+          "height": 148
+        },
+        {
+          "width": 150,
+          "height": 162
+        },
+        {
+          "width": 150,
+          "height": 168.4
+        },
+        {
+          "width": 150,
+          "height": 186
+        },
+        {
+          "width": 150,
+          "height": 208
+        },
+        {
+          "width": 150,
+          "height": 229.6
+        },
+        {
+          "width": 150,
+          "height": 248.4
+        },
+        {
+          "width": 150,
+          "height": 263.2
+        },
+        {
+          "width": 150,
+          "height": 274.8
+        },
+        {
+          "width": 150,
+          "height": 283.2
+        },
+        {
+          "width": 150,
+          "height": 289.2
+        },
+        {
+          "width": 150,
+          "height": 293.6
+        },
+        {
+          "width": 150,
+          "height": 296
+        },
+        {
+          "width": 150,
+          "height": 298
+        },
+        {
+          "width": 150,
+          "height": 298.8
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 300
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0,
+        0,
+        0.0067873597,
+        0.06125766,
+        0.19080031,
+        0.39327443,
+        0.5711931,
+        0.7085583,
+        0.8074065,
+        0.8754226,
+        0.9207788,
+        0.95032376,
+        0.9692185,
+        0.98112255,
+        0.9885286,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
new file mode 100644
index 0000000..ca87bc2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
@@ -0,0 +1,424 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 288.8
+        },
+        {
+          "width": 150,
+          "height": 278.8
+        },
+        {
+          "width": 150,
+          "height": 266
+        },
+        {
+          "width": 150,
+          "height": 252
+        },
+        {
+          "width": 150,
+          "height": 252
+        },
+        {
+          "width": 150,
+          "height": 224
+        },
+        {
+          "width": 150,
+          "height": 183.2
+        },
+        {
+          "width": 150,
+          "height": 141.6
+        },
+        {
+          "width": 150,
+          "height": 104.4
+        },
+        {
+          "width": 150,
+          "height": 72.4
+        },
+        {
+          "width": 150,
+          "height": 46.4
+        },
+        {
+          "width": 150,
+          "height": 28
+        },
+        {
+          "width": 150,
+          "height": 15.6
+        },
+        {
+          "width": 150,
+          "height": 7.2
+        },
+        {
+          "width": 150,
+          "height": 2
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.99532944,
+        0.8594856,
+        0.65783304,
+        0.47089338,
+        0.32286334,
+        0.21474117,
+        0.139602,
+        0.089136004,
+        0.056082606,
+        0.03485179,
+        0.02142787,
+        0.013050735,
+        0.007881463,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
similarity index 61%
rename from packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json
rename to packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
index d8bf48d..98519db 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingOpen.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingOpen.json
@@ -33,7 +33,8 @@
     480,
     496,
     512,
-    528
+    528,
+    544
   ],
   "features": [
     {
@@ -65,108 +66,112 @@
           "type": "not_found"
         },
         {
-          "x": 62.4,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 61.2,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 59.2,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 57.2,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 54.8,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 52.4,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 52.4,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
         },
         {
-          "x": 50,
-          "y": 50
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
         }
       ]
     },
@@ -199,108 +204,112 @@
           "type": "not_found"
         },
         {
-          "width": 163.2,
+          "width": 150,
           "height": 2
         },
         {
-          "width": 166,
+          "width": 150,
           "height": 4.4
         },
         {
-          "width": 170,
+          "width": 150,
           "height": 6.8
         },
         {
-          "width": 174,
+          "width": 150,
           "height": 10
         },
         {
-          "width": 178.4,
+          "width": 150,
           "height": 13.2
         },
         {
-          "width": 183.6,
+          "width": 150,
           "height": 16.8
         },
         {
-          "width": 183.6,
+          "width": 150,
           "height": 16.8
         },
         {
-          "width": 188,
-          "height": 42.4
+          "width": 150,
+          "height": 23.6
         },
         {
-          "width": 188,
-          "height": 100
+          "width": 150,
+          "height": 32.8
         },
         {
-          "width": 188,
-          "height": 161.6
+          "width": 150,
+          "height": 76.8
         },
         {
-          "width": 188,
-          "height": 218
+          "width": 150,
+          "height": 123.6
         },
         {
-          "width": 188,
-          "height": 265.6
+          "width": 150,
+          "height": 164.8
         },
         {
-          "width": 188,
-          "height": 303.6
+          "width": 150,
+          "height": 198.4
         },
         {
-          "width": 188,
-          "height": 332.4
+          "width": 150,
+          "height": 225.6
         },
         {
-          "width": 188,
-          "height": 354
+          "width": 150,
+          "height": 246.4
         },
         {
-          "width": 188,
-          "height": 369.2
+          "width": 150,
+          "height": 262
         },
         {
-          "width": 188,
-          "height": 380
+          "width": 150,
+          "height": 273.2
         },
         {
-          "width": 188,
-          "height": 387.2
+          "width": 150,
+          "height": 281.6
         },
         {
-          "width": 188,
-          "height": 392
+          "width": 150,
+          "height": 287.6
         },
         {
-          "width": 188,
-          "height": 395.2
+          "width": 150,
+          "height": 292
         },
         {
-          "width": 188,
-          "height": 397.6
+          "width": 150,
+          "height": 294.8
         },
         {
-          "width": 188,
-          "height": 398.4
+          "width": 150,
+          "height": 296.4
         },
         {
-          "width": 188,
-          "height": 398.8
+          "width": 150,
+          "height": 297.6
         },
         {
-          "width": 188,
-          "height": 399.2
+          "width": 150,
+          "height": 298.4
         },
         {
-          "width": 188,
-          "height": 399.6
+          "width": 150,
+          "height": 299.2
         },
         {
-          "width": 188,
-          "height": 399.6
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 299.6
         }
       ]
     },
@@ -357,6 +366,7 @@
         1,
         1,
         1,
+        1,
         1
       ]
     }
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
new file mode 100644
index 0000000..850cee9
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_magneticDetachAndReattach.json
@@ -0,0 +1,744 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432,
+    448,
+    464,
+    480,
+    496,
+    512,
+    528,
+    544,
+    560,
+    576,
+    592,
+    608,
+    624,
+    640,
+    656,
+    672,
+    688,
+    704,
+    720,
+    736,
+    752,
+    768,
+    784,
+    800,
+    816,
+    832,
+    848,
+    864,
+    880,
+    896,
+    912,
+    928,
+    944,
+    960,
+    976,
+    992,
+    1008,
+    1024,
+    1040,
+    1056,
+    1072,
+    1088,
+    1104,
+    1120,
+    1136
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 150,
+          "height": 2.8
+        },
+        {
+          "width": 150,
+          "height": 4.8
+        },
+        {
+          "width": 150,
+          "height": 6.8
+        },
+        {
+          "width": 150,
+          "height": 8.4
+        },
+        {
+          "width": 150,
+          "height": 10.4
+        },
+        {
+          "width": 150,
+          "height": 12.4
+        },
+        {
+          "width": 150,
+          "height": 14
+        },
+        {
+          "width": 150,
+          "height": 16
+        },
+        {
+          "width": 150,
+          "height": 17.6
+        },
+        {
+          "width": 150,
+          "height": 19.2
+        },
+        {
+          "width": 150,
+          "height": 20.8
+        },
+        {
+          "width": 150,
+          "height": 22.4
+        },
+        {
+          "width": 150,
+          "height": 24
+        },
+        {
+          "width": 150,
+          "height": 25.6
+        },
+        {
+          "width": 150,
+          "height": 26.8
+        },
+        {
+          "width": 150,
+          "height": 28
+        },
+        {
+          "width": 150,
+          "height": 29.2
+        },
+        {
+          "width": 150,
+          "height": 30.4
+        },
+        {
+          "width": 150,
+          "height": 31.6
+        },
+        {
+          "width": 150,
+          "height": 32.4
+        },
+        {
+          "width": 150,
+          "height": 33.2
+        },
+        {
+          "width": 150,
+          "height": 34
+        },
+        {
+          "width": 150,
+          "height": 36.8
+        },
+        {
+          "width": 150,
+          "height": 42.4
+        },
+        {
+          "width": 150,
+          "height": 50.8
+        },
+        {
+          "width": 150,
+          "height": 64
+        },
+        {
+          "width": 150,
+          "height": 78
+        },
+        {
+          "width": 150,
+          "height": 91.2
+        },
+        {
+          "width": 150,
+          "height": 102.4
+        },
+        {
+          "width": 150,
+          "height": 112.4
+        },
+        {
+          "width": 150,
+          "height": 120
+        },
+        {
+          "width": 150,
+          "height": 126
+        },
+        {
+          "width": 150,
+          "height": 130
+        },
+        {
+          "width": 150,
+          "height": 132.8
+        },
+        {
+          "width": 150,
+          "height": 134
+        },
+        {
+          "width": 150,
+          "height": 134
+        },
+        {
+          "width": 150,
+          "height": 133.2
+        },
+        {
+          "width": 150,
+          "height": 131.2
+        },
+        {
+          "width": 150,
+          "height": 128.8
+        },
+        {
+          "width": 150,
+          "height": 125.2
+        },
+        {
+          "width": 150,
+          "height": 121.6
+        },
+        {
+          "width": 150,
+          "height": 117.6
+        },
+        {
+          "width": 150,
+          "height": 112.8
+        },
+        {
+          "width": 150,
+          "height": 108
+        },
+        {
+          "width": 150,
+          "height": 102.4
+        },
+        {
+          "width": 150,
+          "height": 96.4
+        },
+        {
+          "width": 150,
+          "height": 91.2
+        },
+        {
+          "width": 150,
+          "height": 88
+        },
+        {
+          "width": 150,
+          "height": 81.6
+        },
+        {
+          "width": 150,
+          "height": 70.8
+        },
+        {
+          "width": 150,
+          "height": 59.2
+        },
+        {
+          "width": 150,
+          "height": 48
+        },
+        {
+          "width": 150,
+          "height": 38.4
+        },
+        {
+          "width": 150,
+          "height": 30
+        },
+        {
+          "width": 150,
+          "height": 22.8
+        },
+        {
+          "width": 150,
+          "height": 17.2
+        },
+        {
+          "width": 150,
+          "height": 12.4
+        },
+        {
+          "width": 150,
+          "height": 9.2
+        },
+        {
+          "width": 150,
+          "height": 6.4
+        },
+        {
+          "width": 150,
+          "height": 4.4
+        },
+        {
+          "width": 150,
+          "height": 2.8
+        },
+        {
+          "width": 150,
+          "height": 1.6
+        },
+        {
+          "width": 150,
+          "height": 1.2
+        },
+        {
+          "width": 150,
+          "height": 0.4
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0.0066464543,
+        0.059778452,
+        0.1875459,
+        0.39009166,
+        0.5686131,
+        0.70664865,
+        0.8060679,
+        0.87451804,
+        0.92018366,
+        0.94994,
+        0.9689752,
+        0.9809703,
+        0.98843443,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.98828065,
+        0.9288363,
+        0.7806658,
+        0.57941735,
+        0.40687433,
+        0.27529213,
+        0.18131107,
+        0.11697123,
+        0.074225225,
+        0.046460062,
+        0.028744182,
+        0.017604083,
+        0.010684598
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
new file mode 100644
index 0000000..afa005a
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealCloseTransition.json
@@ -0,0 +1,304 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352,
+    368,
+    384,
+    400,
+    416,
+    432
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 300
+        },
+        {
+          "width": 150,
+          "height": 278.8
+        },
+        {
+          "width": 150,
+          "height": 234.8
+        },
+        {
+          "width": 150,
+          "height": 185.2
+        },
+        {
+          "width": 150,
+          "height": 138.8
+        },
+        {
+          "width": 150,
+          "height": 100.4
+        },
+        {
+          "width": 150,
+          "height": 66.8
+        },
+        {
+          "width": 150,
+          "height": 41.6
+        },
+        {
+          "width": 150,
+          "height": 23.6
+        },
+        {
+          "width": 150,
+          "height": 12
+        },
+        {
+          "width": 150,
+          "height": 4.4
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 0
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        0.9762947,
+        0.8118515,
+        0.60931784,
+        0.43090785,
+        0.29299664,
+        0.19368339,
+        0.12531388,
+        0.079705715,
+        0.049988627,
+        0.030979574,
+        0.019001365,
+        0.011548042,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json
new file mode 100644
index 0000000..317d480
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_triggeredRevealOpenTransition.json
@@ -0,0 +1,254 @@
+{
+  "frame_ids": [
+    0,
+    16,
+    32,
+    48,
+    64,
+    80,
+    96,
+    112,
+    128,
+    144,
+    160,
+    176,
+    192,
+    208,
+    224,
+    240,
+    256,
+    272,
+    288,
+    304,
+    320,
+    336,
+    352
+  ],
+  "features": [
+    {
+      "name": "RevealElement_position",
+      "type": "dpOffset",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        },
+        {
+          "x": 5.2,
+          "y": 5.2
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_size",
+      "type": "dpSize",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        {
+          "width": 150,
+          "height": 0
+        },
+        {
+          "width": 150,
+          "height": 5.2
+        },
+        {
+          "width": 150,
+          "height": 16
+        },
+        {
+          "width": 150,
+          "height": 28.4
+        },
+        {
+          "width": 150,
+          "height": 63.6
+        },
+        {
+          "width": 150,
+          "height": 116.4
+        },
+        {
+          "width": 150,
+          "height": 161.2
+        },
+        {
+          "width": 150,
+          "height": 197.2
+        },
+        {
+          "width": 150,
+          "height": 225.2
+        },
+        {
+          "width": 150,
+          "height": 246.8
+        },
+        {
+          "width": 150,
+          "height": 262.4
+        },
+        {
+          "width": 150,
+          "height": 274
+        },
+        {
+          "width": 150,
+          "height": 282.4
+        },
+        {
+          "width": 150,
+          "height": 288.4
+        },
+        {
+          "width": 150,
+          "height": 292.4
+        },
+        {
+          "width": 150,
+          "height": 294.8
+        },
+        {
+          "width": 150,
+          "height": 296.4
+        },
+        {
+          "width": 150,
+          "height": 297.6
+        },
+        {
+          "width": 150,
+          "height": 298.4
+        },
+        {
+          "width": 150,
+          "height": 299.2
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        },
+        {
+          "width": 150,
+          "height": 299.6
+        }
+      ]
+    },
+    {
+      "name": "RevealElement_alpha",
+      "type": "float",
+      "data_points": [
+        {
+          "type": "not_found"
+        },
+        0,
+        0,
+        0.0951103,
+        0.2911651,
+        0.48551244,
+        0.6439433,
+        0.76157355,
+        0.8441935,
+        0.9001033,
+        0.9369305,
+        0.96069145,
+        0.97577035,
+        0.98520935,
+        0.9910494,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1,
+        1
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json
deleted file mode 100644
index 0fcdfa3..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragFullyClose.json
+++ /dev/null
@@ -1,634 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464,
-    480,
-    496,
-    512,
-    528,
-    544,
-    560,
-    576,
-    592,
-    608,
-    624,
-    640,
-    656,
-    672,
-    688,
-    704,
-    720,
-    736,
-    752,
-    768,
-    784,
-    800,
-    816,
-    832,
-    848,
-    864,
-    880,
-    896,
-    912,
-    928,
-    944,
-    960
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50.4
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 52.4,
-          "y": 50
-        },
-        {
-          "x": 56,
-          "y": 50
-        },
-        {
-          "x": 58.8,
-          "y": 50
-        },
-        {
-          "x": 60.8,
-          "y": 50
-        },
-        {
-          "x": 62,
-          "y": 50
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 63.6,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 393.2
-        },
-        {
-          "width": 188,
-          "height": 393.2
-        },
-        {
-          "width": 188,
-          "height": 386
-        },
-        {
-          "width": 188,
-          "height": 379.6
-        },
-        {
-          "width": 188,
-          "height": 372.8
-        },
-        {
-          "width": 188,
-          "height": 366.8
-        },
-        {
-          "width": 188,
-          "height": 360.4
-        },
-        {
-          "width": 188,
-          "height": 354
-        },
-        {
-          "width": 188,
-          "height": 347.6
-        },
-        {
-          "width": 188,
-          "height": 341.2
-        },
-        {
-          "width": 188,
-          "height": 341.2
-        },
-        {
-          "width": 188,
-          "height": 334
-        },
-        {
-          "width": 188,
-          "height": 328
-        },
-        {
-          "width": 188,
-          "height": 321.6
-        },
-        {
-          "width": 188,
-          "height": 315.2
-        },
-        {
-          "width": 188,
-          "height": 308.8
-        },
-        {
-          "width": 188,
-          "height": 302.4
-        },
-        {
-          "width": 188,
-          "height": 296
-        },
-        {
-          "width": 188,
-          "height": 289.6
-        },
-        {
-          "width": 188,
-          "height": 289.6
-        },
-        {
-          "width": 188,
-          "height": 282.8
-        },
-        {
-          "width": 188,
-          "height": 276.4
-        },
-        {
-          "width": 188,
-          "height": 270
-        },
-        {
-          "width": 188,
-          "height": 263.6
-        },
-        {
-          "width": 188,
-          "height": 257.2
-        },
-        {
-          "width": 188,
-          "height": 250.8
-        },
-        {
-          "width": 188,
-          "height": 244.4
-        },
-        {
-          "width": 188,
-          "height": 238
-        },
-        {
-          "width": 188,
-          "height": 238
-        },
-        {
-          "width": 188,
-          "height": 231.2
-        },
-        {
-          "width": 188,
-          "height": 224.8
-        },
-        {
-          "width": 188,
-          "height": 218.4
-        },
-        {
-          "width": 188,
-          "height": 212
-        },
-        {
-          "width": 188,
-          "height": 212
-        },
-        {
-          "width": 188,
-          "height": 192.4
-        },
-        {
-          "width": 188,
-          "height": 159.6
-        },
-        {
-          "width": 188,
-          "height": 124.4
-        },
-        {
-          "width": 188,
-          "height": 92.8
-        },
-        {
-          "width": 183.2,
-          "height": 66.4
-        },
-        {
-          "width": 176,
-          "height": 46
-        },
-        {
-          "width": 170.4,
-          "height": 39.2
-        },
-        {
-          "width": 166.8,
-          "height": 36
-        },
-        {
-          "width": 164,
-          "height": 31.6
-        },
-        {
-          "width": 162.4,
-          "height": 26.8
-        },
-        {
-          "width": 161.2,
-          "height": 22
-        },
-        {
-          "width": 160.4,
-          "height": 17.6
-        },
-        {
-          "width": 160,
-          "height": 14
-        },
-        {
-          "width": 160,
-          "height": 10.8
-        },
-        {
-          "width": 160,
-          "height": 8
-        },
-        {
-          "width": 160,
-          "height": 6
-        },
-        {
-          "width": 160,
-          "height": 4.4
-        },
-        {
-          "width": 160,
-          "height": 2.8
-        },
-        {
-          "width": 160,
-          "height": 2
-        },
-        {
-          "width": 160,
-          "height": 1.2
-        },
-        {
-          "width": 160,
-          "height": 0.8
-        },
-        {
-          "width": 160,
-          "height": 0.4
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        0.9808927,
-        0.8211168,
-        0.61845565,
-        0.43834114,
-        0.29850912,
-        0.19755232,
-        0.12793064,
-        0.08142871,
-        0.051099956,
-        0.031684637,
-        0.019442618,
-        0.011821032,
-        0,
-        0,
-        0,
-        0,
-        0
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json
deleted file mode 100644
index 3196334..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragHalfClose.json
+++ /dev/null
@@ -1,624 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464,
-    480,
-    496,
-    512,
-    528,
-    544,
-    560,
-    576,
-    592,
-    608,
-    624,
-    640,
-    656,
-    672,
-    688,
-    704,
-    720,
-    736,
-    752,
-    768,
-    784,
-    800,
-    816,
-    832,
-    848,
-    864,
-    880,
-    896,
-    912,
-    928,
-    944
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50.8,
-          "y": 52
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 52.4,
-          "y": 50
-        },
-        {
-          "x": 55.6,
-          "y": 50
-        },
-        {
-          "x": 58.4,
-          "y": 50
-        },
-        {
-          "x": 60.4,
-          "y": 50
-        },
-        {
-          "x": 61.6,
-          "y": 50
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 63.2,
-          "y": 50
-        },
-        {
-          "x": 63.6,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 390
-        },
-        {
-          "width": 188,
-          "height": 390
-        },
-        {
-          "width": 188,
-          "height": 379.2
-        },
-        {
-          "width": 188,
-          "height": 371.2
-        },
-        {
-          "width": 188,
-          "height": 363.2
-        },
-        {
-          "width": 188,
-          "height": 355.2
-        },
-        {
-          "width": 188,
-          "height": 347.2
-        },
-        {
-          "width": 188,
-          "height": 339.2
-        },
-        {
-          "width": 188,
-          "height": 331.2
-        },
-        {
-          "width": 188,
-          "height": 323.2
-        },
-        {
-          "width": 188,
-          "height": 323.2
-        },
-        {
-          "width": 188,
-          "height": 314.8
-        },
-        {
-          "width": 188,
-          "height": 306.8
-        },
-        {
-          "width": 188,
-          "height": 298.8
-        },
-        {
-          "width": 188,
-          "height": 290.8
-        },
-        {
-          "width": 188,
-          "height": 282.8
-        },
-        {
-          "width": 188,
-          "height": 274.8
-        },
-        {
-          "width": 188,
-          "height": 266.8
-        },
-        {
-          "width": 188,
-          "height": 258.8
-        },
-        {
-          "width": 188,
-          "height": 258.8
-        },
-        {
-          "width": 188,
-          "height": 250.4
-        },
-        {
-          "width": 188,
-          "height": 242.4
-        },
-        {
-          "width": 188,
-          "height": 234.4
-        },
-        {
-          "width": 188,
-          "height": 226.4
-        },
-        {
-          "width": 188,
-          "height": 218.4
-        },
-        {
-          "width": 188,
-          "height": 210.4
-        },
-        {
-          "width": 188,
-          "height": 202.4
-        },
-        {
-          "width": 188,
-          "height": 194.4
-        },
-        {
-          "width": 188,
-          "height": 194.4
-        },
-        {
-          "width": 188,
-          "height": 185.6
-        },
-        {
-          "width": 188,
-          "height": 178
-        },
-        {
-          "width": 188,
-          "height": 170
-        },
-        {
-          "width": 188,
-          "height": 161.6
-        },
-        {
-          "width": 188,
-          "height": 161.6
-        },
-        {
-          "width": 188,
-          "height": 144.8
-        },
-        {
-          "width": 188,
-          "height": 118.8
-        },
-        {
-          "width": 188,
-          "height": 92
-        },
-        {
-          "width": 183.6,
-          "height": 68
-        },
-        {
-          "width": 176.8,
-          "height": 48.4
-        },
-        {
-          "width": 171.6,
-          "height": 39.6
-        },
-        {
-          "width": 167.6,
-          "height": 36.8
-        },
-        {
-          "width": 164.8,
-          "height": 32.4
-        },
-        {
-          "width": 162.8,
-          "height": 27.6
-        },
-        {
-          "width": 161.6,
-          "height": 22.8
-        },
-        {
-          "width": 160.8,
-          "height": 18.4
-        },
-        {
-          "width": 160.4,
-          "height": 14.4
-        },
-        {
-          "width": 160,
-          "height": 11.2
-        },
-        {
-          "width": 160,
-          "height": 8.4
-        },
-        {
-          "width": 160,
-          "height": 6.4
-        },
-        {
-          "width": 160,
-          "height": 4.4
-        },
-        {
-          "width": 160,
-          "height": 3.2
-        },
-        {
-          "width": 160,
-          "height": 2
-        },
-        {
-          "width": 160,
-          "height": 1.6
-        },
-        {
-          "width": 160,
-          "height": 0.8
-        },
-        {
-          "width": 160,
-          "height": 0.4
-        },
-        {
-          "width": 160,
-          "height": 0.4
-        },
-        {
-          "width": 160,
-          "height": 0
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        0.9967737,
-        0.86538374,
-        0.66414475,
-        0.47619528,
-        0.32686388,
-        0.21757984,
-        0.14153665,
-        0.09041709,
-        0.05691254,
-        0.035380244,
-        0.02175957,
-        0.01325649,
-        0.008007765,
-        0,
-        0,
-        0,
-        0
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json
deleted file mode 100644
index 4b03068..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_dragOpen.json
+++ /dev/null
@@ -1,544 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464,
-    480,
-    496,
-    512,
-    528,
-    544,
-    560,
-    576,
-    592,
-    608,
-    624,
-    640,
-    656,
-    672,
-    688,
-    704,
-    720,
-    736,
-    752,
-    768,
-    784,
-    800,
-    816
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 61.6,
-          "y": 50
-        },
-        {
-          "x": 60.8,
-          "y": 50
-        },
-        {
-          "x": 59.6,
-          "y": 50
-        },
-        {
-          "x": 58.4,
-          "y": 50
-        },
-        {
-          "x": 57.2,
-          "y": 50
-        },
-        {
-          "x": 56,
-          "y": 50
-        },
-        {
-          "x": 55.2,
-          "y": 50
-        },
-        {
-          "x": 54,
-          "y": 50
-        },
-        {
-          "x": 54,
-          "y": 50
-        },
-        {
-          "x": 52.8,
-          "y": 50
-        },
-        {
-          "x": 51.6,
-          "y": 50
-        },
-        {
-          "x": 50.4,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "width": 162.4,
-          "height": 1.6
-        },
-        {
-          "width": 162.4,
-          "height": 1.6
-        },
-        {
-          "width": 164.8,
-          "height": 3.2
-        },
-        {
-          "width": 166.8,
-          "height": 4.8
-        },
-        {
-          "width": 169.2,
-          "height": 6.4
-        },
-        {
-          "width": 171.6,
-          "height": 8
-        },
-        {
-          "width": 173.6,
-          "height": 9.6
-        },
-        {
-          "width": 176,
-          "height": 11.2
-        },
-        {
-          "width": 178,
-          "height": 12.8
-        },
-        {
-          "width": 180.4,
-          "height": 14.4
-        },
-        {
-          "width": 180.4,
-          "height": 14.4
-        },
-        {
-          "width": 182.8,
-          "height": 16.4
-        },
-        {
-          "width": 185.2,
-          "height": 18
-        },
-        {
-          "width": 187.2,
-          "height": 19.6
-        },
-        {
-          "width": 188,
-          "height": 24.8
-        },
-        {
-          "width": 188,
-          "height": 32.8
-        },
-        {
-          "width": 188,
-          "height": 44
-        },
-        {
-          "width": 188,
-          "height": 57.2
-        },
-        {
-          "width": 188,
-          "height": 70.8
-        },
-        {
-          "width": 188,
-          "height": 78
-        },
-        {
-          "width": 188,
-          "height": 91.2
-        },
-        {
-          "width": 188,
-          "height": 103.2
-        },
-        {
-          "width": 188,
-          "height": 114.4
-        },
-        {
-          "width": 188,
-          "height": 124.4
-        },
-        {
-          "width": 188,
-          "height": 134
-        },
-        {
-          "width": 188,
-          "height": 142.8
-        },
-        {
-          "width": 188,
-          "height": 150.8
-        },
-        {
-          "width": 188,
-          "height": 158.8
-        },
-        {
-          "width": 188,
-          "height": 159.6
-        },
-        {
-          "width": 188,
-          "height": 167.2
-        },
-        {
-          "width": 188,
-          "height": 174
-        },
-        {
-          "width": 188,
-          "height": 180.8
-        },
-        {
-          "width": 188,
-          "height": 187.6
-        },
-        {
-          "width": 188,
-          "height": 187.6
-        },
-        {
-          "width": 188,
-          "height": 207.2
-        },
-        {
-          "width": 188,
-          "height": 240
-        },
-        {
-          "width": 188,
-          "height": 275.2
-        },
-        {
-          "width": 188,
-          "height": 306.8
-        },
-        {
-          "width": 188,
-          "height": 333.2
-        },
-        {
-          "width": 188,
-          "height": 353.6
-        },
-        {
-          "width": 188,
-          "height": 368.8
-        },
-        {
-          "width": 188,
-          "height": 380
-        },
-        {
-          "width": 188,
-          "height": 387.6
-        },
-        {
-          "width": 188,
-          "height": 392.4
-        },
-        {
-          "width": 188,
-          "height": 395.6
-        },
-        {
-          "width": 188,
-          "height": 398
-        },
-        {
-          "width": 188,
-          "height": 398.8
-        },
-        {
-          "width": 188,
-          "height": 399.6
-        },
-        {
-          "width": 188,
-          "height": 400
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        0,
-        0,
-        0,
-        0,
-        0.0067873597,
-        0.0612576,
-        0.19080025,
-        0.39327443,
-        0.5711931,
-        0.70855826,
-        0.8074064,
-        0.8754226,
-        0.9207788,
-        0.95032376,
-        0.9692185,
-        0.98112255,
-        0.9885286,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json
deleted file mode 100644
index 10a9ba7..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_flingClose.json
+++ /dev/null
@@ -1,424 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464,
-    480,
-    496,
-    512,
-    528,
-    544,
-    560,
-    576,
-    592,
-    608,
-    624
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50.4,
-          "y": 50.8
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 51.2,
-          "y": 50
-        },
-        {
-          "x": 55.6,
-          "y": 50
-        },
-        {
-          "x": 58.8,
-          "y": 50
-        },
-        {
-          "x": 60.8,
-          "y": 50
-        },
-        {
-          "x": 62,
-          "y": 50
-        },
-        {
-          "x": 63.2,
-          "y": 50
-        },
-        {
-          "x": 63.6,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 389.6
-        },
-        {
-          "width": 188,
-          "height": 378.8
-        },
-        {
-          "width": 188,
-          "height": 366
-        },
-        {
-          "width": 188,
-          "height": 352
-        },
-        {
-          "width": 188,
-          "height": 352
-        },
-        {
-          "width": 188,
-          "height": 316.8
-        },
-        {
-          "width": 188,
-          "height": 261.2
-        },
-        {
-          "width": 188,
-          "height": 202.8
-        },
-        {
-          "width": 188,
-          "height": 150.8
-        },
-        {
-          "width": 188,
-          "height": 107.6
-        },
-        {
-          "width": 186,
-          "height": 74.4
-        },
-        {
-          "width": 177.2,
-          "height": 49.6
-        },
-        {
-          "width": 170.8,
-          "height": 39.6
-        },
-        {
-          "width": 166.8,
-          "height": 36.8
-        },
-        {
-          "width": 164,
-          "height": 32.4
-        },
-        {
-          "width": 162,
-          "height": 27.6
-        },
-        {
-          "width": 160.8,
-          "height": 22.8
-        },
-        {
-          "width": 160.4,
-          "height": 18.4
-        },
-        {
-          "width": 160,
-          "height": 14.4
-        },
-        {
-          "width": 160,
-          "height": 11.2
-        },
-        {
-          "width": 160,
-          "height": 8.4
-        },
-        {
-          "width": 160,
-          "height": 6
-        },
-        {
-          "width": 160,
-          "height": 4.4
-        },
-        {
-          "width": 160,
-          "height": 3.2
-        },
-        {
-          "width": 160,
-          "height": 2
-        },
-        {
-          "width": 160,
-          "height": 1.2
-        },
-        {
-          "width": 160,
-          "height": 0.8
-        },
-        {
-          "width": 160,
-          "height": 0.4
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        0.9833227,
-        0.8263634,
-        0.623688,
-        0.44261706,
-        0.3016883,
-        0.1997872,
-        0.12944388,
-        0.08242595,
-        0.051743627,
-        0.032093227,
-        0.019698441,
-        0.0119793415,
-        0,
-        0,
-        0,
-        0,
-        0
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json
deleted file mode 100644
index 57bdf3e..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_gesture_magneticDetachAndReattach.json
+++ /dev/null
@@ -1,744 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432,
-    448,
-    464,
-    480,
-    496,
-    512,
-    528,
-    544,
-    560,
-    576,
-    592,
-    608,
-    624,
-    640,
-    656,
-    672,
-    688,
-    704,
-    720,
-    736,
-    752,
-    768,
-    784,
-    800,
-    816,
-    832,
-    848,
-    864,
-    880,
-    896,
-    912,
-    928,
-    944,
-    960,
-    976,
-    992,
-    1008,
-    1024,
-    1040,
-    1056,
-    1072,
-    1088,
-    1104,
-    1120,
-    1136
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 62,
-          "y": 50
-        },
-        {
-          "x": 61.2,
-          "y": 50
-        },
-        {
-          "x": 60.4,
-          "y": 50
-        },
-        {
-          "x": 59.6,
-          "y": 50
-        },
-        {
-          "x": 58.8,
-          "y": 50
-        },
-        {
-          "x": 58,
-          "y": 50
-        },
-        {
-          "x": 57.2,
-          "y": 50
-        },
-        {
-          "x": 56.4,
-          "y": 50
-        },
-        {
-          "x": 55.6,
-          "y": 50
-        },
-        {
-          "x": 55.2,
-          "y": 50
-        },
-        {
-          "x": 54.4,
-          "y": 50
-        },
-        {
-          "x": 53.6,
-          "y": 50
-        },
-        {
-          "x": 53.2,
-          "y": 50
-        },
-        {
-          "x": 52.8,
-          "y": 50
-        },
-        {
-          "x": 52,
-          "y": 50
-        },
-        {
-          "x": 51.6,
-          "y": 50
-        },
-        {
-          "x": 51.2,
-          "y": 50
-        },
-        {
-          "x": 50.8,
-          "y": 50
-        },
-        {
-          "x": 50.4,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50.4,
-          "y": 50
-        },
-        {
-          "x": 50.8,
-          "y": 50
-        },
-        {
-          "x": 51.2,
-          "y": 50
-        },
-        {
-          "x": 51.6,
-          "y": 50
-        },
-        {
-          "x": 52,
-          "y": 50
-        },
-        {
-          "x": 52.8,
-          "y": 50
-        },
-        {
-          "x": 53.2,
-          "y": 50
-        },
-        {
-          "x": 53.6,
-          "y": 50
-        },
-        {
-          "x": 54.4,
-          "y": 50
-        },
-        {
-          "x": 55.2,
-          "y": 50
-        },
-        {
-          "x": 55.6,
-          "y": 50
-        },
-        {
-          "x": 56.4,
-          "y": 50
-        },
-        {
-          "x": 57.2,
-          "y": 50
-        },
-        {
-          "x": 58,
-          "y": 50
-        },
-        {
-          "x": 58.8,
-          "y": 50
-        },
-        {
-          "x": 59.6,
-          "y": 50
-        },
-        {
-          "x": 60.4,
-          "y": 50
-        },
-        {
-          "x": 61.2,
-          "y": 50
-        },
-        {
-          "x": 62,
-          "y": 50
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 63.6,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "width": 162.4,
-          "height": 1.6
-        },
-        {
-          "width": 164,
-          "height": 2.8
-        },
-        {
-          "width": 166,
-          "height": 4
-        },
-        {
-          "width": 167.6,
-          "height": 5.2
-        },
-        {
-          "width": 169.2,
-          "height": 6.4
-        },
-        {
-          "width": 170.8,
-          "height": 7.6
-        },
-        {
-          "width": 172.4,
-          "height": 8.8
-        },
-        {
-          "width": 174,
-          "height": 10
-        },
-        {
-          "width": 175.2,
-          "height": 10.8
-        },
-        {
-          "width": 176.8,
-          "height": 12
-        },
-        {
-          "width": 178,
-          "height": 12.8
-        },
-        {
-          "width": 179.2,
-          "height": 13.6
-        },
-        {
-          "width": 180.8,
-          "height": 14.8
-        },
-        {
-          "width": 182,
-          "height": 15.6
-        },
-        {
-          "width": 182.8,
-          "height": 16.4
-        },
-        {
-          "width": 184,
-          "height": 17.2
-        },
-        {
-          "width": 184.8,
-          "height": 17.6
-        },
-        {
-          "width": 186,
-          "height": 18.4
-        },
-        {
-          "width": 186.8,
-          "height": 19.2
-        },
-        {
-          "width": 187.6,
-          "height": 19.6
-        },
-        {
-          "width": 188,
-          "height": 21.2
-        },
-        {
-          "width": 188,
-          "height": 24
-        },
-        {
-          "width": 188,
-          "height": 29.6
-        },
-        {
-          "width": 188,
-          "height": 37.2
-        },
-        {
-          "width": 188,
-          "height": 45.6
-        },
-        {
-          "width": 188,
-          "height": 53.6
-        },
-        {
-          "width": 188,
-          "height": 60.4
-        },
-        {
-          "width": 188,
-          "height": 66.4
-        },
-        {
-          "width": 188,
-          "height": 71.2
-        },
-        {
-          "width": 188,
-          "height": 75.2
-        },
-        {
-          "width": 188,
-          "height": 77.6
-        },
-        {
-          "width": 188,
-          "height": 79.6
-        },
-        {
-          "width": 188,
-          "height": 80.4
-        },
-        {
-          "width": 188,
-          "height": 80.8
-        },
-        {
-          "width": 188,
-          "height": 80.4
-        },
-        {
-          "width": 188,
-          "height": 79.2
-        },
-        {
-          "width": 187.6,
-          "height": 78
-        },
-        {
-          "width": 186.8,
-          "height": 76
-        },
-        {
-          "width": 186,
-          "height": 74
-        },
-        {
-          "width": 184.8,
-          "height": 71.6
-        },
-        {
-          "width": 184,
-          "height": 69.2
-        },
-        {
-          "width": 182.8,
-          "height": 66
-        },
-        {
-          "width": 182,
-          "height": 62.8
-        },
-        {
-          "width": 180.8,
-          "height": 59.2
-        },
-        {
-          "width": 179.2,
-          "height": 55.6
-        },
-        {
-          "width": 178,
-          "height": 52
-        },
-        {
-          "width": 176.8,
-          "height": 48
-        },
-        {
-          "width": 175.2,
-          "height": 44
-        },
-        {
-          "width": 174,
-          "height": 40
-        },
-        {
-          "width": 172.4,
-          "height": 39.2
-        },
-        {
-          "width": 170.8,
-          "height": 38.4
-        },
-        {
-          "width": 169.2,
-          "height": 34.8
-        },
-        {
-          "width": 167.6,
-          "height": 30
-        },
-        {
-          "width": 166,
-          "height": 25.2
-        },
-        {
-          "width": 164,
-          "height": 20.4
-        },
-        {
-          "width": 162.4,
-          "height": 16.4
-        },
-        {
-          "width": 160.8,
-          "height": 12.8
-        },
-        {
-          "width": 160,
-          "height": 9.6
-        },
-        {
-          "width": 160,
-          "height": 7.2
-        },
-        {
-          "width": 160,
-          "height": 5.2
-        },
-        {
-          "width": 160,
-          "height": 3.6
-        },
-        {
-          "width": 160,
-          "height": 2.8
-        },
-        {
-          "width": 160,
-          "height": 1.6
-        },
-        {
-          "width": 160,
-          "height": 1.2
-        },
-        {
-          "width": 160,
-          "height": 0.8
-        },
-        {
-          "width": 160,
-          "height": 0.4
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        {
-          "type": "not_found"
-        },
-        0,
-        0,
-        0,
-        0,
-        0.012518823,
-        0.0741024,
-        0.2254293,
-        0.42628878,
-        0.5976641,
-        0.7280312,
-        0.82100236,
-        0.8845844,
-        0.9267946,
-        0.95419544,
-        0.9716705,
-        0.98265487,
-        0.98947525,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        0.9944124,
-        0.9417388,
-        0.8184184,
-        0.6157812,
-        0.4361611,
-        0.2968906,
-        0.19641554,
-        0.12716137,
-        0.080921985,
-        0.050773025,
-        0.03147719,
-        0.019312752,
-        0.011740655,
-        0,
-        0,
-        0
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json
deleted file mode 100644
index 9aa91c2..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealCloseTransition.json
+++ /dev/null
@@ -1,304 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336,
-    352,
-    368,
-    384,
-    400,
-    416,
-    432
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 53.2,
-          "y": 50
-        },
-        {
-          "x": 57.2,
-          "y": 50
-        },
-        {
-          "x": 59.6,
-          "y": 50
-        },
-        {
-          "x": 61.6,
-          "y": 50
-        },
-        {
-          "x": 62.8,
-          "y": 50
-        },
-        {
-          "x": 63.6,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 64,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 400
-        },
-        {
-          "width": 188,
-          "height": 372
-        },
-        {
-          "width": 188,
-          "height": 312.8
-        },
-        {
-          "width": 188,
-          "height": 246.8
-        },
-        {
-          "width": 188,
-          "height": 185.2
-        },
-        {
-          "width": 188,
-          "height": 133.6
-        },
-        {
-          "width": 188,
-          "height": 93.2
-        },
-        {
-          "width": 181.6,
-          "height": 62.8
-        },
-        {
-          "width": 174,
-          "height": 40.8
-        },
-        {
-          "width": 168.8,
-          "height": 38.4
-        },
-        {
-          "width": 165.2,
-          "height": 34.8
-        },
-        {
-          "width": 162.8,
-          "height": 30
-        },
-        {
-          "width": 161.2,
-          "height": 25.2
-        },
-        {
-          "width": 160.4,
-          "height": 20.4
-        },
-        {
-          "width": 160,
-          "height": 16.4
-        },
-        {
-          "width": 160,
-          "height": 12.8
-        },
-        {
-          "width": 160,
-          "height": 9.6
-        },
-        {
-          "width": 160,
-          "height": 7.2
-        },
-        {
-          "width": 160,
-          "height": 5.2
-        },
-        {
-          "width": 160,
-          "height": 3.6
-        },
-        {
-          "width": 160,
-          "height": 2.8
-        },
-        {
-          "width": 160,
-          "height": 1.6
-        },
-        {
-          "width": 160,
-          "height": 1.2
-        },
-        {
-          "width": 160,
-          "height": 0.8
-        },
-        {
-          "width": 160,
-          "height": 0.4
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 160,
-          "height": 0
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        0.91758585,
-        0.72435355,
-        0.52812576,
-        0.3665868,
-        0.24600428,
-        0.16102076,
-        0.103373945,
-        0.06533456,
-        0.04075712,
-        0.025142312,
-        0.015358448,
-        0.0092999935,
-        0,
-        0,
-        0,
-        0,
-        0
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json b/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json
deleted file mode 100644
index 622c29e..0000000
--- a/packages/SystemUI/compose/scene/tests/goldens/verticalReveal_triggeredRevealOpenTransition.json
+++ /dev/null
@@ -1,244 +0,0 @@
-{
-  "frame_ids": [
-    0,
-    16,
-    32,
-    48,
-    64,
-    80,
-    96,
-    112,
-    128,
-    144,
-    160,
-    176,
-    192,
-    208,
-    224,
-    240,
-    256,
-    272,
-    288,
-    304,
-    320,
-    336
-  ],
-  "features": [
-    {
-      "name": "RevealElement_position",
-      "type": "dpOffset",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "x": 64,
-          "y": 50
-        },
-        {
-          "x": 59.2,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        },
-        {
-          "x": 50,
-          "y": 50
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_size",
-      "type": "dpSize",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        {
-          "width": 160,
-          "height": 0
-        },
-        {
-          "width": 169.6,
-          "height": 6.8
-        },
-        {
-          "width": 188,
-          "height": 26.8
-        },
-        {
-          "width": 188,
-          "height": 95.6
-        },
-        {
-          "width": 188,
-          "height": 163.2
-        },
-        {
-          "width": 188,
-          "height": 222
-        },
-        {
-          "width": 188,
-          "height": 269.6
-        },
-        {
-          "width": 188,
-          "height": 307.2
-        },
-        {
-          "width": 188,
-          "height": 335.2
-        },
-        {
-          "width": 188,
-          "height": 356
-        },
-        {
-          "width": 188,
-          "height": 370.4
-        },
-        {
-          "width": 188,
-          "height": 380.8
-        },
-        {
-          "width": 188,
-          "height": 387.6
-        },
-        {
-          "width": 188,
-          "height": 392.4
-        },
-        {
-          "width": 188,
-          "height": 395.2
-        },
-        {
-          "width": 188,
-          "height": 397.2
-        },
-        {
-          "width": 188,
-          "height": 398
-        },
-        {
-          "width": 188,
-          "height": 398.8
-        },
-        {
-          "width": 188,
-          "height": 399.2
-        },
-        {
-          "width": 188,
-          "height": 399.2
-        },
-        {
-          "width": 188,
-          "height": 399.6
-        }
-      ]
-    },
-    {
-      "name": "RevealElement_alpha",
-      "type": "float",
-      "data_points": [
-        {
-          "type": "not_found"
-        },
-        0,
-        0.05698657,
-        0.24197984,
-        0.44158113,
-        0.6097554,
-        0.73685503,
-        0.8271309,
-        0.8886989,
-        0.9294886,
-        0.9559254,
-        0.97276413,
-        0.98333716,
-        0.98989624,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1,
-        1
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
index f4e2328..1bc83e0 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/reveal/ContentRevealTest.kt
@@ -37,7 +37,6 @@
 import androidx.compose.ui.test.swipeWithVelocity
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.compose.animation.scene.ContentScope
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.FeatureCaptures.elementAlpha
@@ -47,8 +46,8 @@
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.featureOfElement
 import com.android.compose.animation.scene.transitions
-import com.android.mechanics.behavior.EdgeContainerExpansionSpec
-import com.android.mechanics.behavior.edgeContainerExpansionBackground
+import com.android.mechanics.behavior.VerticalExpandContainerSpec
+import com.android.mechanics.behavior.verticalExpandContainerBackground
 import kotlin.math.sin
 import kotlinx.coroutines.CoroutineScope
 import org.junit.Rule
@@ -63,26 +62,48 @@
 import platform.test.motion.compose.recordMotion
 import platform.test.motion.compose.runTest
 import platform.test.motion.testing.createGoldenPathManager
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.PathConfig
+import platform.test.screenshot.PathElementNoContext
 
-@RunWith(AndroidJUnit4::class)
 @MotionTest
-class ContentRevealTest {
+@RunWith(ParameterizedAndroidJunit4::class)
+class ContentRevealTest(private val isFloating: Boolean) {
+
+    private val pathConfig =
+        PathConfig(
+            PathElementNoContext("floating", isDir = false) {
+                if (isFloating) "floating" else "edge"
+            }
+        )
 
     private val goldenPaths =
-        createGoldenPathManager("frameworks/base/packages/SystemUI/compose/scene/tests/goldens")
+        createGoldenPathManager(
+            "frameworks/base/packages/SystemUI/compose/scene/tests/goldens",
+            pathConfig,
+        )
 
     @get:Rule val motionRule = createFixedConfigurationComposeMotionTestRule(goldenPaths)
 
     private val fakeHaptics = FakeHaptics()
 
+    private val motionSpec = VerticalExpandContainerSpec(isFloating)
+
     @Test
     fun verticalReveal_triggeredRevealOpenTransition() {
-        assertVerticalContainerRevealMotion(TriggeredRevealMotion(SceneClosed, SceneOpen))
+        assertVerticalContainerRevealMotion(
+            TriggeredRevealMotion(SceneClosed, SceneOpen),
+            "verticalReveal_triggeredRevealOpenTransition",
+        )
     }
 
     @Test
     fun verticalReveal_triggeredRevealCloseTransition() {
-        assertVerticalContainerRevealMotion(TriggeredRevealMotion(SceneOpen, SceneClosed))
+        assertVerticalContainerRevealMotion(
+            TriggeredRevealMotion(SceneOpen, SceneClosed),
+            "verticalReveal_triggeredRevealCloseTransition",
+        )
     }
 
     @Test
@@ -90,15 +111,18 @@
         assertVerticalContainerRevealMotion(
             GestureRevealMotion(SceneClosed) {
                 val gestureDurationMillis = 1000L
+                // detach position for the floating container is larger
+                val gestureHeight = if (isFloating) 160.dp.toPx() else 100.dp.toPx()
                 swipe(
                     curve = {
                         val progress = it / gestureDurationMillis.toFloat()
-                        val y = sin(progress * Math.PI).toFloat() * 100.dp.toPx()
+                        val y = sin(progress * Math.PI).toFloat() * gestureHeight
                         Offset(centerX, y)
                     },
                     gestureDurationMillis,
                 )
-            }
+            },
+            "verticalReveal_gesture_magneticDetachAndReattach",
         )
     }
 
@@ -107,7 +131,8 @@
         assertVerticalContainerRevealMotion(
             GestureRevealMotion(SceneClosed) {
                 swipeDown(endY = 200.dp.toPx(), durationMillis = 500)
-            }
+            },
+            "verticalReveal_gesture_dragOpen",
         )
     }
 
@@ -117,7 +142,8 @@
             GestureRevealMotion(SceneClosed) {
                 val end = Offset(centerX, 80.dp.toPx())
                 swipeWithVelocity(start = topCenter, end = end, endVelocity = FlingVelocity.toPx())
-            }
+            },
+            "verticalReveal_gesture_flingOpen",
         )
     }
 
@@ -126,7 +152,8 @@
         assertVerticalContainerRevealMotion(
             GestureRevealMotion(SceneOpen) {
                 swipeUp(200.dp.toPx(), 0.dp.toPx(), durationMillis = 500)
-            }
+            },
+            "verticalReveal_gesture_dragFullyClose",
         )
     }
 
@@ -134,8 +161,9 @@
     fun verticalReveal_gesture_dragHalfClose() {
         assertVerticalContainerRevealMotion(
             GestureRevealMotion(SceneOpen) {
-                swipeUp(350.dp.toPx(), 100.dp.toPx(), durationMillis = 500)
-            }
+                swipeUp(250.dp.toPx(), 100.dp.toPx(), durationMillis = 500)
+            },
+            "verticalReveal_gesture_dragHalfClose",
         )
     }
 
@@ -146,7 +174,8 @@
                 val start = Offset(centerX, 260.dp.toPx())
                 val end = Offset(centerX, 200.dp.toPx())
                 swipeWithVelocity(start, end, FlingVelocity.toPx())
-            }
+            },
+            "verticalReveal_gesture_flingClose",
         )
     }
 
@@ -164,11 +193,14 @@
         val gestureControl: TouchInjectionScope.() -> Unit,
     ) : RevealMotion
 
-    private fun assertVerticalContainerRevealMotion(testInstructions: RevealMotion) =
+    private fun assertVerticalContainerRevealMotion(
+        testInstructions: RevealMotion,
+        goldenName: String,
+    ) =
         motionRule.runTest {
             val transitions = transitions {
                 from(SceneClosed, to = SceneOpen) {
-                    verticalContainerReveal(RevealElement, MotionSpec, fakeHaptics)
+                    verticalContainerReveal(RevealElement, motionSpec, fakeHaptics)
                 }
             }
 
@@ -221,9 +253,9 @@
                         SceneTransitionLayoutForTesting(
                             state,
                             modifier =
-                                Modifier.padding(50.dp)
+                                Modifier.padding(5.dp)
                                     .background(Color.Yellow)
-                                    .size(ContainerSize.width, ContainerSize.height + 200.dp)
+                                    .size(ContainerSize.width, ContainerSize.height + 100.dp)
                                     .testTag("stl"),
                         ) {
                             scene(
@@ -241,7 +273,7 @@
                     recordingSpec,
                 )
 
-            assertThat(motion).timeSeriesMatchesGolden()
+            assertThat(motion).timeSeriesMatchesGolden(goldenName)
         }
 
     @Composable
@@ -256,7 +288,7 @@
                 modifier =
                     Modifier.element(RevealElement)
                         .size(ContainerSize)
-                        .edgeContainerExpansionBackground(Color.DarkGray, MotionSpec)
+                        .verticalExpandContainerBackground(Color.DarkGray, motionSpec)
             )
         }
     }
@@ -266,7 +298,9 @@
     }
 
     companion object {
-        val ContainerSize = DpSize(200.dp, 400.dp)
+        @get:Parameters @JvmStatic val parameterValues = listOf(true, false)
+
+        val ContainerSize = DpSize(150.dp, 300.dp)
 
         val FlingVelocity = 1000.dp // dp/sec
 
@@ -274,6 +308,5 @@
         val SceneOpen = SceneKey("SceneB")
 
         val RevealElement = ElementKey("RevealElement")
-        val MotionSpec = EdgeContainerExpansionSpec()
     }
 }
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 0bd51cd..44f2353 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
@@ -111,8 +111,8 @@
     }
 
     companion object {
-        fun transitionStates() = Factory { metadata, actual: TransitionState ->
-            TransitionStateSubject(metadata, actual)
+        fun transitionStates() = Factory { metadata, actual: TransitionState? ->
+            TransitionStateSubject(metadata, actual!!)
         }
     }
 }
@@ -181,8 +181,8 @@
 
     companion object {
         fun sceneTransitions() =
-            Factory { metadata, actual: TransitionState.Transition.ChangeScene ->
-                SceneTransitionSubject(metadata, actual)
+            Factory { metadata, actual: TransitionState.Transition.ChangeScene? ->
+                SceneTransitionSubject(metadata, actual!!)
             }
     }
 }
@@ -202,8 +202,8 @@
 
     companion object {
         fun showOrHideOverlayTransitions() =
-            Factory { metadata, actual: TransitionState.Transition.ShowOrHideOverlay ->
-                ShowOrHideOverlayTransitionSubject(metadata, actual)
+            Factory { metadata, actual: TransitionState.Transition.ShowOrHideOverlay? ->
+                ShowOrHideOverlayTransitionSubject(metadata, actual!!)
             }
     }
 }
@@ -221,8 +221,8 @@
 
     companion object {
         fun replaceOverlayTransitions() =
-            Factory { metadata, actual: TransitionState.Transition.ReplaceOverlay ->
-                ReplaceOverlayTransitionSubject(metadata, actual)
+            Factory { metadata, actual: TransitionState.Transition.ReplaceOverlay? ->
+                ReplaceOverlayTransitionSubject(metadata, actual!!)
             }
     }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
index e4a87ba..7d9a32b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
@@ -58,17 +58,24 @@
                 assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(1f, 1f))
             }
 
+            at(16) {
+                val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
+                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.75f)
+                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.75f, 0.75f))
+            }
+
             at(32) {
                 val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
                 assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.5f)
                 assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.5f, 0.5f))
             }
 
-            at(64) {
+            at(48) {
                 val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
-                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0f)
-                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0f, 0f))
+                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.25f)
+                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.25f, 0.25f))
             }
+
             after { onElement(TestElements.Foo).assertDoesNotExist() }
         }
     }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
index ab31038..368a333 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
@@ -49,8 +49,8 @@
         val DefaultTolerance = Dp(.5f)
 
         fun dpOffsets() =
-            Factory<DpOffsetSubject, DpOffset> { metadata, actual ->
-                DpOffsetSubject(metadata, actual)
+            Factory { metadata, actual: DpOffset? ->
+                DpOffsetSubject(metadata, actual!!)
             }
     }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 4bf0ceb..2f819d1 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -34,12 +34,14 @@
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.animation.GlyphCallback
 import com.android.systemui.animation.TextAnimator
+import com.android.systemui.animation.TextAnimatorListener
 import com.android.systemui.animation.TypefaceVariantCacheImpl
 import com.android.systemui.customization.R
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.core.LogcatOnlyMessageBuffer
-import com.android.systemui.log.core.Logger
 import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.ClockLogger
+import com.android.systemui.plugins.clocks.ClockLogger.Companion.escapeTime
 import java.io.PrintWriter
 import java.util.Calendar
 import java.util.Locale
@@ -67,7 +69,7 @@
     var messageBuffer: MessageBuffer
         get() = logger.buffer
         set(value) {
-            logger = Logger(value, TAG)
+            logger = ClockLogger(this, value, TAG)
         }
 
     var hasCustomPositionUpdatedAnimation: Boolean = false
@@ -99,7 +101,13 @@
     @VisibleForTesting
     var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator = { layout, invalidateCb ->
         val cache = TypefaceVariantCacheImpl(layout.paint.typeface, NUM_CLOCK_FONT_ANIMATION_STEPS)
-        TextAnimator(layout, cache, invalidateCb)
+        TextAnimator(
+            layout,
+            cache,
+            object : TextAnimatorListener {
+                override fun onInvalidate() = invalidateCb()
+            },
+        )
     }
 
     // Used by screenshot tests to provide stability
@@ -185,7 +193,9 @@
         time.timeInMillis = timeOverrideInMillis ?: System.currentTimeMillis()
         contentDescription = DateFormat.format(descFormat, time)
         val formattedText = DateFormat.format(format, time)
-        logger.d({ "refreshTime: new formattedText=$str1" }) { str1 = formattedText?.toString() }
+        logger.d({ "refreshTime: new formattedText=${escapeTime(str1)}" }) {
+            str1 = formattedText?.toString()
+        }
 
         // Setting text actually triggers a layout pass in TextView (because the text view is set to
         // wrap_content width and TextView always relayouts for this). This avoids needless relayout
@@ -195,7 +205,7 @@
         }
 
         text = formattedText
-        logger.d({ "refreshTime: done setting new time text to: $str1" }) {
+        logger.d({ "refreshTime: done setting new time text to: ${escapeTime(str1)}" }) {
             str1 = formattedText?.toString()
         }
 
@@ -225,7 +235,7 @@
 
     @SuppressLint("DrawAllocation")
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
-        logger.d("onMeasure")
+        logger.onMeasure(widthMeasureSpec, heightMeasureSpec)
 
         if (!isSingleLineInternal && MeasureSpec.getMode(heightMeasureSpec) == EXACTLY) {
             // Call straight into TextView.setTextSize to avoid setting lastUnconstrainedTextSize
@@ -263,14 +273,14 @@
             canvas.translate(parentWidth / 4f, 0f)
         }
 
-        logger.d({ "onDraw($str1)" }) { str1 = text.toString() }
+        logger.onDraw("$text")
         // intentionally doesn't call super.onDraw here or else the text will be rendered twice
         textAnimator?.draw(canvas)
         canvas.restore()
     }
 
     override fun invalidate() {
-        logger.d("invalidate")
+        logger.invalidate()
         super.invalidate()
     }
 
@@ -280,7 +290,7 @@
         lengthBefore: Int,
         lengthAfter: Int,
     ) {
-        logger.d({ "onTextChanged($str1)" }) { str1 = text.toString() }
+        logger.d({ "onTextChanged(${escapeTime(str1)})" }) { str1 = "$text" }
         super.onTextChanged(text, start, lengthBefore, lengthAfter)
     }
 
@@ -370,7 +380,7 @@
             return
         }
 
-        logger.d("animateCharge")
+        logger.animateCharge()
         val startAnimPhase2 = Runnable {
             setTextStyle(
                 weight = if (isDozing()) dozingWeight else lockScreenWeight,
@@ -394,7 +404,7 @@
     }
 
     fun animateDoze(isDozing: Boolean, animate: Boolean) {
-        logger.d("animateDoze")
+        logger.animateDoze(isDozing, animate)
         setTextStyle(
             weight = if (isDozing) dozingWeight else lockScreenWeight,
             color = if (isDozing) dozingColor else lockScreenColor,
@@ -484,7 +494,7 @@
                 isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12
                 else -> DOUBLE_LINE_FORMAT_12_HOUR
             }
-        logger.d({ "refreshFormat($str1)" }) { str1 = format?.toString() }
+        logger.d({ "refreshFormat(${escapeTime(str1)})" }) { str1 = format?.toString() }
 
         descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12
         refreshTime()
@@ -634,7 +644,7 @@
 
     companion object {
         private val TAG = AnimatableClockView::class.simpleName!!
-        private val DEFAULT_LOGGER = Logger(LogcatOnlyMessageBuffer(LogLevel.WARNING), TAG)
+        private val DEFAULT_LOGGER = ClockLogger(null, LogcatOnlyMessageBuffer(LogLevel.DEBUG), TAG)
 
         const val ANIMATION_DURATION_FOLD_TO_AOD: Int = 600
         private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/CanvasUtil.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/CanvasUtil.kt
index dd1599e..9857d7f 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/CanvasUtil.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/CanvasUtil.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.shared.clocks
 
 import android.graphics.Canvas
+import com.android.systemui.plugins.clocks.VPoint
+import com.android.systemui.plugins.clocks.VPointF
 
 object CanvasUtil {
     fun Canvas.translate(pt: VPointF) = this.translate(pt.x, pt.y)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index 37acbe2..5f71b19 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -22,10 +22,10 @@
 import com.android.systemui.log.core.Logger
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
@@ -111,7 +111,7 @@
 
             override fun onZenDataChanged(data: ZenData) {}
 
-            override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+            override fun onFontAxesChanged(axes: ClockAxisStyle) {
                 view.updateAxes(axes)
             }
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index bc4bdf4..3cfa78d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.log.core.MessageBuffer
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockConfig
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockEventListener
@@ -33,7 +34,6 @@
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockSettings
 import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
@@ -232,7 +232,7 @@
 
         override fun onZenDataChanged(data: ZenData) {}
 
-        override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {}
+        override fun onFontAxesChanged(axes: ClockAxisStyle) {}
     }
 
     open inner class DefaultClockAnimations(
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index c3935e6..d778bc0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -20,6 +20,8 @@
 import android.view.LayoutInflater
 import com.android.systemui.customization.R
 import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.AxisPresetConfig
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
 import com.android.systemui.plugins.clocks.ClockLogger
@@ -28,7 +30,7 @@
 import com.android.systemui.plugins.clocks.ClockPickerConfig
 import com.android.systemui.plugins.clocks.ClockProvider
 import com.android.systemui.plugins.clocks.ClockSettings
-import com.android.systemui.shared.clocks.FlexClockController.Companion.AXIS_PRESETS
+import com.android.systemui.shared.clocks.FlexClockController.Companion.buildPresetGroup
 import com.android.systemui.shared.clocks.FlexClockController.Companion.getDefaultAxes
 
 private val TAG = DefaultClockProvider::class.simpleName
@@ -80,7 +82,7 @@
         return if (isClockReactiveVariantsEnabled) {
             val buffers = messageBuffers ?: ClockMessageBuffers(ClockLogger.DEFAULT_MESSAGE_BUFFER)
             val fontAxes = getDefaultAxes(settings).merge(settings.axes)
-            val clockSettings = settings.copy(axes = fontAxes.map { it.toSetting() })
+            val clockSettings = settings.copy(axes = ClockAxisStyle(fontAxes))
             val typefaceCache =
                 TypefaceCache(buffers.infraMessageBuffer, NUM_CLOCK_FONT_ANIMATION_STEPS) {
                     FLEX_TYPEFACE
@@ -106,17 +108,35 @@
             throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG")
         }
 
-        return ClockPickerConfig(
-            settings.clockId ?: DEFAULT_CLOCK_ID,
-            resources.getString(R.string.clock_default_name),
-            resources.getString(R.string.clock_default_description),
-            resources.getDrawable(R.drawable.clock_default_thumbnail, null),
-            isReactiveToTone = true,
-            axes =
-                if (!isClockReactiveVariantsEnabled) emptyList()
-                else getDefaultAxes(settings).merge(settings.axes),
-            axisPresets = if (!isClockReactiveVariantsEnabled) emptyList() else AXIS_PRESETS,
-        )
+        if (!isClockReactiveVariantsEnabled) {
+            return ClockPickerConfig(
+                settings.clockId ?: DEFAULT_CLOCK_ID,
+                resources.getString(R.string.clock_default_name),
+                resources.getString(R.string.clock_default_description),
+                resources.getDrawable(R.drawable.clock_default_thumbnail, null),
+                isReactiveToTone = true,
+                axes = emptyList(),
+                presetConfig = null,
+            )
+        } else {
+            val fontAxes = getDefaultAxes(settings).merge(settings.axes)
+            return ClockPickerConfig(
+                settings.clockId ?: DEFAULT_CLOCK_ID,
+                resources.getString(R.string.clock_default_name),
+                resources.getString(R.string.clock_default_description),
+                resources.getDrawable(R.drawable.clock_default_thumbnail, null),
+                isReactiveToTone = true,
+                axes = fontAxes,
+                presetConfig =
+                    AxisPresetConfig(
+                            listOf(
+                                buildPresetGroup(resources, isRound = true),
+                                buildPresetGroup(resources, isRound = false),
+                            )
+                        )
+                        .let { cfg -> cfg.copy(current = cfg.findStyle(ClockAxisStyle(fontAxes))) },
+            )
+        }
     }
 
     companion object {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DigitTranslateAnimator.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DigitTranslateAnimator.kt
index f5ccc52..941cebf 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DigitTranslateAnimator.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DigitTranslateAnimator.kt
@@ -20,7 +20,8 @@
 import android.animation.AnimatorListenerAdapter
 import android.animation.TimeInterpolator
 import android.animation.ValueAnimator
-import com.android.systemui.shared.clocks.VPointF.Companion.times
+import com.android.systemui.plugins.clocks.VPointF
+import com.android.systemui.plugins.clocks.VPointF.Companion.times
 
 class DigitTranslateAnimator(private val updateCallback: (VPointF) -> Unit) {
     var currentTranslation = VPointF.ZERO
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 5acd446..96c3ac7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -16,20 +16,24 @@
 
 package com.android.systemui.shared.clocks
 
+import android.content.res.Resources
 import com.android.systemui.animation.GSFAxes
 import com.android.systemui.customization.R
 import com.android.systemui.plugins.clocks.AlarmData
+import com.android.systemui.plugins.clocks.AxisPresetConfig
 import com.android.systemui.plugins.clocks.AxisType
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockConfig
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockEventListener
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFontAxis
 import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ClockSettings
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.FontUtils.put
+import com.android.systemui.shared.clocks.FontUtils.toClockAxis
 import com.android.systemui.shared.clocks.view.FlexClockView
 import java.io.PrintWriter
 import java.util.Locale
@@ -96,8 +100,8 @@
                 largeClock.events.onZenDataChanged(data)
             }
 
-            override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
-                val fontAxes = getDefaultAxes(clockCtx.settings).merge(axes).map { it.toSetting() }
+            override fun onFontAxesChanged(axes: ClockAxisStyle) {
+                val fontAxes = ClockAxisStyle(getDefaultAxes(clockCtx.settings).merge(axes))
                 smallClock.events.onFontAxesChanged(fontAxes)
                 largeClock.events.onFontAxesChanged(fontAxes)
             }
@@ -162,66 +166,46 @@
                 ),
             )
 
-        private val LEGACY_FLEX_SETTINGS =
-            listOf(
-                GSFAxes.WEIGHT.toClockAxisSetting(600f),
-                GSFAxes.WIDTH.toClockAxisSetting(100f),
-                GSFAxes.ROUND.toClockAxisSetting(100f),
-                GSFAxes.SLANT.toClockAxisSetting(0f),
-            )
+        private val LEGACY_FLEX_SETTINGS = ClockAxisStyle {
+            put(GSFAxes.WEIGHT, 600f)
+            put(GSFAxes.WIDTH, 100f)
+            put(GSFAxes.ROUND, 100f)
+            put(GSFAxes.SLANT, 0f)
+        }
 
-        val AXIS_PRESETS =
-            listOf(
-                FONT_AXES.map { it.toSetting() },
-                LEGACY_FLEX_SETTINGS,
-                listOf( // Porcelain
-                    GSFAxes.WEIGHT.toClockAxisSetting(500f),
-                    GSFAxes.WIDTH.toClockAxisSetting(100f),
-                    GSFAxes.ROUND.toClockAxisSetting(0f),
-                    GSFAxes.SLANT.toClockAxisSetting(0f),
-                ),
-                listOf( // Midnight
-                    GSFAxes.WEIGHT.toClockAxisSetting(300f),
-                    GSFAxes.WIDTH.toClockAxisSetting(100f),
-                    GSFAxes.ROUND.toClockAxisSetting(100f),
-                    GSFAxes.SLANT.toClockAxisSetting(-10f),
-                ),
-                listOf( // Sterling
-                    GSFAxes.WEIGHT.toClockAxisSetting(1000f),
-                    GSFAxes.WIDTH.toClockAxisSetting(100f),
-                    GSFAxes.ROUND.toClockAxisSetting(0f),
-                    GSFAxes.SLANT.toClockAxisSetting(0f),
-                ),
-                listOf( // Smoky Green
-                    GSFAxes.WEIGHT.toClockAxisSetting(150f),
-                    GSFAxes.WIDTH.toClockAxisSetting(50f),
-                    GSFAxes.ROUND.toClockAxisSetting(0f),
-                    GSFAxes.SLANT.toClockAxisSetting(0f),
-                ),
-                listOf( // Iris
-                    GSFAxes.WEIGHT.toClockAxisSetting(500f),
-                    GSFAxes.WIDTH.toClockAxisSetting(100f),
-                    GSFAxes.ROUND.toClockAxisSetting(100f),
-                    GSFAxes.SLANT.toClockAxisSetting(0f),
-                ),
-                listOf( // Margarita
-                    GSFAxes.WEIGHT.toClockAxisSetting(300f),
-                    GSFAxes.WIDTH.toClockAxisSetting(30f),
-                    GSFAxes.ROUND.toClockAxisSetting(100f),
-                    GSFAxes.SLANT.toClockAxisSetting(-10f),
-                ),
-                listOf( // Raspberry
-                    GSFAxes.WEIGHT.toClockAxisSetting(700f),
-                    GSFAxes.WIDTH.toClockAxisSetting(140f),
-                    GSFAxes.ROUND.toClockAxisSetting(100f),
-                    GSFAxes.SLANT.toClockAxisSetting(-7f),
-                ),
-                listOf( // Ultra Blue
-                    GSFAxes.WEIGHT.toClockAxisSetting(850f),
-                    GSFAxes.WIDTH.toClockAxisSetting(130f),
-                    GSFAxes.ROUND.toClockAxisSetting(0f),
-                    GSFAxes.SLANT.toClockAxisSetting(0f),
-                ),
+        private val PRESET_COUNT = 8
+        private val PRESET_WIDTH_INIT = 30f
+        private val PRESET_WIDTH_STEP = 12.5f
+        private val PRESET_WEIGHT_INIT = 800f
+        private val PRESET_WEIGHT_STEP = -100f
+        private val BASE_PRESETS: List<ClockAxisStyle> = run {
+            val presets = mutableListOf<ClockAxisStyle>()
+            var weight = PRESET_WEIGHT_INIT
+            var width = PRESET_WIDTH_INIT
+            for (i in 1..PRESET_COUNT) {
+                presets.add(
+                    ClockAxisStyle {
+                        put(GSFAxes.WEIGHT, weight)
+                        put(GSFAxes.WIDTH, width)
+                        put(GSFAxes.ROUND, 0f)
+                        put(GSFAxes.SLANT, 0f)
+                    }
+                )
+
+                weight += PRESET_WEIGHT_STEP
+                width += PRESET_WIDTH_STEP
+            }
+
+            return@run presets
+        }
+
+        fun buildPresetGroup(resources: Resources, isRound: Boolean): AxisPresetConfig.Group {
+            val round = if (isRound) GSFAxes.ROUND.maxValue else GSFAxes.ROUND.minValue
+            return AxisPresetConfig.Group(
+                presets = BASE_PRESETS.map { it.copy { put(GSFAxes.ROUND, round) } },
+                // TODO(b/395647577): Placeholder Icon; Replace or remove
+                icon = resources.getDrawable(R.drawable.clock_default_thumbnail, null),
             )
+        }
     }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 578a489..171a68f 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -25,16 +25,18 @@
 import com.android.systemui.customization.R
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 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.ClockFaceLayout
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.FontUtils.get
+import com.android.systemui.shared.clocks.FontUtils.set
 import com.android.systemui.shared.clocks.ViewUtils.computeLayoutDiff
 import com.android.systemui.shared.clocks.view.FlexClockView
 import com.android.systemui.shared.clocks.view.HorizontalAlignment
@@ -129,17 +131,10 @@
             layerController.faceEvents.onThemeChanged(theme)
         }
 
-        override fun onFontAxesChanged(settings: List<ClockFontAxisSetting>) {
-            var axes = settings
-            if (!isLargeClock) {
-                axes =
-                    axes.map { axis ->
-                        if (axis.key == GSFAxes.WIDTH.tag && axis.value > SMALL_CLOCK_MAX_WDTH) {
-                            axis.copy(value = SMALL_CLOCK_MAX_WDTH)
-                        } else {
-                            axis
-                        }
-                    }
+        override fun onFontAxesChanged(settings: ClockAxisStyle) {
+            var axes = ClockAxisStyle(settings)
+            if (!isLargeClock && axes[GSFAxes.WIDTH] > SMALL_CLOCK_MAX_WDTH) {
+                axes[GSFAxes.WIDTH] = SMALL_CLOCK_MAX_WDTH
             }
 
             layerController.events.onFontAxesChanged(axes)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
index 212b1e2..722d76b 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
@@ -18,26 +18,36 @@
 
 import com.android.systemui.animation.AxisDefinition
 import com.android.systemui.plugins.clocks.AxisType
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockFontAxis
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 
-fun AxisDefinition.toClockAxis(
-    type: AxisType,
-    currentValue: Float? = null,
-    name: String,
-    description: String,
-): ClockFontAxis {
-    return ClockFontAxis(
-        key = this.tag,
-        type = type,
-        maxValue = this.maxValue,
-        minValue = this.minValue,
-        currentValue = currentValue ?: this.defaultValue,
-        name = name,
-        description = description,
-    )
-}
+object FontUtils {
+    fun AxisDefinition.toClockAxis(
+        type: AxisType,
+        currentValue: Float? = null,
+        name: String,
+        description: String,
+    ): ClockFontAxis {
+        return ClockFontAxis(
+            key = this.tag,
+            type = type,
+            maxValue = this.maxValue,
+            minValue = this.minValue,
+            currentValue = currentValue ?: this.defaultValue,
+            name = name,
+            description = description,
+        )
+    }
 
-fun AxisDefinition.toClockAxisSetting(value: Float? = null): ClockFontAxisSetting {
-    return ClockFontAxisSetting(this.tag, value ?: this.defaultValue)
+    fun ClockAxisStyle.put(def: AxisDefinition, value: Float? = null) {
+        this.put(def.tag, value ?: def.defaultValue)
+    }
+
+    operator fun ClockAxisStyle.set(def: AxisDefinition, value: Float) {
+        this[def.tag] = value
+    }
+
+    operator fun ClockAxisStyle.get(def: AxisDefinition): Float {
+        return this[def.tag] ?: def.defaultValue
+    }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt
index 336c66e..9ac9e60 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt
@@ -16,13 +16,13 @@
 
 package com.android.systemui.shared.clocks
 
-import android.graphics.RectF
 import android.view.View
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.plugins.clocks.ClockAnimations
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.VRectF
 
 interface SimpleClockLayerController {
     val view: View
@@ -32,5 +32,5 @@
     val config: ClockFaceConfig
 
     @VisibleForTesting var fakeTimeMills: Long?
-    var onViewBoundsChanged: ((RectF) -> Unit)?
+    var onViewBoundsChanged: ((VRectF) -> Unit)?
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
index 1d963af..7be9a93 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -26,10 +26,10 @@
 import com.android.systemui.log.core.Logger
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockAnimations
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
@@ -172,7 +172,7 @@
 
             override fun onZenDataChanged(data: ZenData) {}
 
-            override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+            override fun onFontAxesChanged(axes: ClockAxisStyle) {
                 view.updateAxes(axes)
             }
         }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ViewUtils.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ViewUtils.kt
index 1e90a23..0740b0e 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ViewUtils.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ViewUtils.kt
@@ -18,8 +18,9 @@
 
 import android.graphics.Rect
 import android.view.View
-import com.android.systemui.shared.clocks.VPoint.Companion.center
-import com.android.systemui.shared.clocks.VPointF.Companion.center
+import com.android.systemui.plugins.clocks.VPoint.Companion.center
+import com.android.systemui.plugins.clocks.VPointF
+import com.android.systemui.plugins.clocks.VPointF.Companion.center
 
 object ViewUtils {
     fun View.computeLayoutDiff(targetRegion: Rect, isLargeClock: Boolean): VPointF {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index 2dc3e2b..4531aed 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.shared.clocks.view
 
 import android.graphics.Canvas
-import android.graphics.RectF
 import android.icu.text.NumberFormat
 import android.util.MathUtils.constrainedMap
 import android.view.View
@@ -27,16 +26,17 @@
 import androidx.core.view.children
 import com.android.app.animation.Interpolators
 import com.android.systemui.customization.R
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockLogger
+import com.android.systemui.plugins.clocks.VPoint
+import com.android.systemui.plugins.clocks.VPointF
+import com.android.systemui.plugins.clocks.VPointF.Companion.max
+import com.android.systemui.plugins.clocks.VPointF.Companion.times
+import com.android.systemui.plugins.clocks.VRectF
 import com.android.systemui.shared.clocks.CanvasUtil.translate
 import com.android.systemui.shared.clocks.CanvasUtil.use
 import com.android.systemui.shared.clocks.ClockContext
 import com.android.systemui.shared.clocks.DigitTranslateAnimator
-import com.android.systemui.shared.clocks.VPoint
-import com.android.systemui.shared.clocks.VPointF
-import com.android.systemui.shared.clocks.VPointF.Companion.max
-import com.android.systemui.shared.clocks.VPointF.Companion.times
 import com.android.systemui.shared.clocks.ViewUtils.measuredSize
 import java.util.Locale
 import kotlin.collections.filterNotNull
@@ -101,7 +101,7 @@
         updateLocale(Locale.getDefault())
     }
 
-    var onViewBoundsChanged: ((RectF) -> Unit)? = null
+    var onViewBoundsChanged: ((VRectF) -> Unit)? = null
     private val digitOffsets = mutableMapOf<Int, Float>()
 
     protected fun calculateSize(
@@ -189,13 +189,7 @@
 
     fun updateLocation() {
         val layoutBounds = this.layoutBounds ?: return
-        val bounds =
-            RectF(
-                layoutBounds.centerX() - measuredWidth / 2f,
-                layoutBounds.centerY() - measuredHeight / 2f,
-                layoutBounds.centerX() + measuredWidth / 2f,
-                layoutBounds.centerY() + measuredHeight / 2f,
-            )
+        val bounds = VRectF.fromCenter(layoutBounds.center, this.measuredSize)
         setFrame(
             bounds.left.roundToInt(),
             bounds.top.roundToInt(),
@@ -215,16 +209,11 @@
         onAnimateDoze = null
     }
 
-    private val layoutBounds = RectF()
+    private var layoutBounds = VRectF.ZERO
 
     override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
         logger.onLayout(changed, left, top, right, bottom)
-
-        layoutBounds.left = left.toFloat()
-        layoutBounds.top = top.toFloat()
-        layoutBounds.right = right.toFloat()
-        layoutBounds.bottom = bottom.toFloat()
-
+        layoutBounds = VRectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
         updateChildFrames(isLayout = true)
     }
 
@@ -283,7 +272,7 @@
         invalidate()
     }
 
-    fun updateAxes(axes: List<ClockFontAxisSetting>) {
+    fun updateAxes(axes: ClockAxisStyle) {
         childViews.forEach { view -> view.updateAxes(axes) }
         requestLayout()
     }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 0ec2d18..377a24c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -23,9 +23,7 @@
 import android.graphics.PorterDuff
 import android.graphics.PorterDuffXfermode
 import android.graphics.Rect
-import android.graphics.RectF
 import android.os.VibrationEffect
-import android.text.Layout
 import android.text.TextPaint
 import android.util.AttributeSet
 import android.util.Log
@@ -38,13 +36,17 @@
 import android.widget.TextView
 import com.android.app.animation.Interpolators
 import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.animation.AxisDefinition
 import com.android.systemui.animation.GSFAxes
 import com.android.systemui.animation.TextAnimator
+import com.android.systemui.animation.TextAnimatorListener
 import com.android.systemui.customization.R
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.replace
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting.Companion.toFVar
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockLogger
+import com.android.systemui.plugins.clocks.VPoint
+import com.android.systemui.plugins.clocks.VPointF
+import com.android.systemui.plugins.clocks.VPointF.Companion.size
+import com.android.systemui.plugins.clocks.VRectF
 import com.android.systemui.shared.Flags.ambientAod
 import com.android.systemui.shared.clocks.CanvasUtil.translate
 import com.android.systemui.shared.clocks.CanvasUtil.use
@@ -53,12 +55,9 @@
 import com.android.systemui.shared.clocks.DimensionParser
 import com.android.systemui.shared.clocks.FLEX_CLOCK_ID
 import com.android.systemui.shared.clocks.FontTextStyle
-import com.android.systemui.shared.clocks.VPoint
-import com.android.systemui.shared.clocks.VPointF
-import com.android.systemui.shared.clocks.VPointF.Companion.size
+import com.android.systemui.shared.clocks.FontUtils.set
 import com.android.systemui.shared.clocks.ViewUtils.measuredSize
 import com.android.systemui.shared.clocks.ViewUtils.size
-import com.android.systemui.shared.clocks.toClockAxisSetting
 import java.lang.Thread
 import kotlin.math.max
 import kotlin.math.min
@@ -66,11 +65,11 @@
 
 private val TAG = SimpleDigitalClockTextView::class.simpleName!!
 
-private fun Paint.getTextBounds(text: CharSequence, result: RectF = RectF()): RectF {
-    val rect = Rect()
-    this.getTextBounds(text, 0, text.length, rect)
-    result.set(rect)
-    return result
+private val tempRect = Rect()
+
+private fun Paint.getTextBounds(text: CharSequence): VRectF {
+    this.getTextBounds(text, 0, text.length, tempRect)
+    return VRectF(tempRect)
 }
 
 enum class VerticalAlignment {
@@ -123,9 +122,9 @@
     private val isLegacyFlex = clockCtx.settings.clockId == FLEX_CLOCK_ID
     private val fixedAodAxes =
         when {
-            !isLegacyFlex -> listOf(AOD_WEIGHT_AXIS, WIDTH_AXIS)
-            isLargeClock -> listOf(FLEX_AOD_LARGE_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
-            else -> listOf(FLEX_AOD_SMALL_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
+            !isLegacyFlex -> fromAxes(AOD_WEIGHT_AXIS, WIDTH_AXIS)
+            isLargeClock -> fromAxes(FLEX_AOD_LARGE_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
+            else -> fromAxes(FLEX_AOD_SMALL_WEIGHT_AXIS, FLEX_AOD_WIDTH_AXIS)
         }
 
     private var lsFontVariation: String
@@ -135,15 +134,15 @@
     init {
         val roundAxis = if (!isLegacyFlex) ROUND_AXIS else FLEX_ROUND_AXIS
         val lsFontAxes =
-            if (!isLegacyFlex) listOf(LS_WEIGHT_AXIS, WIDTH_AXIS, ROUND_AXIS, SLANT_AXIS)
-            else listOf(FLEX_LS_WEIGHT_AXIS, FLEX_LS_WIDTH_AXIS, FLEX_ROUND_AXIS, SLANT_AXIS)
+            if (!isLegacyFlex) fromAxes(LS_WEIGHT_AXIS, WIDTH_AXIS, ROUND_AXIS, SLANT_AXIS)
+            else fromAxes(FLEX_LS_WEIGHT_AXIS, FLEX_LS_WIDTH_AXIS, FLEX_ROUND_AXIS, SLANT_AXIS)
 
         lsFontVariation = lsFontAxes.toFVar()
-        aodFontVariation = (fixedAodAxes + listOf(roundAxis, SLANT_AXIS)).toFVar()
+        aodFontVariation = fixedAodAxes.copyWith(fromAxes(roundAxis, SLANT_AXIS)).toFVar()
         fidgetFontVariation = buildFidgetVariation(lsFontAxes).toFVar()
     }
 
-    var onViewBoundsChanged: ((RectF) -> Unit)? = null
+    var onViewBoundsChanged: ((VRectF) -> Unit)? = null
     private val parser = DimensionParser(clockCtx.context)
     var maxSingleDigitHeight = -1f
     var maxSingleDigitWidth = -1f
@@ -159,13 +158,13 @@
     private val initThread = Thread.currentThread()
 
     // textBounds is the size of text in LS, which only measures current text in lockscreen style
-    var textBounds = RectF()
+    var textBounds = VRectF.ZERO
     // prevTextBounds and targetTextBounds are to deal with dozing animation between LS and AOD
     // especially for the textView which has different bounds during the animation
     // prevTextBounds holds the state we are transitioning from
-    private val prevTextBounds = RectF()
+    private var prevTextBounds = VRectF.ZERO
     // targetTextBounds holds the state we are interpolating to
-    private val targetTextBounds = RectF()
+    private var targetTextBounds = VRectF.ZERO
     protected val logger = ClockLogger(this, clockCtx.messageBuffer, this::class.simpleName!!)
         get() = field ?: ClockLogger.INIT_LOGGER
 
@@ -175,11 +174,6 @@
 
     private val typefaceCache = clockCtx.typefaceCache.getVariantCache("")
 
-    @VisibleForTesting
-    var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator = { layout, invalidateCb ->
-        TextAnimator(layout, typefaceCache, invalidateCb)
-    }
-
     var verticalAlignment: VerticalAlignment = VerticalAlignment.BASELINE
     var horizontalAlignment: HorizontalAlignment = HorizontalAlignment.CENTER
 
@@ -206,17 +200,17 @@
         invalidate()
     }
 
-    fun updateAxes(lsAxes: List<ClockFontAxisSetting>) {
+    fun updateAxes(lsAxes: ClockAxisStyle) {
         lsFontVariation = lsAxes.toFVar()
-        aodFontVariation = lsAxes.replace(fixedAodAxes).toFVar()
+        aodFontVariation = lsAxes.copyWith(fixedAodAxes).toFVar()
         fidgetFontVariation = buildFidgetVariation(lsAxes).toFVar()
         logger.updateAxes(lsFontVariation, aodFontVariation)
 
         lockScreenPaint.typeface = typefaceCache.getTypefaceForVariant(lsFontVariation)
         typeface = lockScreenPaint.typeface
 
-        lockScreenPaint.getTextBounds(text, textBounds)
-        targetTextBounds.set(textBounds)
+        textBounds = lockScreenPaint.getTextBounds(text)
+        targetTextBounds = textBounds
 
         textAnimator.setTextStyle(TextAnimator.Style(fVar = lsFontVariation))
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
@@ -225,19 +219,16 @@
         invalidate()
     }
 
-    fun buildFidgetVariation(axes: List<ClockFontAxisSetting>): List<ClockFontAxisSetting> {
-        val result = mutableListOf<ClockFontAxisSetting>()
-        for (axis in axes) {
-            result.add(
-                FIDGET_DISTS.get(axis.key)?.let { (dist, midpoint) ->
-                    ClockFontAxisSetting(
-                        axis.key,
-                        axis.value + dist * if (axis.value > midpoint) -1 else 1,
-                    )
-                } ?: axis
-            )
-        }
-        return result
+    fun buildFidgetVariation(axes: ClockAxisStyle): ClockAxisStyle {
+        return ClockAxisStyle(
+            axes.items
+                .map { (key, value) ->
+                    FIDGET_DISTS.get(key)?.let { (dist, midpoint) ->
+                        key to value + dist * if (value > midpoint) -1 else 1
+                    } ?: (key to value)
+                }
+                .toMap()
+        )
     }
 
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
@@ -247,7 +238,18 @@
         val layout = this.layout
         if (layout != null) {
             if (!this::textAnimator.isInitialized) {
-                textAnimator = textAnimatorFactory(layout, ::invalidate)
+                textAnimator =
+                    TextAnimator(
+                        layout,
+                        typefaceCache,
+                        object : TextAnimatorListener {
+                            override fun onInvalidate() = invalidate()
+
+                            override fun onRebased() = updateTextBounds()
+
+                            override fun onPaintModified() = updateTextBounds()
+                        },
+                    )
                 setInterpolatorPaint()
             } else {
                 textAnimator.updateLayout(layout)
@@ -272,7 +274,7 @@
     override fun onDraw(canvas: Canvas) {
         logger.onDraw(textAnimator.textInterpolator.shapedText)
 
-        val interpProgress = getInterpolatedProgress()
+        val interpProgress = textAnimator.progress
         val interpBounds = getInterpolatedTextBounds(interpProgress)
         if (interpProgress != drawnProgress) {
             drawnProgress = interpProgress
@@ -287,7 +289,7 @@
         canvas.use {
             digitTranslateAnimator?.apply { canvas.translate(currentTranslation) }
             canvas.translate(getDrawTranslation(interpBounds))
-            if (isLayoutRtl()) canvas.translate(interpBounds.width() - textBounds.width(), 0f)
+            if (isLayoutRtl()) canvas.translate(interpBounds.width - textBounds.width, 0f)
             textAnimator.draw(canvas)
         }
     }
@@ -302,16 +304,12 @@
         super.setAlpha(alpha)
     }
 
-    private val layoutBounds = RectF()
+    private var layoutBounds = VRectF.ZERO
 
     override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
         super.onLayout(changed, left, top, right, bottom)
         logger.onLayout(changed, left, top, right, bottom)
-
-        layoutBounds.left = left.toFloat()
-        layoutBounds.top = top.toFloat()
-        layoutBounds.right = right.toFloat()
-        layoutBounds.bottom = bottom.toFloat()
+        layoutBounds = VRectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
     }
 
     override fun invalidate() {
@@ -327,7 +325,7 @@
 
     fun animateDoze(isDozing: Boolean, isAnimated: Boolean) {
         if (!this::textAnimator.isInitialized) return
-        logger.animateDoze()
+        logger.animateDoze(isDozing, isAnimated)
         textAnimator.setTextStyle(
             TextAnimator.Style(
                 fVar = if (isDozing) aodFontVariation else lsFontVariation,
@@ -340,7 +338,11 @@
                 interpolator = aodDozingInterpolator,
             ),
         )
-        updateTextBoundsForTextAnimator()
+
+        if (!isAnimated) {
+            requestLayout()
+            (parent as? FlexClockView)?.requestLayout()
+        }
     }
 
     fun animateCharge() {
@@ -366,11 +368,9 @@
                             duration = CHARGE_ANIMATION_DURATION,
                         ),
                     )
-                    updateTextBoundsForTextAnimator()
                 },
             ),
         )
-        updateTextBoundsForTextAnimator()
     }
 
     fun animateFidget(x: Float, y: Float) = animateFidget(0L)
@@ -400,18 +400,16 @@
                             interpolator = FIDGET_INTERPOLATOR,
                         ),
                     )
-                    updateTextBoundsForTextAnimator()
                 },
             ),
         )
-        updateTextBoundsForTextAnimator()
     }
 
     fun refreshText() {
-        lockScreenPaint.getTextBounds(text, textBounds)
-        if (this::textAnimator.isInitialized) {
-            textAnimator.textInterpolator.targetPaint.getTextBounds(text, targetTextBounds)
-        }
+        textBounds = lockScreenPaint.getTextBounds(text)
+        targetTextBounds =
+            if (!this::textAnimator.isInitialized) textBounds
+            else textAnimator.textInterpolator.targetPaint.getTextBounds(text)
 
         if (layout == null) {
             requestLayout()
@@ -427,28 +425,24 @@
             id == R.id.MINUTE_SECOND_DIGIT
     }
 
-    private fun getInterpolatedProgress(): Float {
-        return textAnimator.animator?.let { it.animatedValue as Float } ?: 1f
-    }
-
     /** Returns the interpolated text bounding rect based on interpolation progress */
-    private fun getInterpolatedTextBounds(progress: Float = getInterpolatedProgress()): RectF {
+    private fun getInterpolatedTextBounds(progress: Float = textAnimator.progress): VRectF {
         if (progress <= 0f) {
             return prevTextBounds
         } else if (!textAnimator.isRunning || progress >= 1f) {
             return targetTextBounds
         }
 
-        return RectF().apply {
-            left = lerp(prevTextBounds.left, targetTextBounds.left, progress)
-            right = lerp(prevTextBounds.right, targetTextBounds.right, progress)
-            top = lerp(prevTextBounds.top, targetTextBounds.top, progress)
-            bottom = lerp(prevTextBounds.bottom, targetTextBounds.bottom, progress)
-        }
+        return VRectF(
+            left = lerp(prevTextBounds.left, targetTextBounds.left, progress),
+            right = lerp(prevTextBounds.right, targetTextBounds.right, progress),
+            top = lerp(prevTextBounds.top, targetTextBounds.top, progress),
+            bottom = lerp(prevTextBounds.bottom, targetTextBounds.bottom, progress),
+        )
     }
 
     private fun computeMeasuredSize(
-        interpBounds: RectF,
+        interpBounds: VRectF,
         widthMeasureSpec: Int = measuredWidthAndState,
         heightMeasureSpec: Int = measuredHeightAndState,
     ): VPointF {
@@ -461,11 +455,11 @@
         return VPointF(
             when {
                 mode.x == EXACTLY -> MeasureSpec.getSize(widthMeasureSpec).toFloat()
-                else -> interpBounds.width() + 2 * lockScreenPaint.strokeWidth
+                else -> interpBounds.width + 2 * lockScreenPaint.strokeWidth
             },
             when {
                 mode.y == EXACTLY -> MeasureSpec.getSize(heightMeasureSpec).toFloat()
-                else -> interpBounds.height() + 2 * lockScreenPaint.strokeWidth
+                else -> interpBounds.height + 2 * lockScreenPaint.strokeWidth
             },
         )
     }
@@ -486,47 +480,35 @@
             MeasureSpec.makeMeasureSpec(measureBounds.x.roundToInt(), mode.x),
             MeasureSpec.makeMeasureSpec(measureBounds.y.roundToInt(), mode.y),
         )
+
+        logger.d({
+            val size = VPointF.fromLong(long1)
+            val mode = VPoint.fromLong(long2)
+            "setInterpolatedSize(size=$size, mode=$mode)"
+        }) {
+            long1 = measureBounds.toLong()
+            long2 = mode.toLong()
+        }
     }
 
     /** Set the location of the view to match the interpolated text bounds */
-    private fun setInterpolatedLocation(measureSize: VPointF): RectF {
-        val targetRect = RectF()
-        targetRect.apply {
-            when (xAlignment) {
-                XAlignment.LEFT -> {
-                    left = layoutBounds.left
-                    right = layoutBounds.left + measureSize.x
-                }
-                XAlignment.CENTER -> {
-                    left = layoutBounds.centerX() - measureSize.x / 2f
-                    right = layoutBounds.centerX() + measureSize.x / 2f
-                }
-                XAlignment.RIGHT -> {
-                    left = layoutBounds.right - measureSize.x
-                    right = layoutBounds.right
-                }
-            }
+    private fun setInterpolatedLocation(measureSize: VPointF): VRectF {
+        val pos =
+            VPointF(
+                when (xAlignment) {
+                    XAlignment.LEFT -> layoutBounds.left
+                    XAlignment.CENTER -> layoutBounds.center.x - measureSize.x / 2f
+                    XAlignment.RIGHT -> layoutBounds.right - measureSize.x
+                },
+                when (verticalAlignment) {
+                    VerticalAlignment.TOP -> layoutBounds.top
+                    VerticalAlignment.CENTER -> layoutBounds.center.y - measureSize.y / 2f
+                    VerticalAlignment.BOTTOM -> layoutBounds.bottom - measureSize.y
+                    VerticalAlignment.BASELINE -> layoutBounds.center.y - measureSize.y / 2f
+                },
+            )
 
-            when (verticalAlignment) {
-                VerticalAlignment.TOP -> {
-                    top = layoutBounds.top
-                    bottom = layoutBounds.top + measureSize.y
-                }
-                VerticalAlignment.CENTER -> {
-                    top = layoutBounds.centerY() - measureSize.y / 2f
-                    bottom = layoutBounds.centerY() + measureSize.y / 2f
-                }
-                VerticalAlignment.BOTTOM -> {
-                    top = layoutBounds.bottom - measureSize.y
-                    bottom = layoutBounds.bottom
-                }
-                VerticalAlignment.BASELINE -> {
-                    top = layoutBounds.centerY() - measureSize.y / 2f
-                    bottom = layoutBounds.centerY() + measureSize.y / 2f
-                }
-            }
-        }
-
+        val targetRect = VRectF.fromTopLeft(pos, measureSize)
         setFrame(
             targetRect.left.roundToInt(),
             targetRect.top.roundToInt(),
@@ -534,10 +516,13 @@
             targetRect.bottom.roundToInt(),
         )
         onViewBoundsChanged?.let { it(targetRect) }
+        logger.d({ "setInterpolatedLocation(${VRectF.fromLong(long1)})" }) {
+            long1 = targetRect.toLong()
+        }
         return targetRect
     }
 
-    private fun getDrawTranslation(interpBounds: RectF): VPointF {
+    private fun getDrawTranslation(interpBounds: VRectF): VPointF {
         val sizeDiff = this.measuredSize - interpBounds.size
         val alignment =
             VPointF(
@@ -586,11 +571,11 @@
         if (fontSizePx > 0) {
             setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSizePx)
             lockScreenPaint.textSize = textSize
-            lockScreenPaint.getTextBounds(text, textBounds)
-            targetTextBounds.set(textBounds)
+            textBounds = lockScreenPaint.getTextBounds(text)
+            targetTextBounds = textBounds
         }
         if (!constrainedByHeight) {
-            val lastUnconstrainedHeight = textBounds.height() + lockScreenPaint.strokeWidth * 2
+            val lastUnconstrainedHeight = textBounds.height + lockScreenPaint.strokeWidth * 2
             fontSizeAdjustFactor = lastUnconstrainedHeight / lastUnconstrainedTextSize
         }
 
@@ -608,8 +593,8 @@
 
         for (i in 0..9) {
             val rectForCalculate = lockScreenPaint.getTextBounds("$i")
-            maxSingleDigitHeight = max(maxSingleDigitHeight, rectForCalculate.height())
-            maxSingleDigitWidth = max(maxSingleDigitWidth, rectForCalculate.width())
+            maxSingleDigitHeight = max(maxSingleDigitHeight, rectForCalculate.height)
+            maxSingleDigitWidth = max(maxSingleDigitWidth, rectForCalculate.width)
         }
         maxSingleDigitWidth += 2 * lockScreenPaint.strokeWidth
         maxSingleDigitHeight += 2 * lockScreenPaint.strokeWidth
@@ -636,9 +621,10 @@
      * rebase if previous animator is canceled so basePaint will store the state we transition from
      * and targetPaint will store the state we transition to
      */
-    private fun updateTextBoundsForTextAnimator() {
-        textAnimator.textInterpolator.basePaint.getTextBounds(text, prevTextBounds)
-        textAnimator.textInterpolator.targetPaint.getTextBounds(text, targetTextBounds)
+    private fun updateTextBounds() {
+        drawnProgress = null
+        prevTextBounds = textAnimator.textInterpolator.basePaint.getTextBounds(text)
+        targetTextBounds = textAnimator.textInterpolator.targetPaint.getTextBounds(text)
     }
 
     /**
@@ -676,18 +662,22 @@
             )
 
         val AOD_COLOR = Color.WHITE
-        val LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(400f)
-        val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(200f)
-        val WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(85f)
-        val ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(0f)
-        val SLANT_AXIS = GSFAxes.SLANT.toClockAxisSetting(0f)
+        private val LS_WEIGHT_AXIS = GSFAxes.WEIGHT to 400f
+        private val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT to 200f
+        private val WIDTH_AXIS = GSFAxes.WIDTH to 85f
+        private val ROUND_AXIS = GSFAxes.ROUND to 0f
+        private val SLANT_AXIS = GSFAxes.SLANT to 0f
 
         // Axes for Legacy version of the Flex Clock
-        val FLEX_LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(600f)
-        val FLEX_AOD_LARGE_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(74f)
-        val FLEX_AOD_SMALL_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(133f)
-        val FLEX_LS_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(100f)
-        val FLEX_AOD_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(43f)
-        val FLEX_ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(100f)
+        private val FLEX_LS_WEIGHT_AXIS = GSFAxes.WEIGHT to 600f
+        private val FLEX_AOD_LARGE_WEIGHT_AXIS = GSFAxes.WEIGHT to 74f
+        private val FLEX_AOD_SMALL_WEIGHT_AXIS = GSFAxes.WEIGHT to 133f
+        private val FLEX_LS_WIDTH_AXIS = GSFAxes.WIDTH to 100f
+        private val FLEX_AOD_WIDTH_AXIS = GSFAxes.WIDTH to 43f
+        private val FLEX_ROUND_AXIS = GSFAxes.ROUND to 100f
+
+        private fun fromAxes(vararg axes: Pair<AxisDefinition, Float>): ClockAxisStyle {
+            return ClockAxisStyle(axes.map { (def, value) -> def.tag to value }.toMap())
+        }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 14d34d7..162218d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -87,7 +87,7 @@
                 contentResolver,
                 selectedUserInteractor,
                 lazyKeyguardUpdateMonitor,
-                dumpManager
+                dumpManager,
             )
     }
 
@@ -116,9 +116,9 @@
             )
         )
         assertFalse(
-                activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
-                        ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
-                )
+            activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+            )
         )
         assertTrue(
             activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -212,7 +212,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
             "",
-            currentUser
+            currentUser,
         )
         updateSetting(
             secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -285,7 +285,7 @@
             ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
             "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
                 "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
 
@@ -328,7 +328,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
             "${ActiveUnlockConfig.BiometricType.NONE.intValue}",
-            currentUser
+            currentUser,
         )
         updateSetting(
             secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -358,7 +358,7 @@
             ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
             "${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
                 "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
-            currentUser
+            currentUser,
         )
         updateSetting(
             secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
@@ -397,10 +397,10 @@
     @Test
     fun isWakeupConsideredUnlockIntent_singleValue() {
         // GIVEN lift is considered an unlock intent
-        secureSettings.putIntForUser(
+        secureSettings.putStringForUser(
             ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
-            PowerManager.WAKE_REASON_LIFT,
-            currentUser
+            PowerManager.WAKE_REASON_LIFT.toString(),
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
 
@@ -422,7 +422,7 @@
             PowerManager.WAKE_REASON_LIFT.toString() +
                 "|" +
                 PowerManager.WAKE_REASON_TAP.toString(),
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
 
@@ -452,7 +452,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
             " ",
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
 
@@ -479,7 +479,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
             PowerManager.WAKE_REASON_LIFT.toString(),
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
 
@@ -501,7 +501,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
             " ",
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
 
@@ -521,7 +521,7 @@
             PowerManager.WAKE_REASON_LIFT.toString() +
                 "|" +
                 PowerManager.WAKE_REASON_TAP.toString(),
-            currentUser
+            currentUser,
         )
         updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
 
@@ -544,7 +544,7 @@
         secureSettings.putStringForUser(
             ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
             "-1",
-            currentUser
+            currentUser,
         )
 
         // WHEN the setting updates
@@ -581,7 +581,7 @@
                 eq(uri),
                 eq(false),
                 capture(settingsObserverCaptor),
-                eq(UserHandle.USER_ALL)
+                eq(UserHandle.USER_ALL),
             )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index e26e19d..29647cd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -16,12 +16,18 @@
 
 package com.android.keyguard;
 
+import static com.android.internal.widget.flags.Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
+import android.hardware.input.InputManager;
+import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
 import android.view.ViewGroup;
@@ -90,6 +96,8 @@
     @Mock
     private UserActivityNotifier mUserActivityNotifier;
     private NumPadKey[] mButtons = new NumPadKey[]{};
+    @Mock
+    private InputManager mInputManager;
 
     private KeyguardPinBasedInputViewController mKeyguardPinViewController;
 
@@ -118,12 +126,13 @@
                 new KeyguardKeyboardInteractor(new FakeKeyboardRepository());
         FakeFeatureFlags featureFlags = new FakeFeatureFlags();
         mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_REVAMPED_BOUNCER_MESSAGES);
+        when(mLockPatternUtils.isPinEnhancedPrivacyEnabled(anyInt())).thenReturn(false);
         mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
                 mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
                 mKeyguardMessageAreaControllerFactory, mLatencyTracker,
                 mEmergencyButtonController, mFalsingCollector, featureFlags,
                 mSelectedUserInteractor, keyguardKeyboardInteractor, mBouncerHapticPlayer,
-                mUserActivityNotifier) {
+                mUserActivityNotifier, mInputManager) {
             @Override
             public void onResume(int reason) {
                 super.onResume(reason);
@@ -148,4 +157,112 @@
         mKeyguardPinViewController.resetState();
         verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pin);
     }
+
+    @Test
+    @EnableFlags(FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+    public void updateAnimations_addDevice_notKeyboard() {
+        when(mLockPatternUtils.isPinEnhancedPrivacyEnabled(anyInt())).thenReturn(false);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        mKeyguardPinViewController.onViewAttached();
+        mKeyguardPinViewController.onInputDeviceAdded(1);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+    }
+
+    @Test
+    @EnableFlags(FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+    public void updateAnimations_addDevice_keyboard() {
+        when(mLockPatternUtils.isPinEnhancedPrivacyEnabled(anyInt())).thenReturn(true);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        mKeyguardPinViewController.onViewAttached();
+        mKeyguardPinViewController.onInputDeviceAdded(1);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+    }
+
+    @Test
+    @EnableFlags(FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+    public void updateAnimations_addDevice_multipleKeyboards() {
+        when(mLockPatternUtils.isPinEnhancedPrivacyEnabled(anyInt())).thenReturn(true);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        mKeyguardPinViewController.onViewAttached();
+        mKeyguardPinViewController.onInputDeviceAdded(1);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+        mKeyguardPinViewController.onInputDeviceAdded(1);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+    }
+
+    @Test
+    @EnableFlags(FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+    public void updateAnimations_removeDevice_notKeyboard() {
+        when(mLockPatternUtils.isPinEnhancedPrivacyEnabled(anyInt())).thenReturn(false);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        mKeyguardPinViewController.onViewAttached();
+        mKeyguardPinViewController.onInputDeviceRemoved(1);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+    }
+
+    @Test
+    @EnableFlags(FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+    public void updateAnimations_removeDevice_keyboard() {
+        when(mLockPatternUtils.isPinEnhancedPrivacyEnabled(anyInt())).thenReturn(true, false);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        verify(mPasswordEntry, times(0)).setShowPassword(false);
+        mKeyguardPinViewController.onViewAttached();
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+        mKeyguardPinViewController.onInputDeviceRemoved(1);
+        verify(mPasswordEntry, times(2)).setShowPassword(true);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+    }
+
+    @Test
+    @EnableFlags(FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+    public void updateAnimations_removeDevice_multipleKeyboards() {
+        when(mLockPatternUtils.isPinEnhancedPrivacyEnabled(anyInt())).thenReturn(true, true);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        verify(mPasswordEntry, times(0)).setShowPassword(false);
+        mKeyguardPinViewController.onViewAttached();
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+        mKeyguardPinViewController.onInputDeviceRemoved(1);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+    }
+
+    @Test
+    @EnableFlags(FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+    public void updateAnimations_updateDevice_notKeyboard() {
+        when(mLockPatternUtils.isPinEnhancedPrivacyEnabled(anyInt())).thenReturn(false);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        mKeyguardPinViewController.onViewAttached();
+        mKeyguardPinViewController.onInputDeviceChanged(1);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+    }
+
+    @Test
+    @EnableFlags(FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+    public void updateAnimations_updateDevice_keyboard() {
+        when(mLockPatternUtils.isPinEnhancedPrivacyEnabled(anyInt())).thenReturn(true, false);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        verify(mPasswordEntry, times(0)).setShowPassword(false);
+        mKeyguardPinViewController.onViewAttached();
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+        mKeyguardPinViewController.onInputDeviceChanged(1);
+        verify(mPasswordEntry, times(2)).setShowPassword(true);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+    }
+
+    @Test
+    @EnableFlags(FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+    public void updateAnimations_updateDevice_multipleKeyboards() {
+        when(mLockPatternUtils.isPinEnhancedPrivacyEnabled(anyInt())).thenReturn(true, true);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        verify(mPasswordEntry, times(0)).setShowPassword(false);
+        mKeyguardPinViewController.onViewAttached();
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+        mKeyguardPinViewController.onInputDeviceChanged(1);
+        verify(mPasswordEntry, times(1)).setShowPassword(true);
+        verify(mPasswordEntry, times(1)).setShowPassword(false);
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 142a286..7fea06e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.keyguard
 
+import android.hardware.input.InputManager
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
@@ -104,6 +105,7 @@
     @Mock lateinit var enterButton: View
     @Mock lateinit var uiEventLogger: UiEventLogger
     @Mock lateinit var mUserActivityNotifier: UserActivityNotifier
+    @Mock lateinit var inputManager: InputManager
 
     @Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
 
@@ -154,6 +156,7 @@
             keyguardKeyboardInteractor,
             kosmos.bouncerHapticPlayer,
             mUserActivityNotifier,
+            inputManager,
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index c751a7d..003669d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.keyguard
 
+import android.hardware.input.InputManager
 import android.telephony.TelephonyManager
 import android.testing.TestableLooper
 import android.view.LayoutInflater
@@ -73,6 +74,7 @@
     @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
     private val updateMonitorCallbackArgumentCaptor =
         ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+    @Mock private lateinit var inputManager: InputManager
 
     private val kosmos = testKosmos()
 
@@ -107,6 +109,7 @@
                 keyguardKeyboardInteractor,
                 kosmos.bouncerHapticPlayer,
                 mUserActivityNotifier,
+                inputManager,
             )
         underTest.init()
         underTest.onViewAttached()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index c346825..85cb388 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.keyguard
 
+import android.hardware.input.InputManager
 import android.telephony.PinResult
 import android.telephony.TelephonyManager
 import android.testing.TestableLooper
@@ -65,6 +66,7 @@
     private lateinit var keyguardMessageAreaController:
         KeyguardMessageAreaController<BouncerKeyguardMessageArea>
     @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
+    @Mock private lateinit var inputManager: InputManager
 
     private val kosmos = testKosmos()
 
@@ -102,6 +104,7 @@
                 keyguardKeyboardInteractor,
                 kosmos.bouncerHapticPlayer,
                 mUserActivityNotifier,
+                inputManager,
             )
         underTest.init()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 245388c..b0db8b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -22,12 +22,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.customization.R as customR
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
 import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.statusbar.StatusBarState.KEYGUARD
 import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.testKosmos
 import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
@@ -47,16 +47,14 @@
 @RunWith(AndroidJUnit4::class)
 class KeyguardUnfoldTransitionTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     private val progressProvider: FakeUnfoldTransitionProvider =
         kosmos.fakeUnfoldTransitionProgressProvider
 
-    @Mock
-    private lateinit var keyguardRootView: KeyguardRootView
+    @Mock private lateinit var keyguardRootView: KeyguardRootView
 
-    @Mock
-    private lateinit var notificationShadeWindowView: NotificationShadeWindowView
+    @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
 
     @Mock private lateinit var statusBarStateController: StatusBarStateController
 
@@ -71,10 +69,14 @@
         xTranslationMax =
             context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
 
-        underTest = KeyguardUnfoldTransition(
-            context, keyguardRootView, notificationShadeWindowView,
-            statusBarStateController, progressProvider
-        )
+        underTest =
+            KeyguardUnfoldTransition(
+                context,
+                keyguardRootView,
+                notificationShadeWindowView,
+                statusBarStateController,
+                progressProvider,
+            )
 
         underTest.setup()
         underTest.statusViewCentered = false
@@ -88,9 +90,8 @@
         underTest.statusViewCentered = true
 
         val view = View(context)
-        whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
-            view
-        )
+        whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large))
+            .thenReturn(view)
 
         progressListener.onTransitionStarted()
         assertThat(view.translationX).isZero()
@@ -110,9 +111,8 @@
         whenever(statusBarStateController.getState()).thenReturn(SHADE)
 
         val view = View(context)
-        whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn(
-            view
-        )
+        whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large))
+            .thenReturn(view)
 
         progressListener.onTransitionStarted()
         assertThat(view.translationX).isZero()
@@ -133,9 +133,11 @@
 
         val view = View(context)
         whenever(
-            notificationShadeWindowView
-                .findViewById<View>(customR.id.lockscreen_clock_view_large)
-        ).thenReturn(view)
+                notificationShadeWindowView.findViewById<View>(
+                    customR.id.lockscreen_clock_view_large
+                )
+            )
+            .thenReturn(view)
 
         progressListener.onTransitionStarted()
         assertThat(view.translationX).isZero()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
index 7fb879c..f0c9141 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java
@@ -32,7 +32,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.animation.AnimatorTestRule;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
-import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/KairosCoreStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/KairosCoreStartableTest.kt
new file mode 100644
index 0000000..4daf023
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/KairosCoreStartableTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.kairos.runKairosTest
+import com.android.systemui.kairos.toColdConflatedFlow
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.launch
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalKairosApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KairosCoreStartableTest : SysuiTestCase() {
+
+    @Test
+    fun kairosNetwork_usedBeforeStarted() =
+        testKosmos().useUnconfinedTestDispatcher().runKairosTest {
+            lateinit var activatable: TestActivatable
+            val underTest = KairosCoreStartable(applicationCoroutineScope) { setOf(activatable) }
+            activatable = TestActivatable(underTest)
+
+            // collect from the cold flow before starting the CoreStartable
+            var collectCount = 0
+            testScope.backgroundScope.launch { activatable.coldFlow.collect { collectCount++ } }
+
+            // start the CoreStartable
+            underTest.start()
+
+            // verify emissions are received
+            activatable.emitEvent()
+
+            assertThat(collectCount).isEqualTo(1)
+        }
+
+    private class TestActivatable(network: KairosNetwork) : KairosBuilder by kairosBuilder() {
+        private val emitter = MutableSharedFlow<Unit>()
+        private val events = buildEvents { emitter.toEvents() }
+
+        val coldFlow = events.toColdConflatedFlow(network)
+
+        suspend fun emitEvent() = emitter.emit(Unit)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
index cde42bd..7660623 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
@@ -23,7 +23,6 @@
 import com.android.internal.accessibility.AccessibilityShortcutController
 import com.android.internal.accessibility.common.ShortcutConstants
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -31,6 +30,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -58,7 +58,7 @@
 
     private lateinit var extraDimDialogDelegate: ExtraDimDialogDelegate
 
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
 
     @Mock private lateinit var dialog: SystemUIDialog
@@ -79,7 +79,7 @@
                 kosmos.testDispatcher,
                 dialogFactory,
                 accessibilityManager,
-                userTracker
+                userTracker,
             )
     }
 
@@ -94,7 +94,7 @@
             verify(dialog)
                 .setPositiveButton(
                     eq(R.string.accessibility_deprecate_extra_dim_dialog_button),
-                    clickListener.capture()
+                    clickListener.capture(),
                 )
 
             clickListener.firstValue.onClick(dialog, 0)
@@ -110,7 +110,7 @@
                                 .flattenToString()
                         )
                     ),
-                    anyInt()
+                    anyInt(),
                 )
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
index 2713bb0..3040c12 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
@@ -83,10 +83,11 @@
         MockitoAnnotations.initMocks(this)
         testableLooper = TestableLooper.get(this)
         val mainHandler = Handler(testableLooper.looper)
-        systemSettings = FakeSettings()
+        val fakeSettings = FakeSettings()
+        systemSettings = fakeSettings
         // Guarantee that the systemSettings always starts with the default font scale.
         systemSettings.putFloatForUser(Settings.System.FONT_SCALE, 1.0f, userTracker.userId)
-        secureSettings = FakeSettings()
+        secureSettings = fakeSettings
         systemClock = FakeSystemClock()
         backgroundDelayableExecutor = FakeExecutor(systemClock)
         whenever(sysuiState.setFlag(anyLong(), anyBoolean())).thenReturn(sysuiState)
@@ -207,9 +208,9 @@
         dialog.show()
 
         val iconStartFrame: ViewGroup = dialog.findViewById(R.id.icon_start_frame)!!
-        secureSettings.putIntForUser(
+        secureSettings.putStringForUser(
             Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
-            OFF,
+            OFF.toString(),
             userTracker.userId,
         )
 
@@ -220,12 +221,11 @@
         backgroundDelayableExecutor.runAllReady()
 
         val currentSettings =
-            secureSettings.getIntForUser(
+            secureSettings.getStringForUser(
                 Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
-                /* def = */ OFF,
                 userTracker.userId,
             )
-        assertThat(currentSettings).isEqualTo(ON)
+        assertThat(currentSettings).isEqualTo(ON.toString())
 
         dialog.dismiss()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 0b13900..d118ace 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -71,6 +71,7 @@
 import com.android.systemui.bluetooth.qsdialog.DeviceItemType;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.shared.QSSettingsPackageRepository;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -108,6 +109,7 @@
     private static final String TEST_LABEL = "label";
     private static final int TEST_PRESET_INDEX = 1;
     private static final String TEST_PRESET_NAME = "test_preset";
+    private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
     private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
 
     @Mock
@@ -137,6 +139,12 @@
     @Mock
     private HearingDevicesUiEventLogger mUiEventLogger;
     @Mock
+    private QSSettingsPackageRepository mQSSettingsPackageRepository;
+    @Mock
+    private HearingDevicesInputRoutingController.Factory mInputRoutingFactory;
+    @Mock
+    private HearingDevicesInputRoutingController mInputRoutingController;
+    @Mock
     private CachedBluetoothDevice mCachedDevice;
     @Mock
     private BluetoothDevice mDevice;
@@ -164,6 +172,8 @@
         when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(List.of(mCachedDevice));
         when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
         when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
+        when(mQSSettingsPackageRepository.getSettingsPackageName())
+                .thenReturn(SETTINGS_PACKAGE_NAME);
         when(mDevice.getBondState()).thenReturn(BOND_BONDED);
         when(mDevice.isConnected()).thenReturn(true);
         when(mCachedDevice.getDevice()).thenReturn(mDevice);
@@ -178,6 +188,7 @@
         when(mCachedDevice.getBondState()).thenReturn(BOND_BONDED);
         when(mCachedDevice.getDeviceSide()).thenReturn(SIDE_LEFT);
         when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
+        when(mInputRoutingFactory.create(any())).thenReturn(mInputRoutingController);
 
         mContext.setMockPackageManager(mPackageManager);
     }
@@ -195,6 +206,7 @@
                 anyInt(), any());
         assertThat(intentCaptor.getValue().getAction()).isEqualTo(
                 Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS);
+        assertThat(intentCaptor.getValue().getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME);
         verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR,
                 TEST_LAUNCH_SOURCE_ID);
     }
@@ -210,6 +222,7 @@
                 anyInt(), any());
         assertThat(intentCaptor.getValue().getAction()).isEqualTo(
                 HearingDevicesDialogDelegate.ACTION_BLUETOOTH_DEVICE_DETAILS);
+        assertThat(intentCaptor.getValue().getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME);
         verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK,
                 TEST_LAUNCH_SOURCE_ID);
     }
@@ -341,6 +354,7 @@
 
         setUpDeviceDialogWithoutPairNewDeviceButton();
         mDialog.show();
+        mExecutor.runAllReady();
 
         ViewGroup ambientLayout = getAmbientLayout(mDialog);
         assertThat(ambientLayout.getVisibility()).isEqualTo(View.VISIBLE);
@@ -392,7 +406,9 @@
                 mExecutor,
                 mExecutor,
                 mAudioManager,
-                mUiEventLogger
+                mUiEventLogger,
+                mQSSettingsPackageRepository,
+                mInputRoutingFactory
         );
         mDialog = mDialogDelegate.createDialog();
     }
@@ -429,7 +445,6 @@
         return dialog.requireViewById(R.id.ambient_layout);
     }
 
-
     private int countChildWithoutSpace(ViewGroup viewGroup) {
         int spaceCount = 0;
         for (int i = 0; i < viewGroup.getChildCount(); i++) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt
new file mode 100644
index 0000000..0a41b77
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingControllerTest.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility.hearingaid
+
+import android.media.AudioDeviceInfo
+import android.media.AudioManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.HapClientProfile
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesInputRoutingController.InputRoutingControlAvailableCallback
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesInputRoutingControllerTest : SysuiTestCase() {
+
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private var hapClientProfile: HapClientProfile = mock()
+    private var cachedDevice: CachedBluetoothDevice = mock()
+    private var memberCachedDevice: CachedBluetoothDevice = mock()
+    private var btDevice: android.bluetooth.BluetoothDevice = mock()
+    private var audioManager: AudioManager = mock()
+    private lateinit var underTest: HearingDevicesInputRoutingController
+    private val testDispatcher = kosmos.testDispatcher
+
+    @Before
+    fun setUp() {
+        hapClientProfile.stub { on { isProfileReady } doReturn true }
+        cachedDevice.stub {
+            on { device } doReturn btDevice
+            on { profiles } doReturn listOf(hapClientProfile)
+        }
+        memberCachedDevice.stub {
+            on { device } doReturn btDevice
+            on { profiles } doReturn listOf(hapClientProfile)
+        }
+
+        underTest = HearingDevicesInputRoutingController(mContext, audioManager, testDispatcher)
+        underTest.setDevice(cachedDevice)
+    }
+
+    @Test
+    fun isInputRoutingControlAvailable_validInput_supportHapProfile_returnTrue() {
+        testScope.runTest {
+            val mockInfoAddress = arrayOf(mockTestAddressInfo(TEST_ADDRESS))
+            cachedDevice.stub {
+                on { address } doReturn TEST_ADDRESS
+                on { profiles } doReturn listOf(hapClientProfile)
+            }
+            audioManager.stub {
+                on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress
+            }
+
+            var result: Boolean? = null
+            underTest.isInputRoutingControlAvailable(
+                object : InputRoutingControlAvailableCallback {
+                    override fun onResult(available: Boolean) {
+                        result = available
+                    }
+                }
+            )
+
+            runCurrent()
+            assertThat(result).isTrue()
+        }
+    }
+
+    @Test
+    fun isInputRoutingControlAvailable_notSupportHapProfile_returnFalse() {
+        testScope.runTest {
+            val mockInfoAddress = arrayOf(mockTestAddressInfo(TEST_ADDRESS))
+            cachedDevice.stub {
+                on { address } doReturn TEST_ADDRESS
+                on { profiles } doReturn emptyList()
+            }
+            audioManager.stub {
+                on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress
+            }
+
+            var result: Boolean? = null
+            underTest.isInputRoutingControlAvailable(
+                object : InputRoutingControlAvailableCallback {
+                    override fun onResult(available: Boolean) {
+                        result = available
+                    }
+                }
+            )
+
+            runCurrent()
+            assertThat(result).isFalse()
+        }
+    }
+
+    @Test
+    fun isInputRoutingControlAvailable_validInputMember_supportHapProfile_returnTrue() {
+        testScope.runTest {
+            val mockInfoAddress2 = arrayOf(mockTestAddressInfo(TEST_ADDRESS_2))
+            cachedDevice.stub {
+                on { address } doReturn TEST_ADDRESS
+                on { profiles } doReturn listOf(hapClientProfile)
+                on { memberDevice } doReturn (setOf(memberCachedDevice))
+            }
+            memberCachedDevice.stub { on { address } doReturn TEST_ADDRESS_2 }
+            audioManager.stub {
+                on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn mockInfoAddress2
+            }
+
+            var result: Boolean? = null
+            underTest.isInputRoutingControlAvailable(
+                object : InputRoutingControlAvailableCallback {
+                    override fun onResult(available: Boolean) {
+                        result = available
+                    }
+                }
+            )
+
+            runCurrent()
+            assertThat(result).isTrue()
+        }
+    }
+
+    @Test
+    fun isAvailable_notValidInputDevice_returnFalse() {
+        testScope.runTest {
+            cachedDevice.stub {
+                on { address } doReturn TEST_ADDRESS
+                on { profiles } doReturn listOf(hapClientProfile)
+            }
+            audioManager.stub {
+                on { getDevices(AudioManager.GET_DEVICES_INPUTS) } doReturn emptyArray()
+            }
+
+            var result: Boolean? = null
+            underTest.isInputRoutingControlAvailable(
+                object : InputRoutingControlAvailableCallback {
+                    override fun onResult(available: Boolean) {
+                        result = available
+                    }
+                }
+            )
+
+            runCurrent()
+            assertThat(result).isFalse()
+        }
+    }
+
+    @Test
+    fun selectInputRouting_builtinMic_setMicrophonePreferredForCallsFalse() {
+        underTest.selectInputRouting(
+            HearingDevicesInputRoutingController.InputRoutingValue.BUILTIN_MIC.ordinal
+        )
+
+        verify(btDevice).isMicrophonePreferredForCalls = false
+    }
+
+    private fun mockTestAddressInfo(address: String): AudioDeviceInfo {
+        val info: AudioDeviceInfo = mock()
+        info.stub {
+            on { type } doReturn AudioDeviceInfo.TYPE_BLE_HEADSET
+            on { this.address } doReturn address
+        }
+
+        return info
+    }
+
+    companion object {
+        private const val TEST_ADDRESS = "55:66:77:88:99:AA"
+        private const val TEST_ADDRESS_2 = "55:66:77:88:99:BB"
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
index 8d3640d..53b364c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
@@ -41,21 +41,9 @@
     @Test
     fun testStyleValueUnchange_getBlankStr() {
         val fontVariationUtils = FontVariationUtils()
-        fontVariationUtils.updateFontVariation(
-            weight = 100,
-            width = 100,
-            opticalSize = 0,
-            roundness = 100,
-        )
-        val updatedFvar1 =
-            fontVariationUtils.updateFontVariation(
-                weight = 100,
-                width = 100,
-                opticalSize = 0,
-                roundness = 100,
-            )
-        Assert.assertEquals("", updatedFvar1)
-        val updatedFvar2 = fontVariationUtils.updateFontVariation()
-        Assert.assertEquals("", updatedFvar2)
+        Assert.assertEquals("", fontVariationUtils.updateFontVariation())
+        val fVar = fontVariationUtils.updateFontVariation(weight = 100)
+        Assert.assertEquals(fVar, fontVariationUtils.updateFontVariation())
+        Assert.assertEquals(fVar, fontVariationUtils.updateFontVariation(weight = 100))
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 8c5fad3..8573312 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -50,6 +49,7 @@
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -74,7 +74,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class BackActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val executor = FakeExecutor(FakeSystemClock())
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index 5249bbe..e8c30ba 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -58,7 +58,6 @@
 import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.firstValue
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -116,12 +115,7 @@
             runCurrent()
 
             verify(kosmos.windowManager).addView(any(), any())
-
             verify(kosmos.windowManager).addView(viewCaptor.capture(), any())
-            verify(viewCaptor.firstValue)
-                .announceForAccessibility(
-                    mContext.getText(R.string.accessibility_side_fingerprint_indicator_label)
-                )
 
             updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = false)
             runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
index 25a287c..15a6de8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinInputViewModelTest.kt
@@ -247,20 +247,20 @@
 }
 
 private class PinInputSubject
-private constructor(metadata: FailureMetadata, private val actual: PinInputViewModel) :
+private constructor(metadata: FailureMetadata, private val actual: PinInputViewModel?) :
     Subject(metadata, actual) {
 
     fun matches(mnemonics: String) {
         val actualMnemonics =
-            actual.input
-                .map { entry ->
+            actual?.input
+                ?.map { entry ->
                     when (entry) {
                         is Digit -> entry.input.digitToChar()
                         is ClearAll -> 'C'
                         else -> throw IllegalArgumentException()
                     }
                 }
-                .joinToString(separator = "")
+                ?.joinToString(separator = "")
 
         if (mnemonics != actualMnemonics) {
             failWithActual(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
index 648d74d..29a0b69 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraAutoRotateRepositoryImplTest.kt
@@ -22,8 +22,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
@@ -34,7 +34,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CameraAutoRotateRepositoryImplTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val settings = kosmos.fakeSettings
     private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
index b73a212..2e357d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepositoryImplTest.kt
@@ -22,8 +22,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
@@ -38,7 +38,7 @@
 @RunWith(AndroidJUnit4::class)
 @android.platform.test.annotations.EnabledOnRavenwood
 class CameraSensorPrivacyRepositoryImplTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val testUser = UserHandle.of(1)
     private val privacyManager = mock<SensorPrivacyManager>()
@@ -46,7 +46,7 @@
         CameraSensorPrivacyRepositoryImpl(
             testScope.testScheduler,
             testScope.backgroundScope,
-            privacyManager
+            privacyManager,
         )
 
     @Test
@@ -87,7 +87,7 @@
                 .addSensorPrivacyListener(
                     ArgumentMatchers.eq(SensorPrivacyManager.Sensors.CAMERA),
                     ArgumentMatchers.eq(testUser.identifier),
-                    captor.capture()
+                    captor.capture(),
                 )
             val sensorPrivacyCallback = captor.value!!
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
index 6c8097e..b3d8983 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraAutoRotateRepositoryTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -32,7 +32,7 @@
 @RunWith(AndroidJUnit4::class)
 @android.platform.test.annotations.EnabledOnRavenwood
 class FakeCameraAutoRotateRepositoryTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val underTest = kosmos.fakeCameraAutoRotateRepository
     private val testUser = UserHandle.of(1)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
index 7161c2c..6b9a7de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/data/repository/FakeCameraSensorPrivacyRepositoryTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -32,7 +32,7 @@
 @RunWith(AndroidJUnit4::class)
 @android.platform.test.annotations.EnabledOnRavenwood
 class FakeCameraSensorPrivacyRepositoryTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val underTest = kosmos.fakeCameraSensorPrivacyRepository
     private val testUser = UserHandle.of(1)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
index 239e026..652a2ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
@@ -25,6 +25,10 @@
 import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Test
@@ -33,7 +37,11 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ActionIntentCreatorTest : SysuiTestCase() {
-    val creator = ActionIntentCreator()
+    private val scheduler = TestCoroutineScheduler()
+    private val mainDispatcher = UnconfinedTestDispatcher(scheduler)
+    private val testScope = TestScope(mainDispatcher)
+
+    val creator = ActionIntentCreator(testScope.backgroundScope)
 
     @Test
     fun test_getTextEditorIntent() {
@@ -65,7 +73,7 @@
     }
 
     @Test
-    fun test_getImageEditIntent() {
+    fun test_getImageEditIntent() = runTest {
         context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "")
         val fakeUri = Uri.parse("content://foo")
         var intent = creator.getImageEditIntent(fakeUri, context)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
index 126b3fa..10c5c52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
@@ -34,6 +34,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.atomic.AtomicReference;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DefaultIntentCreatorTest extends SysuiTestCase {
@@ -73,12 +75,16 @@
     }
 
     @Test
-    public void test_getImageEditIntent() {
+    public void test_getImageEditIntentAsync() {
         getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
                 "");
         Uri fakeUri = Uri.parse("content://foo");
-        Intent intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
+        final AtomicReference<Intent> intentHolder = new AtomicReference<>(null);
+        mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> {
+            intentHolder.set(output);
+        });
 
+        Intent intent = intentHolder.get();
         assertEquals(Intent.ACTION_EDIT, intent.getAction());
         assertEquals("image/*", intent.getType());
         assertEquals(null, intent.getComponent());
@@ -90,8 +96,10 @@
                 "com.android.remotecopy.RemoteCopyActivity");
         getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
                 fakeComponent.flattenToString());
-        intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
-        assertEquals(fakeComponent, intent.getComponent());
+        mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> {
+            intentHolder.set(output);
+        });
+        assertEquals(fakeComponent, intentHolder.get().getComponent());
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/TouchHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/TouchHandlingViewInteractionHandlerTest.kt
index 0f40089..56b06de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/TouchHandlingViewInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/TouchHandlingViewInteractionHandlerTest.kt
@@ -17,14 +17,12 @@
 
 package com.android.systemui.common.ui.view
 
+import android.testing.TestableLooper
+import android.view.MotionEvent
 import android.view.ViewConfiguration
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel
-import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel.Down
-import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel.Move
-import com.android.systemui.common.ui.view.TouchHandlingViewInteractionHandler.MotionEventModel.Up
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -33,18 +31,22 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito.never
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 class TouchHandlingViewInteractionHandlerTest : SysuiTestCase() {
 
     @Mock private lateinit var postDelayed: (Runnable, Long) -> DisposableHandle
     @Mock private lateinit var onLongPressDetected: (Int, Int) -> Unit
     @Mock private lateinit var onSingleTapDetected: (Int, Int) -> Unit
+    @Mock private lateinit var onDoubleTapDetected: () -> Unit
 
     private lateinit var underTest: TouchHandlingViewInteractionHandler
 
@@ -61,14 +63,17 @@
 
         underTest =
             TouchHandlingViewInteractionHandler(
+                context = context,
                 postDelayed = postDelayed,
                 isAttachedToWindow = { isAttachedToWindow },
                 onLongPressDetected = onLongPressDetected,
                 onSingleTapDetected = onSingleTapDetected,
+                onDoubleTapDetected = onDoubleTapDetected,
                 longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() },
                 allowedTouchSlop = ViewConfiguration.getTouchSlop(),
             )
         underTest.isLongPressHandlingEnabled = true
+        underTest.isDoubleTapHandlingEnabled = true
     }
 
     @Test
@@ -76,63 +81,250 @@
         val downX = 123
         val downY = 456
         dispatchTouchEvents(
-            Down(x = downX, y = downY),
-            Move(distanceMoved = ViewConfiguration.getTouchSlop() - 0.1f),
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0),
+            MotionEvent.obtain(
+                0L,
+                0L,
+                MotionEvent.ACTION_MOVE,
+                123f + ViewConfiguration.getTouchSlop() - 0.1f,
+                456f,
+                0,
+            ),
         )
         delayedRunnable?.run()
 
         verify(onLongPressDetected).invoke(downX, downY)
-        verify(onSingleTapDetected, never()).invoke(any(), any())
+        verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt())
     }
 
     @Test
     fun longPressButFeatureNotEnabled() = runTest {
         underTest.isLongPressHandlingEnabled = false
-        dispatchTouchEvents(Down(x = 123, y = 456))
+        dispatchTouchEvents(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0))
 
         assertThat(delayedRunnable).isNull()
-        verify(onLongPressDetected, never()).invoke(any(), any())
-        verify(onSingleTapDetected, never()).invoke(any(), any())
+        verify(onLongPressDetected, never()).invoke(anyInt(), anyInt())
+        verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt())
     }
 
     @Test
     fun longPressButViewNotAttached() = runTest {
         isAttachedToWindow = false
-        dispatchTouchEvents(Down(x = 123, y = 456))
+        dispatchTouchEvents(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0))
         delayedRunnable?.run()
 
-        verify(onLongPressDetected, never()).invoke(any(), any())
-        verify(onSingleTapDetected, never()).invoke(any(), any())
+        verify(onLongPressDetected, never()).invoke(anyInt(), anyInt())
+        verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt())
     }
 
     @Test
     fun draggedTooFarToBeConsideredAlongPress() = runTest {
         dispatchTouchEvents(
-            Down(x = 123, y = 456),
-            Move(distanceMoved = ViewConfiguration.getTouchSlop() + 0.1f),
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123F, 456F, 0),
+            // Drag action within touch slop
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 123f, 456f, 0).apply {
+                addBatch(0L, 123f + ViewConfiguration.getTouchSlop() + 0.1f, 456f, 0f, 0f, 0)
+            },
         )
 
         assertThat(delayedRunnable).isNull()
-        verify(onLongPressDetected, never()).invoke(any(), any())
-        verify(onSingleTapDetected, never()).invoke(any(), any())
+        verify(onLongPressDetected, never()).invoke(anyInt(), anyInt())
+        verify(onSingleTapDetected, never()).invoke(anyInt(), anyInt())
     }
 
     @Test
     fun heldDownTooBrieflyToBeConsideredAlongPress() = runTest {
         dispatchTouchEvents(
-            Down(x = 123, y = 456),
-            Up(
-                distanceMoved = ViewConfiguration.getTouchSlop().toFloat(),
-                gestureDuration = ViewConfiguration.getLongPressTimeout() - 1L,
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0),
+            MotionEvent.obtain(
+                0L,
+                ViewConfiguration.getLongPressTimeout() - 1L,
+                MotionEvent.ACTION_UP,
+                123f,
+                456F,
+                0,
             ),
         )
 
         assertThat(delayedRunnable).isNull()
-        verify(onLongPressDetected, never()).invoke(any(), any())
+        verify(onLongPressDetected, never()).invoke(anyInt(), anyInt())
         verify(onSingleTapDetected).invoke(123, 456)
     }
 
-    private fun dispatchTouchEvents(vararg models: MotionEventModel) {
-        models.forEach { model -> underTest.onTouchEvent(model) }
+    @Test
+    fun doubleTap() = runTest {
+        val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L
+        dispatchTouchEvents(
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0),
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0),
+            MotionEvent.obtain(
+                secondTapTime,
+                secondTapTime,
+                MotionEvent.ACTION_DOWN,
+                123f,
+                456f,
+                0,
+            ),
+            MotionEvent.obtain(secondTapTime, secondTapTime, MotionEvent.ACTION_UP, 123f, 456f, 0),
+        )
+
+        verify(onDoubleTapDetected).invoke()
+        assertThat(delayedRunnable).isNull()
+        verify(onLongPressDetected, never()).invoke(anyInt(), anyInt())
+        verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt())
+    }
+
+    @Test
+    fun doubleTapButFeatureNotEnabled() = runTest {
+        underTest.isDoubleTapHandlingEnabled = false
+
+        val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L
+        dispatchTouchEvents(
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0),
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0),
+            MotionEvent.obtain(
+                secondTapTime,
+                secondTapTime,
+                MotionEvent.ACTION_DOWN,
+                123f,
+                456f,
+                0,
+            ),
+            MotionEvent.obtain(secondTapTime, secondTapTime, MotionEvent.ACTION_UP, 123f, 456f, 0),
+        )
+
+        verify(onDoubleTapDetected, never()).invoke()
+        assertThat(delayedRunnable).isNull()
+        verify(onLongPressDetected, never()).invoke(anyInt(), anyInt())
+        verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt())
+    }
+
+    @Test
+    fun tapIntoLongPress() = runTest {
+        val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L
+        dispatchTouchEvents(
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0),
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0),
+            MotionEvent.obtain(
+                secondTapTime,
+                secondTapTime,
+                MotionEvent.ACTION_DOWN,
+                123f,
+                456f,
+                0,
+            ),
+            MotionEvent.obtain(
+                secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L,
+                secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L,
+                MotionEvent.ACTION_MOVE,
+                123f + ViewConfiguration.getTouchSlop() - 0.1f,
+                456f,
+                0,
+            ),
+        )
+        delayedRunnable?.run()
+
+        verify(onDoubleTapDetected, never()).invoke()
+        verify(onSingleTapDetected).invoke(anyInt(), anyInt())
+        verify(onLongPressDetected).invoke(anyInt(), anyInt())
+    }
+
+    @Test
+    fun tapIntoDownHoldTooBrieflyToBeConsideredLongPress() = runTest {
+        val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L
+        dispatchTouchEvents(
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0),
+            MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 123f, 456f, 0),
+            MotionEvent.obtain(
+                secondTapTime,
+                secondTapTime,
+                MotionEvent.ACTION_DOWN,
+                123f,
+                456f,
+                0,
+            ),
+            MotionEvent.obtain(
+                secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L,
+                secondTapTime + ViewConfiguration.getLongPressTimeout() + 1L,
+                MotionEvent.ACTION_UP,
+                123f,
+                456f,
+                0,
+            ),
+        )
+        delayedRunnable?.run()
+
+        verify(onDoubleTapDetected, never()).invoke()
+        verify(onLongPressDetected, never()).invoke(anyInt(), anyInt())
+        verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt())
+    }
+
+    @Test
+    fun tapIntoDrag() = runTest {
+        val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L
+        dispatchTouchEvents(
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0),
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0),
+            MotionEvent.obtain(
+                secondTapTime,
+                secondTapTime,
+                MotionEvent.ACTION_DOWN,
+                123f,
+                456f,
+                0,
+            ),
+            // Drag event within touch slop
+            MotionEvent.obtain(secondTapTime, secondTapTime, MotionEvent.ACTION_MOVE, 123f, 456f, 0)
+                .apply {
+                    addBatch(
+                        secondTapTime,
+                        123f + ViewConfiguration.getTouchSlop() + 0.1f,
+                        456f,
+                        0f,
+                        0f,
+                        0,
+                    )
+                },
+        )
+        delayedRunnable?.run()
+
+        verify(onDoubleTapDetected, never()).invoke()
+        verify(onLongPressDetected, never()).invoke(anyInt(), anyInt())
+        verify(onSingleTapDetected).invoke(anyInt(), anyInt())
+    }
+
+    @Test
+    fun doubleTapOutOfAllowableSlop() = runTest {
+        val secondTapTime = ViewConfiguration.getDoubleTapTimeout() - 1L
+        val scaledDoubleTapSlop = ViewConfiguration.get(context).scaledDoubleTapSlop
+        dispatchTouchEvents(
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 123f, 456f, 0),
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 123f, 456f, 0),
+            MotionEvent.obtain(
+                secondTapTime,
+                secondTapTime,
+                MotionEvent.ACTION_DOWN,
+                123f + scaledDoubleTapSlop + 0.1f,
+                456f + scaledDoubleTapSlop + 0.1f,
+                0,
+            ),
+            MotionEvent.obtain(
+                secondTapTime,
+                secondTapTime,
+                MotionEvent.ACTION_UP,
+                123f + scaledDoubleTapSlop + 0.1f,
+                456f + scaledDoubleTapSlop + 0.1f,
+                0,
+            ),
+        )
+
+        verify(onDoubleTapDetected, never()).invoke()
+        assertThat(delayedRunnable).isNull()
+        verify(onLongPressDetected, never()).invoke(anyInt(), anyInt())
+        verify(onSingleTapDetected, times(2)).invoke(anyInt(), anyInt())
+    }
+
+    private fun dispatchTouchEvents(vararg events: MotionEvent) {
+        events.forEach { event -> underTest.onTouchEvent(event) }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalOngoingContentStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalOngoingContentStartableTest.kt
index ed73d89..6a25069 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalOngoingContentStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalOngoingContentStartableTest.kt
@@ -73,12 +73,12 @@
             assertThat(fakeCommunalMediaRepository.isListening()).isFalse()
             assertThat(fakeCommunalSmartspaceRepository.isListening()).isFalse()
 
-            kosmos.setCommunalEnabled(true)
+            setCommunalEnabled(true)
 
             assertThat(fakeCommunalMediaRepository.isListening()).isTrue()
             assertThat(fakeCommunalSmartspaceRepository.isListening()).isTrue()
 
-            kosmos.setCommunalEnabled(false)
+            setCommunalEnabled(false)
 
             assertThat(fakeCommunalMediaRepository.isListening()).isFalse()
             assertThat(fakeCommunalSmartspaceRepository.isListening()).isFalse()
@@ -93,13 +93,13 @@
             assertThat(fakeCommunalMediaRepository.isListening()).isFalse()
             assertThat(fakeCommunalSmartspaceRepository.isListening()).isFalse()
 
-            kosmos.setCommunalEnabled(true)
+            setCommunalEnabled(true)
 
             // Media listening does not start when UMO is disabled.
             assertThat(fakeCommunalMediaRepository.isListening()).isFalse()
             assertThat(fakeCommunalSmartspaceRepository.isListening()).isTrue()
 
-            kosmos.setCommunalEnabled(false)
+            setCommunalEnabled(false)
 
             assertThat(fakeCommunalMediaRepository.isListening()).isFalse()
             assertThat(fakeCommunalSmartspaceRepository.isListening()).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt
index 0c97750..6710575 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt
@@ -20,6 +20,7 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.condition.testStart
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP
 import com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE
@@ -55,6 +56,7 @@
         Kosmos.Fixture {
             DeviceInactiveCondition(
                 applicationCoroutineScope,
+                applicationCoroutineScope,
                 keyguardStateController,
                 wakefulnessLifecycle,
                 keyguardUpdateMonitor,
@@ -67,7 +69,7 @@
     fun asleep_conditionTrue() =
         kosmos.runTest {
             // Condition is false to start.
-            underTest.start()
+            testStart(underTest)
             assertThat(underTest.isConditionMet).isFalse()
 
             // Condition is true when device goes to sleep.
@@ -79,7 +81,7 @@
     fun dozingAndAsleep_conditionFalse() =
         kosmos.runTest {
             // Condition is true when device is asleep.
-            underTest.start()
+            testStart(underTest)
             sleep()
             assertThat(underTest.isConditionMet).isTrue()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CarProjectionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CarProjectionRepositoryImplTest.kt
new file mode 100644
index 0000000..f9b29e9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CarProjectionRepositoryImplTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import android.app.UiModeManager
+import android.app.UiModeManager.OnProjectionStateChangedListener
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.launchIn
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CarProjectionRepositoryImplTest : SysuiTestCase() {
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+    private val capturedListeners = mutableListOf<OnProjectionStateChangedListener>()
+
+    private val Kosmos.uiModeManager by
+        Kosmos.Fixture<UiModeManager> {
+            mock {
+                on {
+                    addOnProjectionStateChangedListener(
+                        eq(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE),
+                        any(),
+                        any(),
+                    )
+                } doAnswer
+                    {
+                        val listener = it.getArgument<OnProjectionStateChangedListener>(2)
+                        capturedListeners.add(listener)
+                        Unit
+                    }
+
+                on { removeOnProjectionStateChangedListener(any()) } doAnswer
+                    {
+                        val listener = it.getArgument<OnProjectionStateChangedListener>(0)
+                        capturedListeners.remove(listener)
+                        Unit
+                    }
+
+                on { activeProjectionTypes } doReturn UiModeManager.PROJECTION_TYPE_NONE
+            }
+        }
+
+    private val Kosmos.underTest by
+        Kosmos.Fixture {
+            CarProjectionRepositoryImpl(
+                uiModeManager = uiModeManager,
+                bgDispatcher = testDispatcher,
+            )
+        }
+
+    @Test
+    fun testProjectionActiveUpdatesAfterCallback() =
+        kosmos.runTest {
+            val projectionActive by collectLastValue(underTest.projectionActive)
+            assertThat(projectionActive).isFalse()
+
+            setActiveProjectionType(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE)
+            assertThat(projectionActive).isTrue()
+
+            setActiveProjectionType(UiModeManager.PROJECTION_TYPE_NONE)
+            assertThat(projectionActive).isFalse()
+        }
+
+    @Test
+    fun testProjectionInitialValueTrue() =
+        kosmos.runTest {
+            setActiveProjectionType(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE)
+
+            val projectionActive by collectLastValue(underTest.projectionActive)
+            assertThat(projectionActive).isTrue()
+        }
+
+    @Test
+    fun testUnsubscribeWhenCancelled() =
+        kosmos.runTest {
+            val job = underTest.projectionActive.launchIn(backgroundScope)
+            assertThat(capturedListeners).hasSize(1)
+
+            job.cancel()
+            assertThat(capturedListeners).isEmpty()
+        }
+
+    private fun Kosmos.setActiveProjectionType(@UiModeManager.ProjectionType projectionType: Int) {
+        uiModeManager.stub { on { activeProjectionTypes } doReturn projectionType }
+        capturedListeners.forEach { it.onProjectionStateChanged(projectionType, emptySet()) }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index 5c98365..09d44a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -34,9 +34,7 @@
 import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.communal.data.model.DisabledReason
 import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_BACKGROUND_SETTING
-import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.WhenToDream
 import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
@@ -202,63 +200,6 @@
 
     @EnableFlags(FLAG_COMMUNAL_HUB)
     @Test
-    fun secondaryUserIsInvalid() =
-        kosmos.runTest {
-            val enabledState by collectLastValue(underTest.getEnabledState(SECONDARY_USER))
-
-            assertThat(enabledState?.enabled).isFalse()
-            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_INVALID_USER)
-        }
-
-    @EnableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
-    @Test
-    fun classicFlagIsDisabled() =
-        kosmos.runTest {
-            setCommunalV2Enabled(false)
-            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
-            assertThat(enabledState?.enabled).isFalse()
-            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
-        }
-
-    @DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
-    @Test
-    fun communalHubFlagIsDisabled() =
-        kosmos.runTest {
-            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
-            assertThat(enabledState?.enabled).isFalse()
-            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
-        }
-
-    @EnableFlags(FLAG_COMMUNAL_HUB)
-    @Test
-    fun hubIsDisabledByUser() =
-        kosmos.runTest {
-            fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
-            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
-            assertThat(enabledState?.enabled).isFalse()
-            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_USER_SETTING)
-
-            fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, SECONDARY_USER.id)
-            assertThat(enabledState?.enabled).isFalse()
-
-            fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, PRIMARY_USER.id)
-            assertThat(enabledState?.enabled).isTrue()
-        }
-
-    @EnableFlags(FLAG_COMMUNAL_HUB)
-    @Test
-    fun hubIsDisabledByDevicePolicy() =
-        kosmos.runTest {
-            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
-            assertThat(enabledState?.enabled).isTrue()
-
-            setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
-            assertThat(enabledState?.enabled).isFalse()
-            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_DEVICE_POLICY)
-        }
-
-    @EnableFlags(FLAG_COMMUNAL_HUB)
-    @Test
     fun widgetsAllowedForWorkProfile_isFalse_whenDisallowedByDevicePolicy() =
         kosmos.runTest {
             val widgetsAllowedForWorkProfile by
@@ -269,36 +210,6 @@
             assertThat(widgetsAllowedForWorkProfile).isFalse()
         }
 
-    @EnableFlags(FLAG_COMMUNAL_HUB)
-    @Test
-    fun hubIsEnabled_whenDisallowedByDevicePolicyForWorkProfile() =
-        kosmos.runTest {
-            val enabledStateForPrimaryUser by
-                collectLastValue(underTest.getEnabledState(PRIMARY_USER))
-            assertThat(enabledStateForPrimaryUser?.enabled).isTrue()
-
-            setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_WIDGETS_ALL)
-            assertThat(enabledStateForPrimaryUser?.enabled).isTrue()
-        }
-
-    @EnableFlags(FLAG_COMMUNAL_HUB)
-    @Test
-    fun hubIsDisabledByUserAndDevicePolicy() =
-        kosmos.runTest {
-            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
-            assertThat(enabledState?.enabled).isTrue()
-
-            fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
-            setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
-
-            assertThat(enabledState?.enabled).isFalse()
-            assertThat(enabledState)
-                .containsExactly(
-                    DisabledReason.DISABLED_REASON_DEVICE_POLICY,
-                    DisabledReason.DISABLED_REASON_USER_SETTING,
-                )
-        }
-
     @Test
     @DisableFlags(FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
     fun backgroundType_defaultValue() =
@@ -327,26 +238,6 @@
         }
 
     @Test
-    fun screensaverDisabledByUser() =
-        kosmos.runTest {
-            val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
-
-            fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, 0, PRIMARY_USER.id)
-
-            assertThat(enabledState).isFalse()
-        }
-
-    @Test
-    fun screensaverEnabledByUser() =
-        kosmos.runTest {
-            val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
-
-            fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, 1, PRIMARY_USER.id)
-
-            assertThat(enabledState).isTrue()
-        }
-
-    @Test
     fun whenToDream_charging() =
         kosmos.runTest {
             val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CarProjectionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CarProjectionInteractorTest.kt
new file mode 100644
index 0000000..fc4cd43
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CarProjectionInteractorTest.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.carProjectionRepository
+import com.android.systemui.communal.data.repository.fake
+import com.android.systemui.kosmos.Kosmos
+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 CarProjectionInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+    private val Kosmos.underTest by Kosmos.Fixture { carProjectionInteractor }
+
+    @Test
+    fun testProjectionActive() =
+        kosmos.runTest {
+            val projectionActive by collectLastValue(underTest.projectionActive)
+            assertThat(projectionActive).isFalse()
+
+            carProjectionRepository.fake.setProjectionActive(true)
+            assertThat(projectionActive).isTrue()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
new file mode 100644
index 0000000..95334b5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.data.repository.batteryRepository
+import com.android.systemui.common.data.repository.fake
+import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN
+import com.android.systemui.communal.data.model.FEATURE_MANUAL_OPEN
+import com.android.systemui.communal.data.model.SuppressionReason
+import com.android.systemui.communal.posturing.data.repository.fake
+import com.android.systemui.communal.posturing.data.repository.posturingRepository
+import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.fakeDockManager
+import com.android.systemui.kosmos.Kosmos
+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.android.systemui.user.data.repository.FakeUserRepository.Companion.MAIN_USER_ID
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.settings.fakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalAutoOpenInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+    private val Kosmos.underTest by Kosmos.Fixture { communalAutoOpenInteractor }
+
+    @Before
+    fun setUp() {
+        runBlocking { kosmos.fakeUserRepository.asMainUser() }
+        with(kosmos.fakeSettings) {
+            putIntForUser(
+                Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+                Settings.Secure.GLANCEABLE_HUB_START_NEVER,
+                MAIN_USER_ID,
+            )
+        }
+    }
+
+    @Test
+    fun testStartWhileCharging() =
+        kosmos.runTest {
+            val shouldAutoOpen by collectLastValue(underTest.shouldAutoOpen)
+            val suppressionReason by collectLastValue(underTest.suppressionReason)
+
+            fakeSettings.putIntForUser(
+                Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+                Settings.Secure.GLANCEABLE_HUB_START_CHARGING,
+                MAIN_USER_ID,
+            )
+
+            batteryRepository.fake.setDevicePluggedIn(false)
+            assertThat(shouldAutoOpen).isFalse()
+            assertThat(suppressionReason)
+                .isEqualTo(
+                    SuppressionReason.ReasonWhenToAutoShow(FEATURE_AUTO_OPEN or FEATURE_MANUAL_OPEN)
+                )
+
+            batteryRepository.fake.setDevicePluggedIn(true)
+            assertThat(shouldAutoOpen).isTrue()
+            assertThat(suppressionReason).isNull()
+        }
+
+    @Test
+    fun testStartWhileDocked() =
+        kosmos.runTest {
+            val shouldAutoOpen by collectLastValue(underTest.shouldAutoOpen)
+            val suppressionReason by collectLastValue(underTest.suppressionReason)
+
+            fakeSettings.putIntForUser(
+                Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+                Settings.Secure.GLANCEABLE_HUB_START_DOCKED,
+                MAIN_USER_ID,
+            )
+
+            batteryRepository.fake.setDevicePluggedIn(true)
+            fakeDockManager.setIsDocked(false)
+
+            assertThat(shouldAutoOpen).isFalse()
+            assertThat(suppressionReason)
+                .isEqualTo(
+                    SuppressionReason.ReasonWhenToAutoShow(FEATURE_AUTO_OPEN or FEATURE_MANUAL_OPEN)
+                )
+
+            fakeDockManager.setIsDocked(true)
+            fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
+            assertThat(shouldAutoOpen).isTrue()
+            assertThat(suppressionReason).isNull()
+        }
+
+    @Test
+    fun testStartWhilePostured() =
+        kosmos.runTest {
+            val shouldAutoOpen by collectLastValue(underTest.shouldAutoOpen)
+            val suppressionReason by collectLastValue(underTest.suppressionReason)
+
+            fakeSettings.putIntForUser(
+                Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+                Settings.Secure.GLANCEABLE_HUB_START_CHARGING_UPRIGHT,
+                MAIN_USER_ID,
+            )
+
+            batteryRepository.fake.setDevicePluggedIn(true)
+            posturingRepository.fake.setPosturedState(PosturedState.NotPostured)
+
+            assertThat(shouldAutoOpen).isFalse()
+            assertThat(suppressionReason)
+                .isEqualTo(
+                    SuppressionReason.ReasonWhenToAutoShow(FEATURE_AUTO_OPEN or FEATURE_MANUAL_OPEN)
+                )
+
+            posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+            assertThat(shouldAutoOpen).isTrue()
+            assertThat(suppressionReason).isNull()
+        }
+
+    @Test
+    fun testStartNever() =
+        kosmos.runTest {
+            val shouldAutoOpen by collectLastValue(underTest.shouldAutoOpen)
+            val suppressionReason by collectLastValue(underTest.suppressionReason)
+
+            fakeSettings.putIntForUser(
+                Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+                Settings.Secure.GLANCEABLE_HUB_START_NEVER,
+                MAIN_USER_ID,
+            )
+
+            batteryRepository.fake.setDevicePluggedIn(true)
+            posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+            fakeDockManager.setIsDocked(true)
+
+            assertThat(shouldAutoOpen).isFalse()
+            assertThat(suppressionReason)
+                .isEqualTo(
+                    SuppressionReason.ReasonWhenToAutoShow(FEATURE_AUTO_OPEN or FEATURE_MANUAL_OPEN)
+                )
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
deleted file mode 100644
index beec184..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
+++ /dev/null
@@ -1,82 +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.domain.interactor
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
-import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
-import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
-import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * This class is a variation of the [CommunalInteractorTest] for cases where communal is disabled.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CommunalInteractorCommunalDisabledTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-
-    private lateinit var communalRepository: FakeCommunalSceneRepository
-    private lateinit var widgetRepository: FakeCommunalWidgetRepository
-    private lateinit var keyguardRepository: FakeKeyguardRepository
-
-    private lateinit var underTest: CommunalInteractor
-
-    @Before
-    fun setUp() {
-        communalRepository = kosmos.fakeCommunalSceneRepository
-        widgetRepository = kosmos.fakeCommunalWidgetRepository
-        keyguardRepository = kosmos.fakeKeyguardRepository
-
-        mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB)
-
-        underTest = kosmos.communalInteractor
-    }
-
-    @Test
-    fun isCommunalEnabled_false() =
-        testScope.runTest { assertThat(underTest.isCommunalEnabled.value).isFalse() }
-
-    @Test
-    fun isCommunalAvailable_whenStorageUnlock_false() =
-        testScope.runTest {
-            val isCommunalAvailable by collectLastValue(underTest.isCommunalAvailable)
-
-            assertThat(isCommunalAvailable).isFalse()
-
-            keyguardRepository.setIsEncryptedOrLockdown(false)
-            runCurrent()
-
-            assertThat(isCommunalAvailable).isFalse()
-        }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 8424746..b65ecf46 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -21,7 +21,6 @@
 import android.app.admin.devicePolicyManager
 import android.content.Intent
 import android.content.pm.UserInfo
-import android.content.res.mainResources
 import android.os.UserHandle
 import android.os.UserManager
 import android.os.userManager
@@ -39,9 +38,8 @@
 import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.common.data.repository.batteryRepository
-import com.android.systemui.common.data.repository.fake
 import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
+import com.android.systemui.communal.data.model.SuppressionReason
 import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
 import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
@@ -50,14 +48,9 @@
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
-import com.android.systemui.communal.posturing.data.repository.fake
-import com.android.systemui.communal.posturing.data.repository.posturingRepository
-import com.android.systemui.communal.posturing.shared.model.PosturedState
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.EditModeState
-import com.android.systemui.dock.DockManager
-import com.android.systemui.dock.fakeDockManager
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -75,19 +68,16 @@
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.statusbar.phone.fakeManagedProfileController
 import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.advanceTimeBy
-import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -98,10 +88,6 @@
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
 
-/**
- * This class of test cases assume that communal is enabled. For disabled cases, see
- * [CommunalInteractorCommunalDisabledTest].
- */
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
 class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@@ -109,10 +95,7 @@
         UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
     private val secondaryUser = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
 
-    private val kosmos =
-        testKosmos()
-            .apply { mainResources = mContext.orCreateTestableResources.resources }
-            .useUnconfinedTestDispatcher()
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
 
     private val Kosmos.underTest by Kosmos.Fixture { communalInteractor }
 
@@ -128,104 +111,40 @@
 
         kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
         mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
-
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault,
-            false,
-        )
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault,
-            false,
-        )
-        mContext.orCreateTestableResources.addOverride(
-            com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault,
-            false,
-        )
-    }
-
-    @After
-    fun tearDown() {
-        mContext.orCreateTestableResources.removeOverride(
-            com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault
-        )
-        mContext.orCreateTestableResources.removeOverride(
-            com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault
-        )
-        mContext.orCreateTestableResources.removeOverride(
-            com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault
-        )
     }
 
     @Test
     fun communalEnabled_true() =
         kosmos.runTest {
-            fakeUserRepository.setSelectedUserInfo(mainUser)
+            communalSettingsInteractor.setSuppressionReasons(emptyList())
             assertThat(underTest.isCommunalEnabled.value).isTrue()
         }
 
     @Test
-    fun isCommunalAvailable_mainUserUnlockedAndMainUser_true() =
-        kosmos.runTest {
-            val isAvailable by collectLastValue(underTest.isCommunalAvailable)
-            assertThat(isAvailable).isFalse()
-
-            fakeUserRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, true)
-            fakeUserRepository.setSelectedUserInfo(mainUser)
-            fakeKeyguardRepository.setKeyguardShowing(true)
-
-            assertThat(isAvailable).isTrue()
-        }
-
-    @Test
-    fun isCommunalAvailable_mainUserLockedAndMainUser_false() =
-        kosmos.runTest {
-            val isAvailable by collectLastValue(underTest.isCommunalAvailable)
-            assertThat(isAvailable).isFalse()
-
-            fakeUserRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, false)
-            fakeUserRepository.setSelectedUserInfo(mainUser)
-            fakeKeyguardRepository.setKeyguardShowing(true)
-
-            assertThat(isAvailable).isFalse()
-        }
-
-    @Test
-    fun isCommunalAvailable_mainUserUnlockedAndSecondaryUser_false() =
-        kosmos.runTest {
-            val isAvailable by collectLastValue(underTest.isCommunalAvailable)
-            assertThat(isAvailable).isFalse()
-
-            fakeUserRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, true)
-            fakeUserRepository.setSelectedUserInfo(secondaryUser)
-            fakeKeyguardRepository.setKeyguardShowing(true)
-
-            assertThat(isAvailable).isFalse()
-        }
-
-    @Test
     fun isCommunalAvailable_whenKeyguardShowing_true() =
         kosmos.runTest {
+            communalSettingsInteractor.setSuppressionReasons(emptyList())
+            fakeKeyguardRepository.setKeyguardShowing(false)
+
             val isAvailable by collectLastValue(underTest.isCommunalAvailable)
             assertThat(isAvailable).isFalse()
 
-            fakeUserRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, true)
-            fakeUserRepository.setSelectedUserInfo(mainUser)
             fakeKeyguardRepository.setKeyguardShowing(true)
-
             assertThat(isAvailable).isTrue()
         }
 
     @Test
-    fun isCommunalAvailable_communalDisabled_false() =
+    fun isCommunalAvailable_suppressed() =
         kosmos.runTest {
-            mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
+            communalSettingsInteractor.setSuppressionReasons(emptyList())
+            fakeKeyguardRepository.setKeyguardShowing(true)
 
             val isAvailable by collectLastValue(underTest.isCommunalAvailable)
-            assertThat(isAvailable).isFalse()
+            assertThat(isAvailable).isTrue()
 
-            fakeUserRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, false)
-            fakeUserRepository.setSelectedUserInfo(mainUser)
-            fakeKeyguardRepository.setKeyguardShowing(true)
+            communalSettingsInteractor.setSuppressionReasons(
+                listOf(SuppressionReason.ReasonUnknown())
+            )
 
             assertThat(isAvailable).isFalse()
         }
@@ -1280,66 +1199,6 @@
                 .inOrder()
         }
 
-    @Test
-    fun showCommunalWhileCharging() =
-        kosmos.runTest {
-            fakeUserRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, true)
-            fakeUserRepository.setSelectedUserInfo(mainUser)
-            fakeKeyguardRepository.setKeyguardShowing(true)
-            fakeSettings.putIntForUser(
-                Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
-                1,
-                mainUser.id,
-            )
-
-            val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
-            batteryRepository.fake.setDevicePluggedIn(false)
-            assertThat(shouldShowCommunal).isFalse()
-
-            batteryRepository.fake.setDevicePluggedIn(true)
-            assertThat(shouldShowCommunal).isTrue()
-        }
-
-    @Test
-    fun showCommunalWhilePosturedAndCharging() =
-        kosmos.runTest {
-            fakeUserRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, true)
-            fakeUserRepository.setSelectedUserInfo(mainUser)
-            fakeKeyguardRepository.setKeyguardShowing(true)
-            fakeSettings.putIntForUser(
-                Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
-                1,
-                mainUser.id,
-            )
-
-            val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
-            batteryRepository.fake.setDevicePluggedIn(true)
-            posturingRepository.fake.setPosturedState(PosturedState.NotPostured)
-            assertThat(shouldShowCommunal).isFalse()
-
-            posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
-            assertThat(shouldShowCommunal).isTrue()
-        }
-
-    @Test
-    fun showCommunalWhileDocked() =
-        kosmos.runTest {
-            fakeUserRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, true)
-            fakeUserRepository.setSelectedUserInfo(mainUser)
-            fakeKeyguardRepository.setKeyguardShowing(true)
-            fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, 1, mainUser.id)
-
-            batteryRepository.fake.setDevicePluggedIn(true)
-            fakeDockManager.setIsDocked(false)
-
-            val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
-            assertThat(shouldShowCommunal).isFalse()
-
-            fakeDockManager.setIsDocked(true)
-            fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
-            assertThat(shouldShowCommunal).isTrue()
-        }
-
     private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
         whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
             .thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
index 310bf64..d6f7145 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
@@ -21,10 +21,12 @@
 import android.content.Intent
 import android.content.pm.UserInfo
 import android.os.UserManager
+import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.communal.shared.model.WhenToStartHub
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
@@ -32,6 +34,7 @@
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.settings.fakeSettings
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertNull
@@ -82,6 +85,19 @@
             assertEquals(USER_INFO_WORK.id, disallowedUser!!.id)
         }
 
+    @Test
+    fun whenToStartHub_matchesRepository() =
+        kosmos.runTest {
+            fakeSettings.putIntForUser(
+                Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+                Settings.Secure.GLANCEABLE_HUB_START_CHARGING,
+                MAIN_USER_INFO.id,
+            )
+
+            val startCondition by collectLastValue(underTest.whenToStartHub)
+            assertEquals(startCondition, WhenToStartHub.WHILE_CHARGING)
+        }
+
     private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
         whenever(
                 kosmos.devicePolicyManager.getKeyguardDisabledFeatures(
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 f47aa6b..b8dbc9f 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
@@ -33,6 +33,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
+import com.android.systemui.communal.data.model.FEATURE_MANUAL_OPEN
+import com.android.systemui.communal.data.model.SuppressionReason
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
 import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
 import com.android.systemui.communal.data.repository.FakeCommunalSmartspaceRepository
@@ -48,6 +50,8 @@
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.communal.domain.interactor.communalTutorialInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalEnabled
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.log.CommunalMetricsLogger
 import com.android.systemui.communal.shared.model.CommunalContentSize
@@ -73,6 +77,8 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.transitions.blurConfig
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
@@ -122,7 +128,9 @@
 @RunWith(ParameterizedAndroidJunit4::class)
 class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     @Mock private lateinit var mediaHost: MediaHost
+
     @Mock private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
+
     @Mock private lateinit var metricsLogger: CommunalMetricsLogger
 
     private val kosmos = testKosmos()
@@ -206,11 +214,8 @@
     @Test
     fun tutorial_tutorialNotCompletedAndKeyguardVisible_showTutorialContent() =
         testScope.runTest {
-            // Keyguard showing, storage unlocked, main user, and tutorial not started.
             keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            userRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, true)
-            setIsMainUser(true)
+            kosmos.setCommunalEnabled(true)
             tutorialRepository.setTutorialSettingState(
                 Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
             )
@@ -940,6 +945,31 @@
             assertThat(isUiBlurred).isFalse()
         }
 
+    @Test
+    @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+    fun swipeToCommunal() =
+        kosmos.runTest {
+            setCommunalV2ConfigEnabled(true)
+            // Suppress manual opening
+            communalSettingsInteractor.setSuppressionReasons(
+                listOf(SuppressionReason.ReasonUnknown(FEATURE_MANUAL_OPEN))
+            )
+
+            val viewModel = createViewModel()
+            val swipeToHubEnabled by collectLastValue(viewModel.swipeToHubEnabled)
+            assertThat(swipeToHubEnabled).isFalse()
+
+            communalSettingsInteractor.setSuppressionReasons(emptyList())
+            assertThat(swipeToHubEnabled).isTrue()
+
+            keyguardTransitionRepository.sendTransitionStep(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                transitionState = TransitionState.STARTED,
+            )
+            assertThat(swipeToHubEnabled).isFalse()
+        }
+
     private suspend fun setIsMainUser(isMainUser: Boolean) {
         val user = if (isMainUser) MAIN_USER_INFO else SECONDARY_USER_INFO
         with(userRepository) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index c15f797..b08e676 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -16,15 +16,17 @@
 
 package com.android.systemui.communal.widgets
 
+import android.appwidget.AppWidgetProviderInfo
 import android.content.pm.UserInfo
-import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalEnabled
 import com.android.systemui.communal.shared.model.FakeGlanceableHubMultiUserHelper
 import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
 import com.android.systemui.coroutines.collectLastValue
@@ -37,11 +39,9 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository.Companion.MAIN_USER_ID
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.user.domain.interactor.userLockedInteractor
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.test.runCurrent
@@ -51,6 +51,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -67,6 +68,7 @@
 
     private lateinit var appWidgetIdToRemove: MutableSharedFlow<Int>
 
+    private lateinit var communalInteractorSpy: CommunalInteractor
     private lateinit var underTest: CommunalAppWidgetHostStartable
 
     @Before
@@ -80,12 +82,13 @@
         helper = kosmos.fakeGlanceableHubMultiUserHelper
         appWidgetIdToRemove = MutableSharedFlow()
         whenever(appWidgetHost.appWidgetIdToRemove).thenReturn(appWidgetIdToRemove)
+        communalInteractorSpy = spy(kosmos.communalInteractor)
 
         underTest =
             CommunalAppWidgetHostStartable(
                 { appWidgetHost },
                 { communalWidgetHost },
-                { kosmos.communalInteractor },
+                { communalInteractorSpy },
                 { kosmos.communalSettingsInteractor },
                 { kosmos.keyguardInteractor },
                 { kosmos.fakeUserTracker },
@@ -261,6 +264,41 @@
         }
 
     @Test
+    fun removeNotLockscreenWidgets_whenCommunalIsAvailable() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is available
+                setCommunalAvailable(true)
+                kosmos.fakeUserTracker.set(
+                    userInfos = listOf(MAIN_USER_INFO),
+                    selectedUserIndex = 0,
+                )
+                fakeCommunalWidgetRepository.addWidget(
+                    appWidgetId = 1,
+                    userId = MAIN_USER_INFO.id,
+                    category = AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD,
+                )
+                fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+                fakeCommunalWidgetRepository.addWidget(
+                    appWidgetId = 3,
+                    userId = MAIN_USER_INFO.id,
+                    category = AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD,
+                )
+
+                underTest.start()
+                runCurrent()
+
+                val communalWidgets by
+                    collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
+                assertThat(communalWidgets).hasSize(1)
+                assertThat(communalWidgets!![0].appWidgetId).isEqualTo(2)
+
+                verify(communalInteractorSpy).deleteWidget(1)
+                verify(communalInteractorSpy).deleteWidget(3)
+            }
+        }
+
+    @Test
     fun onStartHeadlessSystemUser_registerWidgetManager_whenCommunalIsAvailable() =
         with(kosmos) {
             testScope.runTest {
@@ -282,22 +320,12 @@
             }
         }
 
-    private suspend fun setCommunalAvailable(
-        available: Boolean,
-        setKeyguardShowing: Boolean = true,
-    ) =
+    private fun setCommunalAvailable(available: Boolean, setKeyguardShowing: Boolean = true) =
         with(kosmos) {
-            fakeUserRepository.setUserUnlocked(MAIN_USER_ID, true)
-            fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
+            setCommunalEnabled(available)
             if (setKeyguardShowing) {
                 fakeKeyguardRepository.setKeyguardShowing(true)
             }
-            val settingsValue = if (available) 1 else 0
-            fakeSettings.putIntForUser(
-                Settings.Secure.GLANCEABLE_HUB_ENABLED,
-                settingsValue,
-                MAIN_USER_INFO.id,
-            )
         }
 
     private companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
index e051500..454c156 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
@@ -56,6 +56,7 @@
 import com.android.systemui.statusbar.phone.screenOffAnimationController
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceTimeBy
@@ -105,7 +106,7 @@
     @Test
     fun nonPowerButtonFPS_vibrateSuccess() =
         testScope.runTest {
-            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
             enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
             runCurrent()
             enterDeviceFromFingerprintUnlockLegacy()
@@ -116,7 +117,7 @@
     @Test
     fun powerButtonFPS_vibrateSuccess() =
         testScope.runTest {
-            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
             enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
 
@@ -133,7 +134,7 @@
     @Test
     fun powerButtonFPS_powerDown_doNotVibrateSuccess() =
         testScope.runTest {
-            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
             enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             kosmos.fakeKeyEventRepository.setPowerButtonDown(true) // power button is currently DOWN
 
@@ -150,7 +151,7 @@
     @Test
     fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() =
         testScope.runTest {
-            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
             enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
 
@@ -174,14 +175,14 @@
         }
 
     @Test
-    fun nonPowerButtonFPS_coExFaceFailure_vibrateError() =
+    fun nonPowerButtonFPS_coExFaceFailure_doNotVibrateError() =
         testScope.runTest {
             val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
             enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
             enrollFace()
             runCurrent()
             faceFailure()
-            assertThat(playErrorHaptic).isNotNull()
+            assertThat(playErrorHaptic).isNull()
         }
 
     @Test
@@ -211,7 +212,7 @@
         testScope.runTest {
             kosmos.configureKeyguardBypass(isBypassAvailable = false)
             underTest = kosmos.deviceEntryHapticsInteractor
-            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
             enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
             runCurrent()
             configureDeviceEntryFromBiometricSource(isFpUnlock = true)
@@ -225,7 +226,7 @@
         testScope.runTest {
             kosmos.configureKeyguardBypass(isBypassAvailable = false)
             underTest = kosmos.deviceEntryHapticsInteractor
-            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
             enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
 
@@ -246,18 +247,19 @@
             enrollFace()
             kosmos.configureKeyguardBypass(isBypassAvailable = true)
             underTest = kosmos.deviceEntryHapticsInteractor
-            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
             configureDeviceEntryFromBiometricSource(isFaceUnlock = true)
             verifyDeviceEntryFromFaceAuth()
             assertThat(playSuccessHaptic).isNotNull()
         }
 
+    @OptIn(ExperimentalCoroutinesApi::class)
     @EnableSceneContainer
     @Test
-    fun playSuccessHaptic_onFaceAuthSuccess_whenBypassDisabled_sceneContainer() =
+    fun skipSuccessHaptic_onFaceAuthSuccess_whenBypassDisabled_sceneContainer() =
         testScope.runTest {
             underTest = kosmos.deviceEntryHapticsInteractor
-            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
 
             enrollFace()
             kosmos.configureKeyguardBypass(isBypassAvailable = false)
@@ -265,7 +267,7 @@
             configureDeviceEntryFromBiometricSource(isFaceUnlock = true, bypassEnabled = false)
             kosmos.fakeDeviceEntryFaceAuthRepository.isAuthenticated.value = true
 
-            assertThat(playSuccessHaptic).isNotNull()
+            assertThat(playSuccessHaptic).isNull()
         }
 
     @EnableSceneContainer
@@ -274,7 +276,7 @@
         testScope.runTest {
             kosmos.configureKeyguardBypass(isBypassAvailable = false)
             underTest = kosmos.deviceEntryHapticsInteractor
-            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
             enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             // power button is currently DOWN
             kosmos.fakeKeyEventRepository.setPowerButtonDown(true)
@@ -295,7 +297,7 @@
         testScope.runTest {
             kosmos.configureKeyguardBypass(isBypassAvailable = false)
             underTest = kosmos.deviceEntryHapticsInteractor
-            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHapticOnDeviceEntry)
             enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 197b0ee..8591375 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -26,6 +26,7 @@
 import android.view.IWindowManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.FlowValue
 import com.android.systemui.coroutines.collectLastValue
@@ -71,13 +72,19 @@
     // that the initial state (soon after construction) contains the expected ones set in every
     // test.
     private val displayRepository: DisplayRepositoryImpl by lazy {
-        DisplayRepositoryImpl(
+        // TODO b/401305290 - move this to kosmos
+        val displayRepositoryFromLib =
+            com.android.app.displaylib.DisplayRepositoryImpl(
                 displayManager,
+                testHandler,
+                testScope.backgroundScope,
+                UnconfinedTestDispatcher(),
+            )
+        DisplayRepositoryImpl(
                 commandQueue,
                 windowManager,
-                testHandler,
-                TestScope(UnconfinedTestDispatcher()),
-                UnconfinedTestDispatcher(),
+                testScope.backgroundScope,
+                displayRepositoryFromLib,
             )
             .also {
                 verify(displayManager, never()).registerDisplayListener(any(), any())
@@ -403,7 +410,7 @@
             val pendingDisplay by lastPendingDisplay()
 
             sendOnDisplayConnected(1, TYPE_EXTERNAL)
-            val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+            val initialPendingDisplay: PendingDisplay? = pendingDisplay
             assertThat(pendingDisplay).isNotNull()
             sendOnDisplayChanged(1)
 
@@ -416,7 +423,7 @@
             val pendingDisplay by lastPendingDisplay()
 
             sendOnDisplayConnected(1, TYPE_EXTERNAL)
-            val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+            val initialPendingDisplay: PendingDisplay? = pendingDisplay
             assertThat(pendingDisplay).isNotNull()
             sendOnDisplayConnected(2, TYPE_EXTERNAL)
 
@@ -648,7 +655,7 @@
         return flowValue
     }
 
-    private fun TestScope.lastPendingDisplay(): FlowValue<DisplayRepository.PendingDisplay?> {
+    private fun TestScope.lastPendingDisplay(): FlowValue<PendingDisplay?> {
         val flowValue = collectLastValue(displayRepository.pendingDisplay)
         captureAddedRemovedListener()
         verify(displayManager)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
index e41d46c..28b9e73 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
@@ -19,6 +19,7 @@
 import android.view.Display
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.kosmos.testScope
@@ -31,7 +32,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyString
-import org.mockito.kotlin.eq
+import org.mockito.kotlin.any
 import org.mockito.kotlin.verify
 
 @RunWith(AndroidJUnit4::class)
@@ -105,7 +106,7 @@
 
     @Test
     fun start_registersDumpable() {
-        verify(kosmos.dumpManager).registerNormalDumpable(anyString(), eq(underTest))
+        verify(kosmos.dumpManager).registerNormalDumpable(anyString(), any())
     }
 
     private fun createDisplay(displayId: Int): Display =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
deleted file mode 100644
index ccadd14..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.conditions;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener;
-import com.android.systemui.shared.condition.Condition;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@android.platform.test.annotations.EnabledOnRavenwood
-public class AssistantAttentionConditionTest extends SysuiTestCase {
-    @Mock
-    Condition.Callback mCallback;
-    @Mock
-    AssistManager mAssistManager;
-    @Mock
-    CoroutineScope mScope;
-
-    private AssistantAttentionCondition mAssistantAttentionCondition;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        mAssistantAttentionCondition = new AssistantAttentionCondition(mScope, mAssistManager);
-        // Adding a callback also starts the condition.
-        mAssistantAttentionCondition.addCallback(mCallback);
-    }
-
-    @Test
-    public void testEnableVisualQueryDetection() {
-        verify(mAssistManager).addVisualQueryAttentionListener(
-                any(VisualQueryAttentionListener.class));
-    }
-
-    @Test
-    public void testDisableVisualQueryDetection() {
-        mAssistantAttentionCondition.stop();
-        verify(mAssistManager).removeVisualQueryAttentionListener(
-                any(VisualQueryAttentionListener.class));
-    }
-
-    @Test
-    public void testAttentionChangedTriggersCondition() {
-        final ArgumentCaptor<VisualQueryAttentionListener> argumentCaptor =
-                ArgumentCaptor.forClass(VisualQueryAttentionListener.class);
-        verify(mAssistManager).addVisualQueryAttentionListener(argumentCaptor.capture());
-
-        argumentCaptor.getValue().onAttentionGained();
-        assertThat(mAssistantAttentionCondition.isConditionMet()).isTrue();
-
-        argumentCaptor.getValue().onAttentionLost();
-        assertThat(mAssistantAttentionCondition.isConditionMet()).isFalse();
-
-        verify(mCallback, times(2)).onConditionChanged(eq(mAssistantAttentionCondition));
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.kt
new file mode 100644
index 0000000..eefc61a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams.conditions
+
+import android.platform.test.annotations.EnabledOnRavenwood
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.assist.AssistManager
+import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shared.condition.Condition
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnabledOnRavenwood
+class AssistantAttentionConditionTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+
+    @Mock private lateinit var callback: Condition.Callback
+
+    @Mock private lateinit var assistManager: AssistManager
+
+    private lateinit var underTest: AssistantAttentionCondition
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        underTest = AssistantAttentionCondition(kosmos.testScope, assistManager)
+        // Adding a callback also starts the condition.
+        underTest.addCallback(callback)
+    }
+
+    @Test
+    fun testEnableVisualQueryDetection() =
+        kosmos.runTest { Mockito.verify(assistManager).addVisualQueryAttentionListener(any()) }
+
+    @Test
+    fun testDisableVisualQueryDetection() =
+        kosmos.runTest {
+            underTest.stop()
+            Mockito.verify(assistManager).removeVisualQueryAttentionListener(any())
+        }
+
+    @Test
+    fun testAttentionChangedTriggersCondition() =
+        kosmos.runTest {
+            val argumentCaptor = argumentCaptor<VisualQueryAttentionListener>()
+            Mockito.verify(assistManager).addVisualQueryAttentionListener(argumentCaptor.capture())
+
+            argumentCaptor.lastValue.onAttentionGained()
+            Truth.assertThat(underTest.isConditionMet).isTrue()
+
+            argumentCaptor.lastValue.onAttentionLost()
+            Truth.assertThat(underTest.isConditionMet).isFalse()
+
+            Mockito.verify(callback, Mockito.times(2)).onConditionChanged(eq(underTest))
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
deleted file mode 100644
index 58c17e2..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.conditions;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.DreamManager;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shared.condition.Condition;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@android.platform.test.annotations.EnabledOnRavenwood
-public class DreamConditionTest extends SysuiTestCase {
-    @Mock
-    Condition.Callback mCallback;
-
-    @Mock
-    DreamManager mDreamManager;
-
-    @Mock
-    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
-    @Mock
-    CoroutineScope mScope;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    /**
-     * Ensure a dreaming state immediately triggers the condition.
-     */
-    @Test
-    public void testInitialDreamingState() {
-        when(mDreamManager.isDreaming()).thenReturn(true);
-        final DreamCondition condition = new DreamCondition(mScope, mDreamManager,
-                mKeyguardUpdateMonitor);
-        condition.addCallback(mCallback);
-
-        verify(mCallback).onConditionChanged(eq(condition));
-        assertThat(condition.isConditionMet()).isTrue();
-    }
-
-    /**
-     * Ensure a non-dreaming state does not trigger the condition.
-     */
-    @Test
-    public void testInitialNonDreamingState() {
-        when(mDreamManager.isDreaming()).thenReturn(false);
-        final DreamCondition condition = new DreamCondition(mScope, mDreamManager,
-                mKeyguardUpdateMonitor);
-        condition.addCallback(mCallback);
-
-        verify(mCallback, never()).onConditionChanged(eq(condition));
-        assertThat(condition.isConditionMet()).isFalse();
-    }
-
-    /**
-     * Ensure that changing dream state triggers condition.
-     */
-    @Test
-    public void testChange() {
-        final ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCaptor =
-                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
-        when(mDreamManager.isDreaming()).thenReturn(true);
-        final DreamCondition condition = new DreamCondition(mScope, mDreamManager,
-                mKeyguardUpdateMonitor);
-        condition.addCallback(mCallback);
-        verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture());
-
-        clearInvocations(mCallback);
-        callbackCaptor.getValue().onDreamingStateChanged(false);
-        verify(mCallback).onConditionChanged(eq(condition));
-        assertThat(condition.isConditionMet()).isFalse();
-
-        clearInvocations(mCallback);
-        callbackCaptor.getValue().onDreamingStateChanged(true);
-        verify(mCallback).onConditionChanged(eq(condition));
-        assertThat(condition.isConditionMet()).isTrue();
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.kt
new file mode 100644
index 0000000..302a6e1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/conditions/DreamConditionTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams.conditions
+
+import android.app.DreamManager
+import android.platform.test.annotations.EnabledOnRavenwood
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shared.condition.Condition
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnabledOnRavenwood
+class DreamConditionTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+
+    @Mock private lateinit var callback: Condition.Callback
+
+    @Mock private lateinit var dreamManager: DreamManager
+
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    /** Ensure a dreaming state immediately triggers the condition. */
+    @Test
+    fun testInitialDreamingState() =
+        kosmos.runTest {
+            whenever(dreamManager.isDreaming).thenReturn(true)
+            val condition = DreamCondition(testScope, dreamManager, keyguardUpdateMonitor)
+            condition.addCallback(callback)
+            runCurrent()
+
+            Mockito.verify(callback).onConditionChanged(eq(condition))
+            Truth.assertThat(condition.isConditionMet).isTrue()
+        }
+
+    /** Ensure a non-dreaming state does not trigger the condition. */
+    @Test
+    fun testInitialNonDreamingState() =
+        kosmos.runTest {
+            whenever(dreamManager.isDreaming).thenReturn(false)
+            val condition = DreamCondition(testScope, dreamManager, keyguardUpdateMonitor)
+            condition.addCallback(callback)
+
+            Mockito.verify(callback, Mockito.never()).onConditionChanged(eq(condition))
+            Truth.assertThat(condition.isConditionMet).isFalse()
+        }
+
+    /** Ensure that changing dream state triggers condition. */
+    @Test
+    fun testChange() =
+        kosmos.runTest {
+            val callbackCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+            whenever(dreamManager.isDreaming).thenReturn(true)
+            val condition = DreamCondition(testScope, dreamManager, keyguardUpdateMonitor)
+            condition.addCallback(callback)
+            runCurrent()
+            Mockito.verify(keyguardUpdateMonitor).registerCallback(callbackCaptor.capture())
+
+            Mockito.clearInvocations(callback)
+            callbackCaptor.lastValue.onDreamingStateChanged(false)
+            runCurrent()
+            Mockito.verify(callback).onConditionChanged(eq(condition))
+            Truth.assertThat(condition.isConditionMet).isFalse()
+
+            Mockito.clearInvocations(callback)
+            callbackCaptor.lastValue.onDreamingStateChanged(true)
+            runCurrent()
+            Mockito.verify(callback).onConditionChanged(eq(condition))
+            Truth.assertThat(condition.isConditionMet).isTrue()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
index f2a6c11..7229761 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
@@ -26,9 +26,9 @@
 import com.android.systemui.education.data.model.EduDeviceConnectionTime
 import com.android.systemui.education.data.model.GestureEduModel
 import com.android.systemui.education.domain.interactor.mockEduInputManager
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import java.io.File
 import javax.inject.Provider
@@ -48,7 +48,7 @@
 class ContextualEducationRepositoryTest : SysuiTestCase() {
 
     private lateinit var underTest: UserContextualEducationRepository
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val dsScopeProvider: Provider<CoroutineScope> = Provider {
         TestScope(kosmos.testDispatcher).backgroundScope
@@ -70,7 +70,7 @@
                 testContext,
                 dsScopeProvider,
                 kosmos.mockEduInputManager,
-                kosmos.testDispatcher
+                kosmos.testDispatcher,
             )
         underTest.setUser(testUserId)
     }
@@ -109,7 +109,7 @@
                     lastEducationTime = kosmos.fakeEduClock.instant(),
                     usageSessionStartTime = kosmos.fakeEduClock.instant(),
                     userId = testUserId,
-                    gestureType = BACK
+                    gestureType = BACK,
                 )
             underTest.updateGestureEduModel(BACK) { newModel }
             val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
index 5030d1e..0f75e57c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/HapticSliderPluginTest.kt
@@ -21,9 +21,9 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.haptics.msdl.msdlPlayer
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -45,7 +45,7 @@
 @RunWith(AndroidJUnit4::class)
 class HapticSliderPluginTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     @Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
     @Mock private lateinit var vibratorHelper: VibratorHelper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
index 798c5ab..39b5e81 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
@@ -23,9 +23,9 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.inputdevice.data.model.UserDeviceConnectionStatus
 import com.android.systemui.keyboard.data.repository.keyboardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.touchpad.data.repository.touchpadRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.user.data.repository.userRepository
@@ -41,7 +41,7 @@
 class UserInputDeviceRepositoryTest : SysuiTestCase() {
 
     private lateinit var underTest: UserInputDeviceRepository
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val keyboardRepository = kosmos.keyboardRepository
     private val touchpadRepository = kosmos.touchpadRepository
@@ -54,7 +54,7 @@
                 kosmos.testDispatcher,
                 keyboardRepository,
                 touchpadRepository,
-                kosmos.userRepository
+                kosmos.userRepository,
             )
         userRepository.setUserInfos(USER_INFOS)
     }
@@ -72,7 +72,7 @@
             assertThat(isAnyKeyboardConnected)
                 .containsExactly(
                     UserDeviceConnectionStatus(isConnected = true, USER_INFOS[0].id),
-                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id)
+                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id),
                 )
                 .inOrder()
         }
@@ -90,16 +90,13 @@
             assertThat(isAnyTouchpadConnected)
                 .containsExactly(
                     UserDeviceConnectionStatus(isConnected = true, USER_INFOS[0].id),
-                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id)
+                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id),
                 )
                 .inOrder()
         }
 
     companion object {
         private val USER_INFOS =
-            listOf(
-                UserInfo(100, "First User", 0),
-                UserInfo(101, "Second User", 0),
-            )
+            listOf(UserInfo(100, "First User", 0), UserInfo(101, "Second User", 0))
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
index 274880b..6bd0fb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/data/repository/InputMethodRepositoryTest.kt
@@ -23,9 +23,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
@@ -47,7 +47,7 @@
 
     @Mock private lateinit var inputMethodManager: InputMethodManager
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     private lateinit var underTest: InputMethodRepository
@@ -72,7 +72,7 @@
                     inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
                         any(),
                         anyBoolean(),
-                        eq(USER_HANDLE)
+                        eq(USER_HANDLE),
                     )
                 )
                 .thenReturn(listOf())
@@ -97,7 +97,7 @@
                     inputMethodManager.getEnabledInputMethodSubtypeListAsUser(
                         eq(selectedImiId),
                         anyBoolean(),
-                        eq(USER_HANDLE)
+                        eq(USER_HANDLE),
                     )
                 )
                 .thenReturn(
@@ -125,7 +125,7 @@
             verify(inputMethodManager)
                 .showInputMethodPickerFromSystem(
                     /* showAuxiliarySubtypes = */ eq(true),
-                    /* displayId = */ eq(displayId)
+                    /* displayId = */ eq(displayId),
                 )
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
index 8e6de2f..9e129a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputmethod/domain/interactor/InputMethodInteractorTest.kt
@@ -22,8 +22,8 @@
 import com.android.systemui.inputmethod.data.model.InputMethodModel
 import com.android.systemui.inputmethod.data.repository.fakeInputMethodRepository
 import com.android.systemui.inputmethod.data.repository.inputMethodRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import java.util.UUID
 import kotlinx.coroutines.test.runTest
@@ -34,7 +34,7 @@
 @RunWith(AndroidJUnit4::class)
 class InputMethodInteractorTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val fakeInputMethodRepository = kosmos.fakeInputMethodRepository
 
@@ -148,7 +148,7 @@
             subtypes =
                 List(auxiliarySubtypes + nonAuxiliarySubtypes) {
                     InputMethodModel.Subtype(subtypeId = it, isAuxiliary = it < auxiliarySubtypes)
-                }
+                },
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
index 1bb4805..655c646 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt
@@ -34,7 +34,6 @@
 import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSource
 import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
 import com.android.systemui.keyboard.shortcut.shortcutHelperViewModel
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
@@ -43,6 +42,7 @@
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.userTracker
 import com.android.systemui.statusbar.phone.systemUIDialogFactory
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
@@ -60,7 +60,7 @@
     private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource()
     private val mockUserContext: Context = mock()
     private val kosmos =
-        Kosmos().also {
+        testKosmos().also {
             it.testCase = this
             it.testDispatcher = UnconfinedTestDispatcher()
             it.shortcutHelperSystemShortcutsSource = fakeSystemSource
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
index be9e93c..ba57ffd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.keyboard.stickykeys.shared.model.Locked
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -52,19 +52,19 @@
     fun setup() {
         val dialogFactory = mock<StickyKeyDialogFactory>()
         whenever(dialogFactory.create(any())).thenReturn(dialog)
-        val keyboardRepository = Kosmos().keyboardRepository
+        val keyboardRepository = testKosmos().keyboardRepository
         val viewModel =
             StickyKeysIndicatorViewModel(
                 stickyKeysRepository,
                 keyboardRepository,
-                testScope.backgroundScope
+                testScope.backgroundScope,
             )
         coordinator =
             StickyKeysIndicatorCoordinator(
                 testScope.backgroundScope,
                 dialogFactory,
                 viewModel,
-                mock<StickyKeysLogger>()
+                mock<StickyKeysLogger>(),
             )
         coordinator.startListening()
         keyboardRepository.setIsAnyKeyboardConnected(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index 9daf0ff..642b2e3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
@@ -64,7 +63,7 @@
     private val inputManager = mock<InputManager>()
     private val keyboardRepository = FakeKeyboardRepository()
     private val secureSettings = kosmos.fakeSettings
-    private val userRepository = Kosmos().fakeUserRepository
+    private val userRepository = testKosmos().fakeUserRepository
     private val captor =
         ArgumentCaptor.forClass(InputManager.StickyModifierStateListener::class.java)
 
@@ -117,9 +116,9 @@
     }
 
     private fun setStickyKeySetting(enabled: Boolean) {
-        val newValue = if (enabled) "1" else "0"
+        val newValue = if (enabled) 1 else 0
         val defaultUser = userRepository.getSelectedUserInfo().id
-        secureSettings.putStringForUser(ACCESSIBILITY_STICKY_KEYS, newValue, defaultUser)
+        secureSettings.putIntForUser(ACCESSIBILITY_STICKY_KEYS, newValue, defaultUser)
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
index f373062..efc68f3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
@@ -57,4 +57,17 @@
             repository.setPowerButtonDown(true)
             assertThat(isPowerDown).isTrue()
         }
+
+    @Test
+    fun testPowerButtonBeingLongPressedInteractor() =
+        runTest {
+            val isPowerButtonLongPressed by collectLastValue(
+                underTest.isPowerButtonLongPressed)
+
+            repository.setPowerButtonLongPressed(false)
+            assertThat(isPowerButtonLongPressed).isFalse()
+
+            repository.setPowerButtonLongPressed(true)
+            assertThat(isPowerButtonLongPressed).isTrue()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index 909acca..573216d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -49,9 +49,9 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SystemUIInitializerImpl;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -177,7 +177,8 @@
     @Test
     public void schedulesAlarm12hBefore() {
         long in16Hours = System.currentTimeMillis() + TimeUnit.HOURS.toHours(16);
-        AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(in16Hours, null);
+        AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(in16Hours,
+                null);
         mProvider.onNextAlarmChanged(alarmClockInfo);
 
         long twelveHours = TimeUnit.HOURS.toMillis(KeyguardSliceProvider.ALARM_VISIBILITY_HOURS);
@@ -189,7 +190,8 @@
     @Test
     public void updatingNextAlarmInvalidatesSlice() {
         long in16Hours = System.currentTimeMillis() + TimeUnit.HOURS.toHours(8);
-        AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(in16Hours, null);
+        AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(in16Hours,
+                null);
         mProvider.onNextAlarmChanged(alarmClockInfo);
 
         verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
@@ -204,7 +206,7 @@
     @Test
     public void addZenMode_addedToSlice() {
         ListBuilder listBuilder = spy(new ListBuilder(getContext(), mProvider.getUri(),
-            ListBuilder.INFINITY));
+                ListBuilder.INFINITY));
         mProvider.addZenModeLocked(listBuilder);
         verify(listBuilder, never()).addRow(any(ListBuilder.RowBuilder.class));
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index baf3b5b..3f1cadc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -23,12 +23,12 @@
 import com.android.systemui.defaultDeviceState
 import com.android.systemui.deviceStateManager
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argThat
 import java.util.function.Predicate
@@ -68,7 +68,7 @@
     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
     @Mock private lateinit var powerManager: PowerManager
     @Mock private lateinit var wallpaperManager: WallpaperManager
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val deviceStateManager = kosmos.deviceStateManager
 
     @Mock
@@ -92,7 +92,7 @@
             surfaceControl1,
             Rect(),
             mock(ActivityManager.RunningTaskInfo::class.java),
-            false
+            false,
         )
 
     private var surfaceControl2 = mock(SurfaceControl::class.java)
@@ -113,7 +113,7 @@
             surfaceControl2,
             Rect(),
             mock(ActivityManager.RunningTaskInfo::class.java),
-            false
+            false,
         )
     private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
 
@@ -135,7 +135,7 @@
             surfaceControlWp,
             Rect(),
             mock(ActivityManager.RunningTaskInfo::class.java),
-            false
+            false,
         )
     private lateinit var wallpaperTargets: Array<RemoteAnimationTarget>
 
@@ -157,7 +157,7 @@
             surfaceControlLockWp,
             Rect(),
             mock(ActivityManager.RunningTaskInfo::class.java),
-            false
+            false,
         )
     private lateinit var lockWallpaperTargets: Array<RemoteAnimationTarget>
     private var shouldPerformSmartspaceTransition = false
@@ -179,14 +179,14 @@
                     notificationShadeWindowController,
                     powerManager,
                     wallpaperManager,
-                    deviceStateManager
+                    deviceStateManager,
                 ) {
                 override fun shouldPerformSmartspaceTransition(): Boolean =
                     shouldPerformSmartspaceTransition
             }
         keyguardUnlockAnimationController.setLauncherUnlockController(
             "",
-            launcherUnlockAnimationController
+            launcherUnlockAnimationController,
         )
 
         whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
@@ -227,7 +227,7 @@
             arrayOf(),
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
@@ -259,7 +259,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Since the animation is running, we should not have finished the remote animation.
@@ -282,7 +282,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         verify(listener).onUnlockAnimationStarted(any(), eq(true), any(), any())
@@ -303,7 +303,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         verify(listener).onUnlockAnimationStarted(any(), eq(false), any(), any())
@@ -327,7 +327,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            true /* requestedShowSurfaceBehindKeyguard */
+            true, /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertTrue(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
@@ -351,7 +351,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            true /* requestedShowSurfaceBehindKeyguard */
+            true, /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -373,7 +373,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -389,7 +389,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            true /* requestedShowSurfaceBehindKeyguard */
+            true, /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertFalse(keyguardUnlockAnimationController.canPerformInWindowLauncherAnimations())
@@ -406,7 +406,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
@@ -427,7 +427,7 @@
             wallpaperTargets,
             lockWallpaperTargets,
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         for (i in 0..10) {
@@ -471,7 +471,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Cancel the animator so we can verify only the setSurfaceBehind call below.
@@ -492,7 +492,7 @@
         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
         verify(
                 surfaceTransactionApplier,
-                times(1).description("WallpaperSurface was expected to receive scheduleApply once")
+                times(1).description("WallpaperSurface was expected to receive scheduleApply once"),
             )
             .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
 
@@ -523,7 +523,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Cancel the animator so we can verify only the setSurfaceBehind call below.
@@ -539,7 +539,7 @@
         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
         verify(
                 surfaceTransactionApplier,
-                atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply")
+                atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply"),
             )
             .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
 
@@ -562,7 +562,7 @@
             wallpaperTargets,
             arrayOf(),
             0 /* startTime */,
-            false /* requestedShowSurfaceBehindKeyguard */
+            false, /* requestedShowSurfaceBehindKeyguard */
         )
 
         // Stop the animator - we just want to test whether the override is not applied.
@@ -578,7 +578,7 @@
         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
         verify(
                 surfaceTransactionApplier,
-                atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply")
+                atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply"),
             )
             .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
 
@@ -588,7 +588,7 @@
         assertEquals(
             "Wallpaper surface was expected to have opacity 1",
             1f,
-            captorWp.getLastValue().alpha
+            captorWp.getLastValue().alpha,
         )
 
         verifyNoMoreInteractions(surfaceTransactionApplier)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyEventRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyEventRepositoryTest.kt
index c7f1525..9ab5f89 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyEventRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyEventRepositoryTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.keyevent.data.repository.KeyEventRepositoryImpl
 import com.android.systemui.statusbar.CommandQueue
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -34,10 +35,10 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class KeyEventRepositoryTest : SysuiTestCase() {
@@ -62,6 +63,15 @@
         }
 
     @Test
+    fun isPowerButtonBeingLongPressed_initialValueFalse() =
+        testScope.runTest {
+            val isPowerButtonLongPressed by collectLastValue(
+                underTest.isPowerButtonLongPressed)
+            runCurrent()
+            assertThat(isPowerButtonLongPressed).isFalse()
+        }
+
+    @Test
     fun isPowerButtonDown_onChange() =
         testScope.runTest {
             val isPowerButtonDown by collectLastValue(underTest.isPowerButtonDown)
@@ -77,4 +87,54 @@
             )
             assertThat(isPowerButtonDown).isFalse()
         }
+
+
+    @Test
+    fun isPowerButtonBeingLongPressed_onPowerButtonDown() =
+        testScope.runTest {
+            val isPowerButtonLongPressed by collectLastValue(
+                underTest.isPowerButtonLongPressed)
+
+            runCurrent()
+
+            verify(commandQueue).addCallback(commandQueueCallbacks.capture())
+
+            val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_POWER)
+            commandQueueCallbacks.value.handleSystemKey(keyEvent)
+
+            assertThat(isPowerButtonLongPressed).isFalse()
+        }
+
+    @Test
+    fun isPowerButtonBeingLongPressed_onPowerButtonUp() =
+        testScope.runTest {
+            val isPowerButtonLongPressed by collectLastValue(
+                underTest.isPowerButtonLongPressed)
+
+            runCurrent()
+
+            verify(commandQueue).addCallback(commandQueueCallbacks.capture())
+
+            val keyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_POWER)
+            commandQueueCallbacks.value.handleSystemKey(keyEvent)
+
+            assertThat(isPowerButtonLongPressed).isFalse()
+        }
+
+    @Test
+    fun isPowerButtonBeingLongPressed_onPowerButtonDown_longPressFlagSet() =
+        testScope.runTest {
+            val isPowerButtonBeingLongPressed by collectLastValue(
+                underTest.isPowerButtonLongPressed)
+
+            runCurrent()
+
+            verify(commandQueue).addCallback(commandQueueCallbacks.capture())
+
+            val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_POWER)
+            keyEvent.setFlags(KeyEvent.FLAG_LONG_PRESS)
+            commandQueueCallbacks.value.handleSystemKey(keyEvent)
+
+            assertThat(isPowerButtonBeingLongPressed).isTrue()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index 6c4325a..2ab3650 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -35,7 +35,6 @@
 import android.os.PowerManager
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
@@ -43,8 +42,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.common.data.repository.batteryRepository
-import com.android.systemui.common.data.repository.fake
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalV2Available
@@ -72,7 +69,6 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
 import com.android.systemui.testKosmos
-import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth
 import junit.framework.Assert.assertEquals
 import kotlinx.coroutines.runBlocking
@@ -433,9 +429,7 @@
     @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
     fun testTransitionToGlanceableHub_onWakeUpFromAod() =
         kosmos.runTest {
-            val user = setCommunalV2Available(true)
-            fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1, user.id)
-            batteryRepository.fake.setDevicePluggedIn(true)
+            setCommunalV2Available(true)
 
             val currentScene by collectLastValue(communalSceneInteractor.currentScene)
             fakeCommunalSceneRepository.changeScene(CommunalScenes.Blank)
@@ -449,4 +443,24 @@
             Truth.assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
             assertThat(transitionRepository).noTransitionsStarted()
         }
+
+    @Test
+    @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+    fun testDoNotTransitionToGlanceableHub_onWakeUpFromAodDueToMotion() =
+        kosmos.runTest {
+            setCommunalV2Available(true)
+
+            val currentScene by collectLastValue(communalSceneInteractor.currentScene)
+            fakeCommunalSceneRepository.changeScene(CommunalScenes.Blank)
+
+            // Communal is not showing
+            Truth.assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+
+            powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_LIFT)
+            testScope.advanceTimeBy(100) // account for debouncing
+
+            Truth.assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+            assertThat(transitionRepository)
+                .startedTransition(from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN)
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index 9be786f..c3d18a3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -20,7 +20,6 @@
 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.service.dream.dreamManager
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
@@ -30,8 +29,6 @@
 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
 import com.android.systemui.Flags.glanceableHubV2
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.data.repository.batteryRepository
-import com.android.systemui.common.data.repository.fake
 import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
 import com.android.systemui.communal.data.repository.communalSceneRepository
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
@@ -61,8 +58,6 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth
 import junit.framework.Assert.assertEquals
 import kotlinx.coroutines.flow.flowOf
@@ -171,15 +166,7 @@
     fun testTransitionToLockscreen_onWake_canDream_ktfRefactor() =
         kosmos.runTest {
             setCommunalAvailable(true)
-            if (glanceableHubV2()) {
-                val user = fakeUserRepository.asMainUser()
-                fakeSettings.putIntForUser(
-                    Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
-                    1,
-                    user.id,
-                )
-                batteryRepository.fake.setDevicePluggedIn(true)
-            } else {
+            if (!glanceableHubV2()) {
                 whenever(dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
             }
 
@@ -193,6 +180,7 @@
 
     @Test
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
     fun testTransitionToLockscreen_onWake_canNotDream_glanceableHubAvailable() =
         kosmos.runTest {
             whenever(dreamManager.canStartDreaming(anyBoolean())).thenReturn(false)
@@ -225,15 +213,7 @@
     fun testTransitionToGlanceableHub_onWakeup_ifAvailable() =
         kosmos.runTest {
             setCommunalAvailable(true)
-            if (glanceableHubV2()) {
-                val user = fakeUserRepository.asMainUser()
-                fakeSettings.putIntForUser(
-                    Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
-                    1,
-                    user.id,
-                )
-                batteryRepository.fake.setDevicePluggedIn(true)
-            } else {
+            if (!glanceableHubV2()) {
                 whenever(dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
             }
 
@@ -249,6 +229,25 @@
         }
 
     @Test
+    @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR, FLAG_SCENE_CONTAINER)
+    @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+    fun testTransitionToLockscreen_onWakeupFromLift() =
+        kosmos.runTest {
+            setCommunalAvailable(true)
+            if (!glanceableHubV2()) {
+                whenever(dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
+            }
+
+            // Device turns on.
+            powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_LIFT)
+            testScope.advanceTimeBy(51L)
+
+            // We transition to the lockscreen instead of the hub.
+            assertThat(transitionRepository)
+                .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.LOCKSCREEN)
+        }
+
+    @Test
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop() =
         kosmos.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt
index 2558d58..89a53f5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt
@@ -49,6 +49,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
+@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
 class KeyguardStateCallbackInteractorTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
@@ -81,7 +82,6 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun test_lockscreenVisibility_notifyDismissSucceeded_ifNotVisible() =
         testScope.runTest {
             underTest.addCallback(callback)
@@ -109,7 +109,6 @@
         }
 
     @Test
-    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun test_lockscreenVisibility_reportsKeyguardShowingChanged() =
         testScope.runTest {
             underTest.addCallback(callback)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
index e203a27..1dddfc1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
@@ -18,11 +18,17 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.content.Intent
+import android.os.PowerManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.provider.Settings
 import android.view.accessibility.accessibilityManagerWrapper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.logging.uiEventLogger
+import com.android.systemui.Flags.FLAG_DOUBLE_TAP_TO_SLEEP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
@@ -39,6 +45,8 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
+import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.advanceTimeBy
@@ -46,14 +54,19 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mock
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
 class KeyguardTouchHandlingInteractorTest : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
@@ -61,17 +74,23 @@
             this.uiEventLogger = mock<UiEventLoggerFake>()
         }
 
+    @get:Rule val setFlagsRule = SetFlagsRule()
+
     private lateinit var underTest: KeyguardTouchHandlingInteractor
 
     private val logger = kosmos.uiEventLogger
     private val testScope = kosmos.testScope
     private val keyguardRepository = kosmos.fakeKeyguardRepository
     private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val secureSettingsRepository = kosmos.userAwareSecureSettingsRepository
+
+    @Mock private lateinit var powerManager: PowerManager
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, true)
+        overrideResource(com.android.internal.R.bool.config_supportDoubleTapSleep, true)
         whenever(kosmos.accessibilityManagerWrapper.getRecommendedTimeoutMillis(anyInt(), anyInt()))
             .thenAnswer { it.arguments[0] }
 
@@ -80,13 +99,13 @@
 
     @After
     fun tearDown() {
-        mContext
-            .getOrCreateTestableResources()
-            .removeOverride(R.bool.long_press_keyguard_customize_lockscreen_enabled)
+        val testableResource = mContext.getOrCreateTestableResources()
+        testableResource.removeOverride(R.bool.long_press_keyguard_customize_lockscreen_enabled)
+        testableResource.removeOverride(com.android.internal.R.bool.config_supportDoubleTapSleep)
     }
 
     @Test
-    fun isEnabled() =
+    fun isLongPressEnabled() =
         testScope.runTest {
             val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
             KeyguardState.values().forEach { keyguardState ->
@@ -101,7 +120,7 @@
         }
 
     @Test
-    fun isEnabled_alwaysFalseWhenQuickSettingsAreVisible() =
+    fun isLongPressEnabled_alwaysFalseWhenQuickSettingsAreVisible() =
         testScope.runTest {
             val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
             KeyguardState.values().forEach { keyguardState ->
@@ -112,7 +131,7 @@
         }
 
     @Test
-    fun isEnabled_alwaysFalseWhenConfigEnabledBooleanIsFalse() =
+    fun isLongPressEnabled_alwaysFalseWhenConfigEnabledBooleanIsFalse() =
         testScope.runTest {
             overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, false)
             createUnderTest()
@@ -294,6 +313,119 @@
             assertThat(isMenuVisible).isFalse()
         }
 
+    @Test
+    @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
+    fun isDoubleTapEnabled_flagEnabled_userSettingEnabled_onlyTrueInLockScreenState() {
+        testScope.runTest {
+            secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true)
+
+            val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled)
+            KeyguardState.entries.forEach { keyguardState ->
+                setUpState(keyguardState = keyguardState)
+
+                if (keyguardState == KeyguardState.LOCKSCREEN) {
+                    assertThat(isEnabled()).isTrue()
+                } else {
+                    assertThat(isEnabled()).isFalse()
+                }
+            }
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
+    fun isDoubleTapEnabled_flagEnabled_userSettingDisabled_alwaysFalse() {
+        testScope.runTest {
+            secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, false)
+
+            val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled)
+            KeyguardState.entries.forEach { keyguardState ->
+                setUpState(keyguardState = keyguardState)
+
+                assertThat(isEnabled()).isFalse()
+            }
+        }
+    }
+
+    @Test
+    @DisableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
+    fun isDoubleTapEnabled_flagDisabled_userSettingEnabled_alwaysFalse() {
+        testScope.runTest {
+            secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true)
+
+            val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled)
+            KeyguardState.entries.forEach { keyguardState ->
+                setUpState(keyguardState = keyguardState)
+
+                assertThat(isEnabled()).isFalse()
+            }
+        }
+    }
+
+
+
+    @Test
+    @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
+    fun isDoubleTapEnabled_flagEnabledAndConfigDisabled_alwaysFalse() {
+        testScope.runTest {
+            secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true)
+            overrideResource(com.android.internal.R.bool.config_supportDoubleTapSleep, false)
+            createUnderTest()
+
+            val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled)
+            KeyguardState.entries.forEach { keyguardState ->
+                setUpState(keyguardState = keyguardState)
+
+                assertThat(isEnabled()).isFalse()
+            }
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
+    fun isDoubleTapEnabled_quickSettingsVisible_alwaysFalse() {
+        testScope.runTest {
+            secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true)
+
+            val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled)
+            KeyguardState.entries.forEach { keyguardState ->
+                setUpState(keyguardState = keyguardState, isQuickSettingsVisible = true)
+
+                assertThat(isEnabled()).isFalse()
+            }
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
+    fun onDoubleClick_doubleTapEnabled() {
+        testScope.runTest {
+            secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true)
+            val isEnabled by collectLastValue(underTest.isDoubleTapHandlingEnabled)
+            runCurrent()
+
+            underTest.onDoubleClick()
+
+            assertThat(isEnabled).isTrue()
+            verify(powerManager).goToSleep(anyLong())
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
+    fun onDoubleClick_doubleTapDisabled() {
+        testScope.runTest {
+            secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, false)
+            val isEnabled by collectLastValue(underTest.isDoubleTapHandlingEnabled)
+            runCurrent()
+
+            underTest.onDoubleClick()
+
+            assertThat(isEnabled).isFalse()
+            verify(powerManager, never()).goToSleep(anyLong())
+        }
+    }
+
     private suspend fun createUnderTest(isRevampedWppFeatureEnabled: Boolean = true) {
         // This needs to be re-created for each test outside of kosmos since the flag values are
         // read during initialization to set up flows. Maybe there is a better way to handle that.
@@ -309,6 +441,9 @@
                 accessibilityManager = kosmos.accessibilityManagerWrapper,
                 pulsingGestureListener = kosmos.pulsingGestureListener,
                 faceAuthInteractor = kosmos.deviceEntryFaceAuthInteractor,
+                secureSettingsRepository = secureSettingsRepository,
+                powerManager = powerManager,
+                systemClock = kosmos.fakeSystemClock,
             )
         setUpState()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 8df70ef..7d5e9a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -33,7 +33,7 @@
 import com.android.systemui.communal.domain.interactor.CommunalSceneTransitionInteractor
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSceneTransitionInteractor
-import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.domain.interactor.setCommunalV2Available
 import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
 import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -1004,16 +1004,15 @@
     @BrokenWithSceneContainer(339465026)
     fun occludedToGlanceableHub_communalKtfRefactor() =
         testScope.runTest {
-            // GIVEN a device on lockscreen and communal is available
-            keyguardRepository.setKeyguardShowing(true)
-            kosmos.setCommunalAvailable(true)
-            runCurrent()
-
             // GIVEN a prior transition has run to OCCLUDED from GLANCEABLE_HUB
             runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.OCCLUDED)
             keyguardRepository.setKeyguardOccluded(true)
             runCurrent()
 
+            // GIVEN a device on lockscreen and communal is available
+            kosmos.setCommunalV2Available(true)
+            runCurrent()
+
             // WHEN occlusion ends
             keyguardRepository.setKeyguardOccluded(false)
             runCurrent()
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 97c746c..d0762a3 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
@@ -25,10 +25,13 @@
 import android.view.RemoteAnimationTarget
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.window.flags.Flags
@@ -63,6 +66,9 @@
     @Mock
     private lateinit var keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor
     @Mock private lateinit var keyguardTransitions: KeyguardTransitions
+    @Mock private lateinit var lockPatternUtils: LockPatternUtils
+    @Mock private lateinit var keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor
+    @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
 
     @Before
     fun setUp() {
@@ -77,6 +83,9 @@
                 keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
                 keyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor,
                 keyguardTransitions = keyguardTransitions,
+                selectedUserInteractor = selectedUserInteractor,
+                lockPatternUtils = lockPatternUtils,
+                keyguardShowWhileAwakeInteractor = keyguardShowWhileAwakeInteractor,
             )
     }
 
@@ -236,6 +245,8 @@
             .whenever(keyguardDismissTransitionInteractor)
             .startDismissKeyguardTransition(any(), any())
 
+        whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(-1)
+
         underTest.onKeyguardGoingAwayRemoteAnimationStart(
             transit = 0,
             apps = arrayOf(mock<RemoteAnimationTarget>()),
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 83bee7c..fe213a6 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
@@ -458,6 +458,56 @@
 
     @Test
     @DisableSceneContainer
+    fun alpha_shadeExpansionIgnoredWhenTransitioningAwayFromLockscreen() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.alpha(viewState))
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                testScope,
+            )
+
+            shadeTestUtil.setQsExpansion(0f)
+            assertThat(alpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                listOf(
+                    TransitionStep(
+                        from = KeyguardState.LOCKSCREEN,
+                        to = KeyguardState.PRIMARY_BOUNCER,
+                        transitionState = TransitionState.STARTED,
+                        value = 0f,
+                    ),
+                    TransitionStep(
+                        from = KeyguardState.LOCKSCREEN,
+                        to = KeyguardState.PRIMARY_BOUNCER,
+                        transitionState = TransitionState.RUNNING,
+                        value = 0.8f,
+                    ),
+                ),
+                testScope,
+            )
+            val priorAlpha = alpha
+            shadeTestUtil.setQsExpansion(0.5f)
+            assertThat(alpha).isEqualTo(priorAlpha)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                listOf(
+                    TransitionStep(
+                        from = KeyguardState.LOCKSCREEN,
+                        to = KeyguardState.PRIMARY_BOUNCER,
+                        transitionState = TransitionState.FINISHED,
+                        value = 1f,
+                    )
+                ),
+                testScope,
+            )
+            assertThat(alpha).isEqualTo(0f)
+        }
+
+    @Test
+    @DisableSceneContainer
     fun alphaFromShadeExpansion_doesNotEmitWhenTransitionRunning() =
         testScope.runTest {
             keyguardTransitionRepository.sendTransitionSteps(
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 91cb1ff..9c16829 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
@@ -26,6 +26,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.flags.BrokenWithSceneContainer
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -44,7 +45,7 @@
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.runCurrent
@@ -101,20 +102,30 @@
 
             // immediately 0f
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
-            runCurrent()
-            Truth.assertThat(actual).isEqualTo(0f)
+            assertThat(actual).isEqualTo(0f)
 
             repository.sendTransitionStep(step(.2f))
-            runCurrent()
-            Truth.assertThat(actual).isEqualTo(0f)
+            assertThat(actual).isEqualTo(0f)
 
             repository.sendTransitionStep(step(0.8f))
-            runCurrent()
-            Truth.assertThat(actual).isEqualTo(0f)
+            assertThat(actual).isEqualTo(0f)
 
             repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(actual).isEqualTo(0f)
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun lockscreenAlphaEndsWithZero() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.lockscreenAlpha)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             runCurrent()
-            Truth.assertThat(actual).isEqualTo(0f)
+
+            // Jump right to the end and validate the value
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(alpha).isEqualTo(0f)
         }
 
     @Test
@@ -138,21 +149,17 @@
             runCurrent()
             // fade out
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
-            runCurrent()
-            Truth.assertThat(actual).isEqualTo(1f)
+            assertThat(actual).isEqualTo(1f)
 
             repository.sendTransitionStep(step(.1f))
-            runCurrent()
-            Truth.assertThat(actual).isIn(Range.open(.1f, .9f))
+            assertThat(actual).isIn(Range.open(.1f, .9f))
 
             // alpha is 1f before the full transition starts ending
             repository.sendTransitionStep(step(0.8f))
-            runCurrent()
-            Truth.assertThat(actual).isEqualTo(0f)
+            assertThat(actual).isEqualTo(0f)
 
             repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
-            runCurrent()
-            Truth.assertThat(actual).isEqualTo(0f)
+            assertThat(actual).isEqualTo(0f)
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt
index 8533134..95a6e56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModelTest.kt
@@ -21,15 +21,20 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_NOTIFICATION_SHADE_BLUR
 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.DisableSceneContainer
 import com.android.systemui.flags.andSceneContainer
+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.blurConfig
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -43,6 +48,7 @@
     SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
+    private val repository = kosmos.fakeKeyguardTransitionRepository
     private lateinit var underTest: OccludedToPrimaryBouncerTransitionViewModel
 
     companion object {
@@ -63,6 +69,25 @@
     }
 
     @Test
+    @DisableSceneContainer
+    fun lockscreenAlphaImmediatelyToZero() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.lockscreenAlpha)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(0.1f, TransitionState.RUNNING))
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+        }
+
+    @Test
     @BrokenWithSceneContainer(388068805)
     fun notificationsAreBlurredImmediatelyWhenBouncerIsOpenedAndShadeIsExpanded() =
         testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/log/LogWtfHandlerRuleTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/log/LogWtfHandlerRuleTest.kt
new file mode 100644
index 0000000..d5d256e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/log/LogWtfHandlerRuleTest.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.model.Statement
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class LogWtfHandlerRuleTest : SysuiTestCase() {
+
+    val underTest = LogWtfHandlerRule()
+
+    @Test
+    fun passingTestWithoutWtf_shouldPass() {
+        val result = runTestCodeWithRule {
+            Log.e(TAG, "just an error", IndexOutOfBoundsException())
+        }
+        assertThat(result.isSuccess).isTrue()
+    }
+
+    @Test
+    fun passingTestWithWtf_shouldFail() {
+        val result = runTestCodeWithRule {
+            Log.wtf(TAG, "some terrible failure", IllegalStateException())
+        }
+        assertThat(result.isFailure).isTrue()
+        val exception = result.exceptionOrNull()
+        assertThat(exception).isInstanceOf(AssertionError::class.java)
+        assertThat(exception?.cause).isInstanceOf(Log.TerribleFailure::class.java)
+        assertThat(exception?.cause?.cause).isInstanceOf(IllegalStateException::class.java)
+    }
+
+    @Test
+    fun failingTestWithoutWtf_shouldFail() {
+        val result = runTestCodeWithRule {
+            Log.e(TAG, "just an error", IndexOutOfBoundsException())
+            throw NullPointerException("some npe")
+        }
+        assertThat(result.isFailure).isTrue()
+        assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java)
+    }
+
+    @Test
+    fun failingTestWithWtf_shouldFail() {
+        val result = runTestCodeWithRule {
+            Log.wtf(TAG, "some terrible failure", IllegalStateException())
+            throw NullPointerException("some npe")
+        }
+        assertThat(result.isFailure).isTrue()
+        assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java)
+        val suppressedExceptions = result.exceptionOrNull()!!.suppressedExceptions
+        assertThat(suppressedExceptions).hasSize(1)
+        val suppressed = suppressedExceptions.first()
+        assertThat(suppressed).isInstanceOf(AssertionError::class.java)
+        assertThat(suppressed.cause).isInstanceOf(Log.TerribleFailure::class.java)
+        assertThat(suppressed.cause?.cause).isInstanceOf(IllegalStateException::class.java)
+    }
+
+    @Test
+    fun passingTestWithExemptWtf_shouldPass() {
+        underTest.addFailureLogExemption { it.tag == TAG_EXPECTED }
+        val result = runTestCodeWithRule {
+            Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException())
+        }
+        assertThat(result.isSuccess).isTrue()
+    }
+
+    @Test
+    fun failingTestWithExemptWtf_shouldFail() {
+        underTest.addFailureLogExemption { it.tag == TAG_EXPECTED }
+        val result = runTestCodeWithRule {
+            Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException())
+            throw NullPointerException("some npe")
+        }
+        assertThat(result.isFailure).isTrue()
+        assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java)
+        val suppressedExceptions = result.exceptionOrNull()!!.suppressedExceptions
+        assertThat(suppressedExceptions).isEmpty()
+    }
+
+    @Test
+    fun passingTestWithOneExemptWtfOfTwo_shouldFail() {
+        underTest.addFailureLogExemption { it.tag == TAG_EXPECTED }
+        val result = runTestCodeWithRule {
+            Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException())
+            Log.wtf(TAG, "some terrible failure", IllegalStateException())
+        }
+        assertThat(result.isFailure).isTrue()
+        val exception = result.exceptionOrNull()
+        assertThat(exception).isInstanceOf(AssertionError::class.java)
+        assertThat(exception?.cause).isInstanceOf(Log.TerribleFailure::class.java)
+        assertThat(exception?.cause?.cause).isInstanceOf(IllegalStateException::class.java)
+    }
+
+    @Test
+    fun failingTestWithOneExemptWtfOfTwo_shouldFail() {
+        underTest.addFailureLogExemption { it.tag == TAG_EXPECTED }
+        val result = runTestCodeWithRule {
+            Log.wtf(TAG_EXPECTED, "some expected failure", IllegalStateException())
+            Log.wtf(TAG, "some terrible failure", IllegalStateException())
+            throw NullPointerException("some npe")
+        }
+        assertThat(result.isFailure).isTrue()
+        assertThat(result.exceptionOrNull()).isInstanceOf(NullPointerException::class.java)
+        val suppressedExceptions = result.exceptionOrNull()!!.suppressedExceptions
+        assertThat(suppressedExceptions).hasSize(1)
+        val suppressed = suppressedExceptions.first()
+        assertThat(suppressed).isInstanceOf(AssertionError::class.java)
+        assertThat(suppressed.cause).isInstanceOf(Log.TerribleFailure::class.java)
+        assertThat(suppressed.cause?.cause).isInstanceOf(IllegalStateException::class.java)
+    }
+
+    private fun runTestCodeWithRule(testCode: () -> Unit): Result<Unit> {
+        val testCodeStatement =
+            object : Statement() {
+                override fun evaluate() {
+                    testCode()
+                }
+            }
+        val wrappedTest = underTest.apply(testCodeStatement, mock())
+        return try {
+            wrappedTest.evaluate()
+            Result.success(Unit)
+        } catch (e: Throwable) {
+            Result.failure(e)
+        }
+    }
+
+    companion object {
+        const val TAG = "LogWtfHandlerRuleTest"
+        const val TAG_EXPECTED = "EXPECTED"
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/NotificationMediaManagerTest.kt
similarity index 99%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/media/NotificationMediaManagerTest.kt
index a7fe586..10b0085 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/NotificationMediaManagerTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar
+package com.android.systemui.media
 
 import android.media.MediaMetadata
 import android.media.session.MediaController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
index 61119cc..8592c42 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
@@ -235,6 +235,19 @@
         verify(mediaCarousel, never()).animationTargetX = anyFloat()
     }
 
+    @Test
+    fun testScrollingDisabled_noScroll_notDismissible() {
+        setupMediaContainer(visibleIndex = 1, showsSettingsButton = false)
+
+        mediaCarouselScrollHandler.scrollingDisabled = true
+
+        clock.advanceTime(DISMISS_DELAY)
+        executor.runAllReady()
+
+        verify(mediaCarousel, never()).smoothScrollTo(anyInt(), anyInt())
+        verify(mediaCarousel, never()).animationTargetX = anyFloat()
+    }
+
     private fun setupMediaContainer(visibleIndex: Int, showsSettingsButton: Boolean = true) {
         whenever(contentContainer.childCount).thenReturn(2)
         val child1: View = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacyTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacyTest.java
index 2db2199..9c4d93c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacyTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacyTest.java
@@ -48,10 +48,12 @@
 import com.android.media.flags.Flags;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.res.R;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListeningExecutorService;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -61,6 +63,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 import java.util.stream.Collectors;
 
 @SmallTest
@@ -95,6 +98,8 @@
     private List<MediaDevice> mMediaDevices = new ArrayList<>();
     private List<MediaItem> mMediaItems = new ArrayList<>();
     MediaOutputSeekbar mSpyMediaOutputSeekbar;
+    Executor mMainExecutor = mContext.getMainExecutor();
+    ListeningExecutorService mBackgroundExecutor = ThreadUtils.getBackgroundExecutor();
 
     @Before
     public void setUp() {
@@ -108,6 +113,8 @@
         when(mMediaSwitchingController.getSessionVolumeMax()).thenReturn(TEST_MAX_VOLUME);
         when(mMediaSwitchingController.getSessionVolume()).thenReturn(TEST_CURRENT_VOLUME);
         when(mMediaSwitchingController.getSessionName()).thenReturn(TEST_SESSION_NAME);
+        when(mMediaSwitchingController.getColorSchemeLegacy()).thenReturn(
+                mock(MediaOutputColorSchemeLegacy.class));
         when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
         when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
         when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
@@ -122,7 +129,8 @@
         mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice1, true));
         mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice2, false));
 
-        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController);
+        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController, mMainExecutor,
+                mBackgroundExecutor);
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapterLegacy.MediaDeviceViewHolderLegacy) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
@@ -148,7 +156,8 @@
 
     @Test
     public void onBindViewHolder_bindPairNew_verifyView() {
-        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController);
+        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController, mMainExecutor,
+                mBackgroundExecutor);
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapterLegacy.MediaDeviceViewHolderLegacy) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
@@ -173,7 +182,8 @@
                                 .map((item) -> item.getMediaDevice().get())
                                 .collect(Collectors.toList()));
         when(mMediaSwitchingController.getSessionName()).thenReturn(TEST_SESSION_NAME);
-        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController);
+        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController, mMainExecutor,
+                mBackgroundExecutor);
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapterLegacy.MediaDeviceViewHolderLegacy) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
@@ -195,7 +205,8 @@
                                 .map((item) -> item.getMediaDevice().get())
                                 .collect(Collectors.toList()));
         when(mMediaSwitchingController.getSessionName()).thenReturn(null);
-        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController);
+        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController, mMainExecutor,
+                mBackgroundExecutor);
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapterLegacy.MediaDeviceViewHolderLegacy) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
@@ -665,7 +676,8 @@
 
     @Test
     public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
-        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController);
+        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController, mMainExecutor,
+                mBackgroundExecutor);
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapterLegacy.MediaDeviceViewHolderLegacy) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
@@ -683,7 +695,8 @@
         assertThat(mMediaDevice2.getState()).isEqualTo(
                 LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
         when(mMediaDevice2.getSelectionBehavior()).thenReturn(SELECTION_BEHAVIOR_TRANSFER);
-        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController);
+        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController, mMainExecutor,
+                mBackgroundExecutor);
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapterLegacy.MediaDeviceViewHolderLegacy) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
@@ -701,7 +714,8 @@
         assertThat(mMediaDevice2.getState()).isEqualTo(
                 LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
         when(mMediaDevice2.getSelectionBehavior()).thenReturn(SELECTION_BEHAVIOR_TRANSFER);
-        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController);
+        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController,
+                mContext.getMainExecutor(), ThreadUtils.getBackgroundExecutor());
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapterLegacy.MediaDeviceViewHolderLegacy) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
@@ -723,7 +737,8 @@
         when(mMediaDevice2.getState()).thenReturn(
                 LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
         when(mMediaDevice2.getSelectionBehavior()).thenReturn(SELECTION_BEHAVIOR_GO_TO_APP);
-        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController);
+        mMediaOutputAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController, mMainExecutor,
+                mBackgroundExecutor);
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapterLegacy.MediaDeviceViewHolderLegacy) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
@@ -778,6 +793,8 @@
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+        assertThat(mViewHolder.mTitleText.getAlpha())
+                .isEqualTo(MediaOutputAdapterLegacy.DEVICE_ACTIVE_ALPHA);
         assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue();
 
         mViewHolder.mContainerLayout.performClick();
@@ -799,6 +816,8 @@
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+        assertThat(mViewHolder.mTitleText.getAlpha())
+                .isEqualTo(MediaOutputAdapterLegacy.DEVICE_ACTIVE_ALPHA);
         assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue();
 
         mViewHolder.mContainerLayout.performClick();
@@ -820,6 +839,8 @@
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+        assertThat(mViewHolder.mTitleText.getAlpha())
+                .isEqualTo(MediaOutputAdapterLegacy.DEVICE_ACTIVE_ALPHA);
         assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue();
 
         mViewHolder.mContainerLayout.performClick();
@@ -841,6 +862,8 @@
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+        assertThat(mViewHolder.mTitleText.getAlpha())
+                .isEqualTo(MediaOutputAdapterLegacy.DEVICE_ACTIVE_ALPHA);
         assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue();
 
         mViewHolder.mContainerLayout.performClick();
@@ -862,6 +885,8 @@
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+        assertThat(mViewHolder.mTitleText.getAlpha())
+                .isEqualTo(MediaOutputAdapterLegacy.DEVICE_ACTIVE_ALPHA);
         assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue();
 
         mViewHolder.mContainerLayout.performClick();
@@ -883,6 +908,8 @@
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+        assertThat(mViewHolder.mTitleText.getAlpha())
+                .isEqualTo(MediaOutputAdapterLegacy.DEVICE_DISABLED_ALPHA);
         assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue();
 
         mViewHolder.mContainerLayout.performClick();
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 6bc8000..04f7fe1 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
@@ -21,10 +21,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.policy.CastDevice
 import com.android.systemui.statusbar.policy.fakeCastController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -34,7 +34,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MediaRouterRepositoryTest : SysuiTestCase() {
-    val kosmos = Kosmos()
+    val kosmos = testKosmos()
     val testScope = kosmos.testScope
     val castController = kosmos.fakeCastController
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUIStateDispatcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUIStateDispatcherTest.kt
index b82f5fc..ff2e13b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUIStateDispatcherTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUIStateDispatcherTest.kt
@@ -81,7 +81,7 @@
     private companion object {
         const val DISPLAY_1 = 1
         const val DISPLAY_2 = 2
-        const val FLAG_1 = 10L
-        const val FLAG_2 = 20L
+        const val FLAG_1 = 1L
+        const val FLAG_2 = 2L
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt
index 09588f9..d1b5529 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt
@@ -35,10 +35,10 @@
 
     @Test
     fun updateFlags() {
-        underTest.updateFlags(Display.DEFAULT_DISPLAY, 1L to true, 2L to false, 3L to true)
+        underTest.updateFlags(Display.DEFAULT_DISPLAY, 1L to true, 2L to false, 4L to true)
 
         assertThat(underTest.flags and 1L).isNotEqualTo(0L)
         assertThat(underTest.flags and 2L).isEqualTo(0L)
-        assertThat(underTest.flags and 3L).isNotEqualTo(0L)
+        assertThat(underTest.flags and 4L).isNotEqualTo(0L)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java
deleted file mode 100644
index 5250d56..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.process.condition;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.TestableLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.process.ProcessWrapper;
-import com.android.systemui.shared.condition.Condition;
-import com.android.systemui.shared.condition.Monitor;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
-@SmallTest
-public class SystemProcessConditionTest extends SysuiTestCase {
-    @Mock
-    ProcessWrapper mProcessWrapper;
-
-    @Mock
-    Monitor.Callback mCallback;
-
-    @Mock
-    CoroutineScope mScope;
-
-    private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    /**
-     * Verifies condition reports false when tracker reports the process is being ran by the
-     * system user.
-     */
-    @Test
-    public void testConditionFailsWithNonSystemProcess() {
-
-        final Condition condition = new SystemProcessCondition(mScope, mProcessWrapper);
-        when(mProcessWrapper.isSystemUser()).thenReturn(false);
-
-        final Monitor monitor = new Monitor(mExecutor);
-
-        monitor.addSubscription(new Monitor.Subscription.Builder(mCallback)
-                .addCondition(condition)
-                .build());
-
-        mExecutor.runAllReady();
-
-        verify(mCallback).onConditionsChanged(false);
-    }
-
-    /**
-     * Verifies condition reports true when tracker reports the process is being ran by the
-     * system user.
-     */
-    @Test
-    public void testConditionSucceedsWithSystemProcess() {
-
-        final Condition condition = new SystemProcessCondition(mScope, mProcessWrapper);
-        when(mProcessWrapper.isSystemUser()).thenReturn(true);
-
-        final Monitor monitor = new Monitor(mExecutor);
-
-        monitor.addSubscription(new Monitor.Subscription.Builder(mCallback)
-                .addCondition(condition)
-                .build());
-
-        mExecutor.runAllReady();
-
-        verify(mCallback).onConditionsChanged(true);
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/process/condition/SystemProcessConditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/process/condition/SystemProcessConditionTest.kt
new file mode 100644
index 0000000..21524d9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/process/condition/SystemProcessConditionTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.process.condition
+
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.process.ProcessWrapper
+import com.android.systemui.shared.condition.Condition
+import com.android.systemui.shared.condition.Monitor
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+@SmallTest
+class SystemProcessConditionTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+
+    @Mock private lateinit var processWrapper: ProcessWrapper
+
+    @Mock private lateinit var callback: Monitor.Callback
+
+    private val executor = FakeExecutor(FakeSystemClock())
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    /**
+     * Verifies condition reports false when tracker reports the process is being ran by the system
+     * user.
+     */
+    @Test
+    fun testConditionFailsWithNonSystemProcess() =
+        kosmos.runTest {
+            val condition: Condition = SystemProcessCondition(kosmos.testScope, processWrapper)
+            whenever(processWrapper.isSystemUser).thenReturn(false)
+
+            val monitor = Monitor(executor)
+
+            monitor.addSubscription(
+                Monitor.Subscription.Builder(callback).addCondition(condition).build()
+            )
+
+            executor.runAllReady()
+            runCurrent()
+            executor.runAllReady()
+
+            Mockito.verify(callback).onConditionsChanged(false)
+        }
+
+    /**
+     * Verifies condition reports true when tracker reports the process is being ran by the system
+     * user.
+     */
+    @Test
+    fun testConditionSucceedsWithSystemProcess() =
+        kosmos.runTest {
+            val condition: Condition = SystemProcessCondition(testScope, processWrapper)
+            whenever(processWrapper.isSystemUser).thenReturn(true)
+
+            val monitor = Monitor(executor)
+
+            monitor.addSubscription(
+                Monitor.Subscription.Builder(callback).addCondition(condition).build()
+            )
+
+            executor.runAllReady()
+            runCurrent()
+            executor.runAllReady()
+
+            Mockito.verify(callback).onConditionsChanged(true)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
index 0f63150..d9990ba 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
@@ -275,14 +275,14 @@
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
                 "abc/.def", /* validateActivity */ true, /* enableSetting */true,
                 /* enableOnLockScreen */ true);
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
                 UserHandle.USER_CURRENT);
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
                 UserHandle.USER_CURRENT);
 
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "1",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 1,
                 UserHandle.USER_CURRENT);
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "1",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 1,
                 UserHandle.USER_CURRENT);
         // Once from setup + twice from this function
         verify(mCallback, times(3)).onQRCodeScannerPreferenceChanged();
@@ -297,14 +297,14 @@
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isAbleToLaunchScannerActivity()).isTrue();
 
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
                 UserHandle.USER_CURRENT);
         verifyActivityDetails("abc/.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isAllowedOnLockScreen()).isTrue();
         assertThat(mController.isAbleToLaunchScannerActivity()).isTrue();
 
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "1",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 1,
                 UserHandle.USER_CURRENT);
         verifyActivityDetails("abc/.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
index 858ed6a..473d7b6d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/UserSettingObserverTest.kt
@@ -22,8 +22,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
@@ -64,7 +64,7 @@
         mSetFlagsRule.setFlagsParameterization(flags)
     }
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     private lateinit var testableLooper: TestableLooper
@@ -85,7 +85,7 @@
                     Handler(testableLooper.looper),
                     TEST_SETTING,
                     USER,
-                    DEFAULT_VALUE
+                    DEFAULT_VALUE,
                 ) {
                 override fun handleValueChanged(value: Int, observedChange: Boolean) {
                     callback(value, observedChange)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index e3fe24c..abecf33 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -46,12 +46,14 @@
 import com.android.systemui.statusbar.sysuiStatusBarStateController
 import com.android.systemui.util.animation.DisappearParameters
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.whenever
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
@@ -457,6 +459,20 @@
             }
         }
 
+    @Test
+    fun isEditing() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                underTest.containerViewModel.editModeViewModel.startEditing()
+                runCurrent()
+                assertThat(underTest.isEditing).isTrue()
+
+                underTest.containerViewModel.editModeViewModel.stopEditing()
+                runCurrent()
+                assertThat(underTest.isEditing).isFalse()
+            }
+        }
+
     private fun TestScope.setMediaState(state: MediaState) {
         with(kosmos) {
             val activeMedia = state == ACTIVE_MEDIA
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/ui/viewmodel/TileRequestDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/ui/viewmodel/TileRequestDialogViewModelTest.kt
index 3029928..cb13b11 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/ui/viewmodel/TileRequestDialogViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/ui/viewmodel/TileRequestDialogViewModelTest.kt
@@ -25,6 +25,7 @@
 import androidx.test.filters.SmallTest
 import com.android.app.iUriGrantsManager
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.ui.viewmodel.iconProvider
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.runCurrent
 import com.android.systemui.kosmos.runTest
@@ -32,6 +33,8 @@
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.external.TileData
+import com.android.systemui.qs.panels.ui.viewmodel.IconProvider
+import com.android.systemui.qs.panels.ui.viewmodel.toIconProvider
 import com.android.systemui.qs.panels.ui.viewmodel.toUiState
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
@@ -80,28 +83,32 @@
     @Test
     fun uiState_beforeActivation_hasDefaultIcon_andCorrectData() =
         kosmos.runTest {
-            val expectedState =
-                baseResultLegacyState.apply { icon = defaultIcon }.toUiState(mainResources)
+            val state = baseResultLegacyState.apply { icon = defaultIcon }
+
+            val expectedState = state.toUiState(mainResources)
+            val expectedIconProvider = state.toIconProvider()
 
             with(underTest.uiState) {
                 expect.that(label).isEqualTo(TEST_LABEL)
                 expect.that(secondaryLabel).isEmpty()
-                expect.that(state).isEqualTo(expectedState.state)
+                expect.that(this.state).isEqualTo(expectedState.state)
                 expect.that(handlesLongClick).isFalse()
                 expect.that(handlesSecondaryClick).isFalse()
-                expect.that(icon).isEqualTo(defaultIcon)
                 expect.that(sideDrawable).isNull()
                 expect.that(accessibilityUiState).isEqualTo(expectedState.accessibilityUiState)
             }
+
+            expect.that(underTest.iconProvider).isEqualTo(expectedIconProvider)
         }
 
     @Test
     fun uiState_afterActivation_hasCorrectIcon_andCorrectData() =
         kosmos.runTest {
-            val expectedState =
-                baseResultLegacyState
-                    .apply { icon = QSTileImpl.DrawableIcon(loadedDrawable) }
-                    .toUiState(mainResources)
+            val state =
+                baseResultLegacyState.apply { icon = QSTileImpl.DrawableIcon(loadedDrawable) }
+
+            val expectedState = state.toUiState(mainResources)
+            val expectedIconProvider = state.toIconProvider()
 
             underTest.activateIn(testScope)
             runCurrent()
@@ -109,13 +116,13 @@
             with(underTest.uiState) {
                 expect.that(label).isEqualTo(TEST_LABEL)
                 expect.that(secondaryLabel).isEmpty()
-                expect.that(state).isEqualTo(expectedState.state)
+                expect.that(this.state).isEqualTo(expectedState.state)
                 expect.that(handlesLongClick).isFalse()
                 expect.that(handlesSecondaryClick).isFalse()
-                expect.that(icon).isEqualTo(QSTileImpl.DrawableIcon(loadedDrawable))
                 expect.that(sideDrawable).isNull()
                 expect.that(accessibilityUiState).isEqualTo(expectedState.accessibilityUiState)
             }
+            expect.that(underTest.iconProvider).isEqualTo(expectedIconProvider)
         }
 
     @Test
@@ -135,7 +142,7 @@
             underTest.activateIn(testScope)
             runCurrent()
 
-            assertThat(underTest.uiState.icon).isEqualTo(defaultIcon)
+            assertThat(underTest.iconProvider).isEqualTo(IconProvider.ConstantIcon(defaultIcon))
         }
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
index 053a59a..5f8adf0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
@@ -32,12 +32,12 @@
 import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.shared.model.TileCategory
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.fakeQSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.qSTileConfigProvider
 import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
 import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
 import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.fakeQSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider
 import com.android.systemui.settings.userTracker
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -58,12 +58,7 @@
     private val batteryTileConfig = kosmos.qsBatterySaverTileConfig
 
     private val serviceInfo =
-        FakeInstalledTilesComponentRepository.ServiceInfo(
-            component,
-            tileName,
-            icon,
-            appName,
-        )
+        FakeInstalledTilesComponentRepository.ServiceInfo(component, tileName, icon, appName)
 
     private val underTest =
         with(kosmos) {
@@ -79,7 +74,7 @@
         with(kosmos) {
             fakeInstalledTilesRepository.setInstalledServicesForUser(
                 userTracker.userId,
-                listOf(serviceInfo)
+                listOf(serviceInfo),
             )
 
             with(fakeQSTileConfigProvider) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt
index 9d2528d..530ca95 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt
@@ -22,7 +22,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.AIRPLANE_MODE_TILE_SPEC
 import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.HOTSPOT_TILE_SPEC
 import com.android.systemui.statusbar.policy.PolicyModule.Companion.WORK_MODE_TILE_SPEC
@@ -38,61 +38,72 @@
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class NewTilesAvailabilityInteractorTest : SysuiTestCase() {
-    private val kosmos = testKosmos().apply {
-        tileAvailabilityInteractorsMap = buildMap {
-            put(AIRPLANE_MODE_TILE_SPEC, QSTileAvailabilityInteractor.AlwaysAvailableInteractor)
-            put(WORK_MODE_TILE_SPEC, FakeTileAvailabilityInteractor(
-                    mapOf(
-                       fakeUserRepository.getSelectedUserInfo().id to flowOf(true),
-                    ).withDefault { flowOf(false) }
-            ))
-            put(HOTSPOT_TILE_SPEC, FakeTileAvailabilityInteractor(
-                    emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
-            ))
+    private val kosmos =
+        testKosmos().apply {
+            tileAvailabilityInteractorsMap = buildMap {
+                put(AIRPLANE_MODE_TILE_SPEC, QSTileAvailabilityInteractor.AlwaysAvailableInteractor)
+                put(
+                    WORK_MODE_TILE_SPEC,
+                    FakeTileAvailabilityInteractor(
+                        mapOf(fakeUserRepository.getSelectedUserInfo().id to flowOf(true))
+                            .withDefault { flowOf(false) }
+                    ),
+                )
+                put(
+                    HOTSPOT_TILE_SPEC,
+                    FakeTileAvailabilityInteractor(
+                        emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
+                    ),
+                )
+            }
         }
-    }
 
     private val underTest by lazy { kosmos.newTilesAvailabilityInteractor }
 
     @Test
-    fun defaultUser_getAvailabilityFlow() = with(kosmos) {
-        testScope.runTest {
-            val availability by collectLastValue(underTest.newTilesAvailable)
+    fun defaultUser_getAvailabilityFlow() =
+        with(kosmos) {
+            testScope.runTest {
+                val availability by collectLastValue(underTest.newTilesAvailable)
 
-            assertThat(availability).isEqualTo(
-                    mapOf(
+                assertThat(availability)
+                    .isEqualTo(
+                        mapOf(
                             TileSpec.create(AIRPLANE_MODE_TILE_SPEC) to true,
                             TileSpec.create(WORK_MODE_TILE_SPEC) to true,
                             TileSpec.create(HOTSPOT_TILE_SPEC) to false,
+                        )
                     )
-            )
+            }
         }
-    }
 
     @Test
-    fun getAvailabilityFlow_userChange() = with(kosmos) {
-        testScope.runTest {
-            val availability by collectLastValue(underTest.newTilesAvailable)
-            fakeUserRepository.asMainUser()
+    fun getAvailabilityFlow_userChange() =
+        with(kosmos) {
+            testScope.runTest {
+                val availability by collectLastValue(underTest.newTilesAvailable)
+                fakeUserRepository.asMainUser()
 
-            assertThat(availability).isEqualTo(
-                    mapOf(
+                assertThat(availability)
+                    .isEqualTo(
+                        mapOf(
                             TileSpec.create(AIRPLANE_MODE_TILE_SPEC) to true,
                             TileSpec.create(WORK_MODE_TILE_SPEC) to false,
                             TileSpec.create(HOTSPOT_TILE_SPEC) to false,
+                        )
                     )
-            )
+            }
         }
-    }
 
     @Test
-    fun noAvailabilityInteractor_emptyMap() = with(kosmos) {
-        testScope.runTest {
-            tileAvailabilityInteractorsMap = emptyMap()
+    fun noAvailabilityInteractor_emptyMap() =
+        with(kosmos) {
+            testScope.runTest {
+                tileAvailabilityInteractorsMap = emptyMap()
 
-            val availability by collectLastValue(underTest.newTilesAvailable)
+                val availability by collectLastValue(underTest.newTilesAvailable)
 
-            assertThat(availability).isEmpty()
+                assertThat(availability).isEmpty()
+            }
         }
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt
index 67fb100..7743c2c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt
@@ -28,7 +28,7 @@
 import com.android.systemui.qs.FakeQSTile
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.qsTileFactory
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.AIRPLANE_MODE_TILE_SPEC
 import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.HOTSPOT_TILE_SPEC
 import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.INTERNET_TILE_SPEC
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt
index 68a591d..1d42424 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt
@@ -16,11 +16,9 @@
 
 package com.android.systemui.qs.panels.ui.viewmodel
 
-
 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.qs.FakeQSTile
 import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
@@ -48,45 +46,43 @@
     }
 
     @Test
-    fun changeTileDetailsViewModel() = with(kosmos) {
-        testScope.runTest {
-            val specs = listOf(
-                spec,
-                specNoDetails,
-            )
-            tileSpecRepository.setTiles(0, specs)
-            runCurrent()
+    fun changeTileDetailsViewModel() =
+        with(kosmos) {
+            testScope.runTest {
+                val specs = listOf(spec, specNoDetails)
+                tileSpecRepository.setTiles(0, specs)
+                runCurrent()
 
-            val tiles = currentTilesInteractor.currentTiles.value
+                val tiles = currentTilesInteractor.currentTiles.value
 
-            assertThat(currentTilesInteractor.currentTilesSpecs.size).isEqualTo(2)
-            assertThat(tiles[1].spec).isEqualTo(specNoDetails)
-            (tiles[1].tile as FakeQSTile).hasDetailsViewModel = false
+                assertThat(currentTilesInteractor.currentTilesSpecs.size).isEqualTo(2)
+                assertThat(tiles[1].spec).isEqualTo(specNoDetails)
+                (tiles[1].tile as FakeQSTile).hasDetailsViewModel = false
 
-            assertThat(underTest.activeTileDetails).isNull()
+                assertThat(underTest.activeTileDetails).isNull()
 
-            // Click on the tile who has the `spec`.
-            assertThat(underTest.onTileClicked(spec)).isTrue()
-            assertThat(underTest.activeTileDetails).isNotNull()
-            assertThat(underTest.activeTileDetails?.getTitle()).isEqualTo("internet")
+                // Click on the tile who has the `spec`.
+                assertThat(underTest.onTileClicked(spec)).isTrue()
+                assertThat(underTest.activeTileDetails).isNotNull()
+                assertThat(underTest.activeTileDetails?.title).isEqualTo("internet")
 
-            // Click on a tile who dose not have a valid spec.
-            assertThat(underTest.onTileClicked(null)).isFalse()
-            assertThat(underTest.activeTileDetails).isNull()
+                // Click on a tile who dose not have a valid spec.
+                assertThat(underTest.onTileClicked(null)).isFalse()
+                assertThat(underTest.activeTileDetails).isNull()
 
-            // Click again on the tile who has the `spec`.
-            assertThat(underTest.onTileClicked(spec)).isTrue()
-            assertThat(underTest.activeTileDetails).isNotNull()
-            assertThat(underTest.activeTileDetails?.getTitle()).isEqualTo("internet")
+                // Click again on the tile who has the `spec`.
+                assertThat(underTest.onTileClicked(spec)).isTrue()
+                assertThat(underTest.activeTileDetails).isNotNull()
+                assertThat(underTest.activeTileDetails?.title).isEqualTo("internet")
 
-            // Click on a tile who dose not have a detailed view.
-            assertThat(underTest.onTileClicked(specNoDetails)).isFalse()
-            assertThat(underTest.activeTileDetails).isNull()
+                // Click on a tile who dose not have a detailed view.
+                assertThat(underTest.onTileClicked(specNoDetails)).isFalse()
+                assertThat(underTest.activeTileDetails).isNull()
 
-            underTest.closeDetailedView()
-            assertThat(underTest.activeTileDetails).isNull()
+                underTest.closeDetailedView()
+                assertThat(underTest.activeTileDetails).isNull()
 
-            assertThat(underTest.onTileClicked(null)).isFalse()
+                assertThat(underTest.onTileClicked(null)).isFalse()
+            }
         }
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
index 50229eb..71ab0a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
@@ -49,6 +49,9 @@
 import com.android.systemui.qs.pipeline.shared.metricSpec
 import com.android.systemui.qs.qsTileFactory
 import com.android.systemui.qs.shared.model.TileCategory
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.fakeQSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.qSTileConfigProvider
 import com.android.systemui.qs.tiles.impl.airplane.qsAirplaneModeTileConfig
 import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
 import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
@@ -56,9 +59,6 @@
 import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
 import com.android.systemui.qs.tiles.impl.sensorprivacy.qsCameraSensorPrivacyToggleTileConfig
 import com.android.systemui.qs.tiles.impl.sensorprivacy.qsMicrophoneSensorPrivacyToggleTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.fakeQSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider
 import com.android.systemui.settings.userTracker
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/IconProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/IconProviderTest.kt
new file mode 100644
index 0000000..7257a89
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/IconProviderTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import android.graphics.drawable.TestStubDrawable
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import java.util.function.Supplier
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class IconProviderTest : SysuiTestCase() {
+
+    @Test
+    fun iconAndSupplier_prefersIcon() {
+        val state =
+            QSTile.State().apply {
+                icon = ResourceIcon.get(R.drawable.android)
+                iconSupplier = Supplier { QSTileImpl.DrawableIcon(TestStubDrawable()) }
+            }
+        val iconProvider = state.toIconProvider()
+
+        assertThat(iconProvider).isEqualTo(IconProvider.ConstantIcon(state.icon))
+    }
+
+    @Test
+    fun iconOnly_hasIcon() {
+        val state = QSTile.State().apply { icon = ResourceIcon.get(R.drawable.android) }
+        val iconProvider = state.toIconProvider()
+
+        assertThat(iconProvider).isEqualTo(IconProvider.ConstantIcon(state.icon))
+    }
+
+    @Test
+    fun supplierOnly_hasIcon() {
+        val state =
+            QSTile.State().apply {
+                iconSupplier = Supplier { ResourceIcon.get(R.drawable.android) }
+            }
+        val iconProvider = state.toIconProvider()
+
+        assertThat(iconProvider).isEqualTo(IconProvider.IconSupplier(state.iconSupplier))
+    }
+
+    @Test
+    fun noIconOrSupplier_iconNull() {
+        val state = QSTile.State()
+        val iconProvider = state.toIconProvider()
+
+        assertThat(iconProvider).isEqualTo(IconProvider.Empty)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt
index 9c8e322..b144f06 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt
@@ -18,7 +18,6 @@
 
 import android.content.res.Resources
 import android.content.res.mainResources
-import android.graphics.drawable.TestStubDrawable
 import android.service.quicksettings.Tile
 import android.widget.Button
 import android.widget.Switch
@@ -27,12 +26,9 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
-import java.util.function.Supplier
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -267,45 +263,6 @@
             .contains(resources.getString(R.string.tile_unavailable))
     }
 
-    @Test
-    fun iconAndSupplier_prefersIcon() {
-        val state =
-            QSTile.State().apply {
-                icon = ResourceIcon.get(R.drawable.android)
-                iconSupplier = Supplier { QSTileImpl.DrawableIcon(TestStubDrawable()) }
-            }
-        val uiState = state.toUiState()
-
-        assertThat(uiState.icon).isEqualTo(state.icon)
-    }
-
-    @Test
-    fun iconOnly_hasIcon() {
-        val state = QSTile.State().apply { icon = ResourceIcon.get(R.drawable.android) }
-        val uiState = state.toUiState()
-
-        assertThat(uiState.icon).isEqualTo(state.icon)
-    }
-
-    @Test
-    fun supplierOnly_hasIcon() {
-        val state =
-            QSTile.State().apply {
-                iconSupplier = Supplier { ResourceIcon.get(R.drawable.android) }
-            }
-        val uiState = state.toUiState()
-
-        assertThat(uiState.icon).isEqualTo(state.iconSupplier.get())
-    }
-
-    @Test
-    fun noIconOrSupplier_iconNull() {
-        val state = QSTile.State()
-        val uiState = state.toUiState()
-
-        assertThat(uiState.icon).isNull()
-    }
-
     private fun QSTile.State.toUiState() = toUiState(resources)
 }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index 0049042..a2829b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.pipeline.data.model.RestoreData
 import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
 import com.android.systemui.qs.pipeline.data.model.workTileRestoreProcessor
@@ -36,6 +35,7 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.WorkModeTile
 import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -47,7 +47,7 @@
 @RunWith(AndroidJUnit4::class)
 class WorkTileAutoAddableTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     private val restoreProcessor: RestoreProcessor
         get() = kosmos.workTileRestoreProcessor
@@ -62,7 +62,7 @@
             FakeUserTracker(
                 _userId = USER_INFO_0.id,
                 _userInfo = USER_INFO_0,
-                _userProfiles = listOf(USER_INFO_0)
+                _userProfiles = listOf(USER_INFO_0),
             )
 
         underTest = WorkTileAutoAddable(userTracker, kosmos.workTileRestoreProcessor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index c308976..12f1e66 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -49,7 +49,7 @@
 import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
 import com.android.systemui.qs.pipeline.shared.logging.qsLogger
 import com.android.systemui.qs.qsTileFactory
-import com.android.systemui.qs.tiles.di.newQSTileFactory
+import com.android.systemui.qs.tiles.base.ui.model.newQSTileFactory
 import com.android.systemui.qs.toProto
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.testKosmos
@@ -691,11 +691,11 @@
                 var currentModel: TileDetailsViewModel? = null
                 val setCurrentModel = { model: TileDetailsViewModel? -> currentModel = model }
                 tiles!![0].tile.getDetailsViewModel(setCurrentModel)
-                assertThat(currentModel?.getTitle()).isEqualTo("a")
+                assertThat(currentModel?.title).isEqualTo("a")
 
                 currentModel = null
                 tiles!![1].tile.getDetailsViewModel(setCurrentModel)
-                assertThat(currentModel?.getTitle()).isEqualTo("b")
+                assertThat(currentModel?.title).isEqualTo("b")
 
                 currentModel = null
                 tiles!![2].tile.getDetailsViewModel(setCurrentModel)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
index 3a9c3d0..4e0adca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.MediumTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.FakeQSFactory
@@ -39,6 +38,7 @@
 import com.android.systemui.qs.qsTileFactory
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -55,22 +55,12 @@
 @RunWith(AndroidJUnit4::class)
 class NoLowNumberOfTilesTest : SysuiTestCase() {
 
-    private val USER_0_INFO =
-        UserInfo(
-            0,
-            "zero",
-            "",
-            UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
-        )
+    private val USER_0_INFO = UserInfo(0, "zero", "", UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL)
 
-    private val defaultTiles =
-        listOf(
-            TileSpec.create("internet"),
-            TileSpec.create("bt"),
-        )
+    private val defaultTiles = listOf(TileSpec.create("internet"), TileSpec.create("bt"))
 
     private val kosmos =
-        Kosmos().apply {
+        testKosmos().apply {
             fakeMinimumTilesRepository = MinimumTilesFixedRepository(minNumberOfTiles = 2)
             fakeUserTracker.set(listOf(USER_0_INFO), 0)
             qsTileFactory = FakeQSFactory(::tileCreator)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
index 9d9bfda..77030ac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.MediumTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.FakeQSFactory
@@ -34,6 +33,7 @@
 import com.android.systemui.qs.qsTileFactory
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -52,7 +52,9 @@
 @RunWith(AndroidJUnit4::class)
 class WorkProfileAutoAddedAfterRestoreTest : SysuiTestCase() {
 
-    private val kosmos by lazy { Kosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) } }
+    private val kosmos by lazy {
+        testKosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) }
+    }
     // Getter here so it can change when there is a managed profile.
     private val workTileAvailable: Boolean
         get() = hasManagedProfile()
@@ -143,30 +145,15 @@
     }
 
     private fun TestScope.createManagedProfileAndAdd() {
-        kosmos.fakeUserTracker.set(
-            listOf(USER_0_INFO, MANAGED_USER_INFO),
-            0,
-        )
+        kosmos.fakeUserTracker.set(listOf(USER_0_INFO, MANAGED_USER_INFO), 0)
         runCurrent()
     }
 
     private companion object {
         val WORK_TILE_SPEC = "work".toTileSpec()
-        val USER_0_INFO =
-            UserInfo(
-                0,
-                "zero",
-                "",
-                UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
-            )
+        val USER_0_INFO = UserInfo(0, "zero", "", UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL)
         val MANAGED_USER_INFO =
-            UserInfo(
-                10,
-                "ten-managed",
-                "",
-                0,
-                UserManager.USER_TYPE_PROFILE_MANAGED,
-            )
+            UserInfo(10, "ten-managed", "", 0, UserManager.USER_TYPE_PROFILE_MANAGED)
 
         fun String.toTileSpec() = TileSpec.create(this)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesDndTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesDndTileTest.kt
new file mode 100644
index 0000000..6e5a700
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesDndTileTest.kt
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.app.Flags
+import android.os.Handler
+import android.platform.test.annotations.EnableFlags
+import android.service.quicksettings.Tile
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.kosmos.mainCoroutineContext
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile.BooleanState
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileDataInteractor
+import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
+import com.android.systemui.qs.tiles.impl.modes.ui.ModesDndTileMapper
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
+import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.SecureSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
+
+@EnableFlags(Flags.FLAG_MODES_UI)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper(setAsMainLooper = true)
+class ModesDndTileTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val testDispatcher = kosmos.testDispatcher
+
+    @Mock private lateinit var qsHost: QSHost
+
+    @Mock private lateinit var metricsLogger: MetricsLogger
+
+    @Mock private lateinit var statusBarStateController: StatusBarStateController
+
+    @Mock private lateinit var activityStarter: ActivityStarter
+
+    @Mock private lateinit var qsLogger: QSLogger
+
+    @Mock private lateinit var uiEventLogger: QsEventLogger
+
+    @Mock private lateinit var qsTileConfigProvider: QSTileConfigProvider
+
+    @Mock private lateinit var dialogDelegate: ModesDialogDelegate
+
+    @Mock private lateinit var settingsPackageRepository: QSSettingsPackageRepository
+
+    private val inputHandler = FakeQSTileIntentUserInputHandler()
+    private val zenModeRepository = kosmos.zenModeRepository
+    private val tileDataInteractor =
+        ModesDndTileDataInteractor(context, kosmos.zenModeInteractor, testDispatcher)
+    private val mapper = ModesDndTileMapper(context.resources, context.theme)
+
+    private lateinit var userActionInteractor: ModesDndTileUserActionInteractor
+    private lateinit var secureSettings: SecureSettings
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var underTest: ModesDndTile
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        secureSettings = FakeSettings()
+
+        // Allow the tile to load resources
+        whenever(qsHost.context).thenReturn(context)
+        whenever(qsHost.userContext).thenReturn(context)
+
+        whenever(qsTileConfigProvider.getConfig(any()))
+            .thenReturn(
+                QSTileConfigTestBuilder.build {
+                    uiConfig =
+                        QSTileUIConfig.Resource(
+                            iconRes = R.drawable.qs_dnd_icon_off,
+                            labelRes = R.string.quick_settings_dnd_label,
+                        )
+                }
+            )
+
+        userActionInteractor =
+            ModesDndTileUserActionInteractor(
+                kosmos.mainCoroutineContext,
+                inputHandler,
+                dialogDelegate,
+                kosmos.zenModeInteractor,
+                kosmos.modesDialogEventLogger,
+                settingsPackageRepository,
+            )
+
+        underTest =
+            ModesDndTile(
+                qsHost,
+                uiEventLogger,
+                testableLooper.looper,
+                Handler(testableLooper.looper),
+                FalsingManagerFake(),
+                metricsLogger,
+                statusBarStateController,
+                activityStarter,
+                qsLogger,
+                qsTileConfigProvider,
+                tileDataInteractor,
+                mapper,
+                userActionInteractor,
+            )
+
+        underTest.initialize()
+        underTest.setListening(Object(), true)
+
+        testableLooper.processAllMessages()
+    }
+
+    @After
+    fun tearDown() {
+        underTest.destroy()
+        testableLooper.processAllMessages()
+    }
+
+    @Test
+    fun stateUpdatesOnChange() =
+        testScope.runTest {
+            assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
+
+            zenModeRepository.activateMode(TestModeBuilder.MANUAL_DND)
+            runCurrent()
+            testableLooper.processAllMessages()
+
+            assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
+        }
+
+    @Test
+    fun handleUpdateState_withModel_updatesState() =
+        testScope.runTest {
+            val tileState =
+                BooleanState().apply {
+                    state = Tile.STATE_INACTIVE
+                    secondaryLabel = "Old secondary label"
+                }
+            val model = ModesDndTileModel(isActivated = true, extraStatus = "Until 14:30")
+
+            underTest.handleUpdateState(tileState, model)
+
+            assertThat(tileState.state).isEqualTo(Tile.STATE_ACTIVE)
+            assertThat(tileState.secondaryLabel).isEqualTo("Until 14:30")
+        }
+
+    @Test
+    fun handleUpdateState_withNull_updatesState() =
+        testScope.runTest {
+            val tileState =
+                BooleanState().apply {
+                    state = Tile.STATE_INACTIVE
+                    secondaryLabel = "Old secondary label"
+                }
+            zenModeRepository.activateMode(TestModeBuilder.MANUAL_DND)
+            runCurrent()
+
+            underTest.handleUpdateState(tileState, null)
+
+            assertThat(tileState.state).isEqualTo(Tile.STATE_ACTIVE)
+            assertThat(tileState.secondaryLabel).isEqualTo(null) // Tile will use default On text
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index bbc0dbc..c218a1b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -37,14 +37,14 @@
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.impl.modes.ui.ModesTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.impl.modes.ui.mapper.ModesTileMapper
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
 import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerTest.kt
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerTest.kt
index 02a8141..9444963 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.qs.tiles.base.domain.actions
 
 import android.app.PendingIntent
 import android.content.ComponentName
@@ -119,7 +119,7 @@
             .postStartActivityDismissingKeyguard(
                 argThat(IntentMatcher(expectedIntent)),
                 eq(0),
-                any()
+                any(),
             )
     }
 
@@ -149,7 +149,7 @@
                 packageManager.queryIntentActivitiesAsUser(
                     any(Intent::class.java),
                     any(ResolveInfoFlags::class.java),
-                    eq(user.identifier)
+                    eq(user.identifier),
                 )
             )
             .thenReturn(infos.map { ResolveInfo().apply { activityInfo = it } })
@@ -157,6 +157,7 @@
 
     private class IntentMatcher(intent: Intent) : ArgumentMatcher<Intent> {
         private val expectedIntent = intent
+
         override fun matches(argument: Intent?): Boolean {
             return argument?.action.equals(expectedIntent.action) &&
                 argument?.`package`.equals(expectedIntent.`package`) &&
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorTest.kt
similarity index 97%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorTest.kt
index c0e5a9b..5c2edc2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
 
 import android.content.ComponentName
 import android.content.Context
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLoggerTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLoggerTest.kt
index 6a33b5f..ec88f71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLoggerTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.logging
+package com.android.systemui.qs.tiles.base.shared.logging
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -26,8 +26,8 @@
 import com.android.systemui.log.LogcatEchoTrackerAlways
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderTest.kt
similarity index 89%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderTest.kt
index 40971a8..fd83895 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
 
 import android.platform.test.annotations.EnabledOnRavenwood
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -34,7 +34,7 @@
 
     private val underTest =
         createQSTileConfigProviderImpl(
-            mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC }),
+            mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC })
         )
 
     @Test
@@ -66,11 +66,7 @@
 
     private fun createQSTileConfigProviderImpl(
         configs: Map<String, QSTileConfig>
-    ): QSTileConfigProviderImpl =
-        QSTileConfigProviderImpl(
-            configs,
-            mock<QsEventLogger>(),
-        )
+    ): QSTileConfigProviderImpl = QSTileConfigProviderImpl(configs, mock<QsEventLogger>())
 
     private companion object {
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsTest.kt
similarity index 85%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsTest.kt
index fd09e3c..ee0f5cd5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.analytics
+package com.android.systemui.qs.tiles.base.ui.analytics
 
 import android.platform.test.annotations.EnabledOnRavenwood
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -23,8 +23,8 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.qs.QSEvent
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -57,7 +57,7 @@
                 eq(QSEvent.QS_ACTION_CLICK),
                 eq(0),
                 eq("test_spec"),
-                eq(InstanceId.fakeInstanceId(0))
+                eq(InstanceId.fakeInstanceId(0)),
             )
     }
 
@@ -70,7 +70,7 @@
                 eq(QSEvent.QS_ACTION_LONG_PRESS),
                 eq(0),
                 eq("test_spec"),
-                eq(InstanceId.fakeInstanceId(0))
+                eq(InstanceId.fakeInstanceId(0)),
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImplTest.kt
similarity index 84%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImplTest.kt
index da3cebd..4ebd22a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImplTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -23,16 +23,16 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.qs.FakeTileDetailsViewModel
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelTest.kt
similarity index 87%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelTest.kt
index 4e9b635..d2f8b07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
 
 import android.os.UserHandle
 import android.platform.test.annotations.EnabledOnRavenwood
@@ -26,14 +26,17 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.qs.FakeTileDetailsViewModel
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelUserInputTest.kt
similarity index 85%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelUserInputTest.kt
index 166e950..0f939a95 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelUserInputTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -23,16 +23,21 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.qs.FakeTileDetailsViewModel
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor.Companion.DISABLED_RESTRICTION
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor.Companion.ENABLED_RESTRICTION
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor.Companion.DISABLED_RESTRICTION
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor.Companion.DISABLED_RESTRICTION_2
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor.Companion.ENABLED_RESTRICTION
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.base.ui.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
@@ -171,10 +176,7 @@
                 QSTileConfigTestBuilder.build {
                     policy =
                         QSTilePolicy.Restricted(
-                            listOf(
-                                DISABLED_RESTRICTION,
-                                FakeDisabledByPolicyInteractor.DISABLED_RESTRICTION_2,
-                            )
+                            listOf(DISABLED_RESTRICTION, DISABLED_RESTRICTION_2)
                         )
                 }
 
@@ -199,7 +201,7 @@
                 .logUserActionRejectedByPolicy(
                     eq(userAction),
                     eq(tileConfig.tileSpec),
-                    eq(FakeDisabledByPolicyInteractor.DISABLED_RESTRICTION_2),
+                    eq(DISABLED_RESTRICTION_2),
                 )
             verify(qsTileAnalytics, never()).trackUserAction(any(), any())
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractorTest.kt
index fda75ca..78756d6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractorTest.kt
@@ -22,7 +22,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
index d27e810..ce6bcdf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
@@ -22,10 +22,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject.Companion.assertThat
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.longClick
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.longClick
 import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapperTest.kt
similarity index 86%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapperTest.kt
index 557f4ea..ccb2386 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl
+package com.android.systemui.qs.tiles.impl.airplane.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.service.quicksettings.Tile
@@ -23,29 +23,28 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.airplane.domain.AirplaneModeMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
 import com.android.systemui.qs.tiles.impl.airplane.qsAirplaneModeTileConfig
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class AirplaneModeMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+class AirplaneModeTileMapperTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
     private val airplaneModeConfig = kosmos.qsAirplaneModeTileConfig
 
-    private lateinit var mapper: AirplaneModeMapper
+    private lateinit var mapper: AirplaneModeTileMapper
 
     @Before
     fun setup() {
         mapper =
-            AirplaneModeMapper(
+            AirplaneModeTileMapper(
                 context.orCreateTestableResources
                     .apply {
                         addOverride(R.drawable.qs_airplane_icon_off, TestStubDrawable())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractorTest.kt
index afbc3e8..5fb0ea6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractorTest.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
index be2da17..e7dd145 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
@@ -22,9 +22,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
 import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapperTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapperTest.kt
index 24e4668..9b1d0a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.alarm.domain
+package com.android.systemui.qs.tiles.impl.alarm.ui.mapper
 
 import android.app.AlarmManager
 import android.graphics.drawable.TestStubDrawable
@@ -23,12 +23,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
 import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.android.systemui.util.time.FakeSystemClock
 import java.time.Instant
 import java.time.LocalDateTime
@@ -41,7 +41,7 @@
 @RunWith(AndroidJUnit4::class)
 class AlarmTileMapperTest : SysuiTestCase() {
     private val oneMinute = 60000L
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val alarmTileConfig = kosmos.qsAlarmTileConfig
     private val fakeClock = FakeSystemClock()
     // Using lazy (versus =) to make sure we override the right context -- see b/311612168
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
index 44c175a..8d3f8d4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
@@ -23,10 +23,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileDataInteractor
+import com.android.systemui.testKosmos
 import com.android.systemui.utils.leaks.FakeBatteryController
 import com.google.common.truth.Truth
 import kotlinx.coroutines.flow.flowOf
@@ -40,7 +40,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class BatterySaverTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val batteryController = FakeBatteryController(LeakCheck())
     private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileUserActionInteractorTest.kt
index 62c51e6..68a4d41 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileUserActionInteractorTest.kt
@@ -22,9 +22,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
 import com.android.systemui.utils.leaks.FakeBatteryController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapperTest.kt
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapperTest.kt
index 2ddaddd..8128c0c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.battery.ui
+package com.android.systemui.qs.tiles.impl.battery.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.widget.Switch
@@ -22,12 +22,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
 import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -35,7 +35,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class BatterySaverTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val batterySaverTileConfig = kosmos.qsBatterySaverTileConfig
     private lateinit var mapper: BatterySaverTileMapper
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt
index 4c156b7..4819539 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
index 0cf3734..90533f5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
@@ -24,9 +24,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository
 import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapperTest.kt
similarity index 90%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapperTest.kt
index a3c159820..124e013 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.colorcorrection.domain
+package com.android.systemui.qs.tiles.impl.colorcorrection.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.widget.Switch
@@ -22,19 +22,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
 import com.android.systemui.qs.tiles.impl.colorcorrection.qsColorCorrectionTileConfig
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ColorCorrectionTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val colorCorrectionTileConfig = kosmos.qsColorCorrectionTileConfig
     private val subtitleArray by lazy {
         context.resources.getStringArray(R.array.tile_states_color_correction)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepositoryTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepositoryTest.kt
index 10530a2..80e865d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepositoryTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom
+package com.android.systemui.qs.tiles.impl.custom.data.repository
 
 import android.content.ComponentName
 import android.content.Context
@@ -25,9 +25,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
@@ -199,6 +197,7 @@
             appInfoIcon = APP_INFO_ICON_1,
             isSystemApp = isSystemApp,
         )
+
     private fun PackageManager.setupApp2(isSystemApp: Boolean = false) =
         setupApp(
             componentName = COMPONENT_NAME_2,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepositoryTest.kt
similarity index 92%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepositoryTest.kt
index 835dba2..93dcf7d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepositoryTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom
+package com.android.systemui.qs.tiles.impl.custom.data.repository
 
 import android.annotation.SuppressLint
 import android.content.BroadcastReceiver
@@ -28,8 +28,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
@@ -111,7 +109,7 @@
                     any(),
                     any(),
                     nullable(),
-                    nullable()
+                    nullable(),
                 )
             listenerCaptor.value.onReceive(mockedContext, Intent(Intent.ACTION_MAIN))
             runCurrent()
@@ -144,9 +142,9 @@
                 type = IntentFilter.SCHEME_PACKAGE
                 putExtra(
                     Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST,
-                    arrayOf(componentName.packageName)
+                    arrayOf(componentName.packageName),
                 )
-            }
+            },
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
index 83e61d1..f8489c1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
@@ -24,16 +24,16 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.external.TileServiceKey
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
-import com.android.systemui.qs.tiles.impl.custom.commons.copy
 import com.android.systemui.qs.tiles.impl.custom.customTileSpec
 import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
 import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade
+import com.android.systemui.qs.tiles.impl.custom.shared.model.copy
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.test.runCurrent
@@ -45,7 +45,7 @@
 @RunWith(AndroidJUnit4::class)
 class CustomTileRepositoryTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos().apply { customTileSpec = TileSpec.create(TEST_COMPONENT) }
+    private val kosmos = testKosmos().apply { customTileSpec = TileSpec.create(TEST_COMPONENT) }
     private val underTest: CustomTileRepository =
         with(kosmos) {
             CustomTileRepositoryImpl(
@@ -213,7 +213,7 @@
                 underTest.updateWithTile(
                     TEST_USER_1,
                     Tile().apply { subtitle = "test_subtitle" },
-                    true
+                    true,
                 )
                 runCurrent()
 
@@ -247,7 +247,7 @@
                 underTest.updateWithTile(
                     TEST_USER_1,
                     Tile().apply { subtitle = "test_subtitle" },
-                    true
+                    true,
                 )
                 runCurrent()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
index 2cc3678..8e10649 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.qs.external.tileServiceManagerFacade
 import com.android.systemui.qs.external.tileServicesFacade
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
 import com.android.systemui.qs.tiles.impl.custom.customTileDefaultsRepository
 import com.android.systemui.qs.tiles.impl.custom.customTileInteractor
@@ -39,7 +39,7 @@
 import com.android.systemui.qs.tiles.impl.custom.customTileRepository
 import com.android.systemui.qs.tiles.impl.custom.customTileServiceInteractor
 import com.android.systemui.qs.tiles.impl.custom.customTileSpec
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
 import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
index a317dc5..6fef9f1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
@@ -33,7 +33,7 @@
 import com.android.systemui.qs.tiles.impl.custom.customTileRepository
 import com.android.systemui.qs.tiles.impl.custom.customTileSpec
 import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.first
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt
index 72e5766..df4f097 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt
@@ -38,14 +38,14 @@
 import com.android.systemui.qs.external.componentName
 import com.android.systemui.qs.external.iQSTileService
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.actions.pendingIntentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.longClick
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.actions.pendingIntentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.longClick
 import com.android.systemui.qs.tiles.impl.custom.customTileServiceInteractor
 import com.android.systemui.qs.tiles.impl.custom.customTileSpec
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
 import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
@@ -144,7 +144,7 @@
                     assertThat(
                             intent.getParcelableExtra(
                                 Intent.EXTRA_COMPONENT_NAME,
-                                ComponentName::class.java
+                                ComponentName::class.java,
                             )
                         )
                         .isEqualTo(componentName)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapperTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapperTest.kt
index 608adf1..41f6872 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.domain.interactor
+package com.android.systemui.qs.tiles.impl.custom.ui.mapper
 
 import android.app.IUriGrantsManager
 import android.content.ComponentName
@@ -32,12 +32,11 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject.Companion.assertThat
 import com.android.systemui.qs.tiles.impl.custom.customTileQsTileConfig
 import com.android.systemui.qs.tiles.impl.custom.customTileSpec
-import com.android.systemui.qs.tiles.impl.custom.domain.CustomTileMapper
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
index d42eb5e..5df7ae3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
 import com.android.systemui.utils.leaks.FakeFlashlightController
 import com.google.common.truth.Truth.assertThat
@@ -57,6 +57,7 @@
 
         assertThat(availability).isTrue()
     }
+
     @Test
     fun availabilityOffMatchesController() = runTest {
         controller.hasFlashlight = false
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
index 1f19c98..d7536dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
@@ -20,7 +20,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
 import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
 import com.android.systemui.statusbar.policy.FlashlightController
 import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapperTest.kt
similarity index 94%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapperTest.kt
index a115c12..aebc67b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,18 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.flashlight.domain
+package com.android.systemui.qs.tiles.impl.flashlight.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
 import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
 import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertEquals
 import org.junit.Test
@@ -34,7 +34,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class FlashlightMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsFlashlightTileConfig
     private val mapper by lazy {
         FlashlightMapper(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt
index cd82504..585c1a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt
@@ -22,7 +22,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.google.common.truth.Truth
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
index 9bd4895..11b9f77 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
@@ -26,17 +26,17 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.LaunchableView
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
 import com.android.systemui.statusbar.phone.FakeKeyguardStateController
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -67,7 +67,7 @@
 
     @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val scope = kosmos.testScope
     private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
     private val keyguardStateController = FakeKeyguardStateController()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapperTest.kt
similarity index 88%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapperTest.kt
index 8f8f710..1e794f9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.fontscaling.domain
+package com.android.systemui.qs.tiles.impl.fontscaling.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.widget.Switch
@@ -22,19 +22,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
 import com.android.systemui.qs.tiles.impl.fontscaling.qsFontScalingTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class FontScalingTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val fontScalingTileConfig = kosmos.qsFontScalingTileConfig
 
     private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
index 4b9d11d..ae37684 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -24,11 +24,11 @@
 import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
 import com.android.systemui.statusbar.policy.fakeBluetoothController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
@@ -46,7 +46,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val testUser = UserHandle.of(1)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
index 00ee1c3..5ab728f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
@@ -22,12 +22,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
 import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -40,12 +41,13 @@
 import org.mockito.junit.MockitoRule
 import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val inputHandler = FakeQSTileIntentUserInputHandler()
 
@@ -53,14 +55,18 @@
 
     @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
     @Mock private lateinit var dialogManager: HearingDevicesDialogManager
+    @Mock private lateinit var settingsPackageRepository: QSSettingsPackageRepository
 
     @Before
     fun setUp() {
+        whenever(settingsPackageRepository.getSettingsPackageName())
+            .thenReturn(SETTINGS_PACKAGE_NAME)
         underTest =
             HearingDevicesTileUserActionInteractor(
                 testScope.coroutineContext,
                 inputHandler,
                 dialogManager,
+                settingsPackageRepository,
             )
     }
 
@@ -91,6 +97,11 @@
 
             QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
                 assertThat(it.intent.action).isEqualTo(Settings.ACTION_HEARING_DEVICES_SETTINGS)
+                assertThat(it.intent.`package`).isEqualTo(SETTINGS_PACKAGE_NAME)
             }
         }
+
+    companion object {
+        private const val SETTINGS_PACKAGE_NAME = "com.android.settings"
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapperTest.kt
similarity index 92%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapperTest.kt
index 3d3447d..b0129f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.qs.tiles.impl.hearingdevices.domain
+package com.android.systemui.qs.tiles.impl.hearingdevices.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.widget.Switch
@@ -21,19 +21,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
 import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class HearingDevicesTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsHearingDevicesTileConfig
     private val mapper by lazy {
         HearingDevicesTileMapper(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
index 63607f1..a8747e8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,10 +29,9 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.table.logcatTableLogBuffer
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.connectivity.WifiIcons
@@ -54,6 +53,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -67,7 +67,7 @@
 @RunWith(AndroidJUnit4::class)
 class InternetTileDataInteractorTest : SysuiTestCase() {
     private val testUser = UserHandle.of(1)
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     private lateinit var underTest: InternetTileDataInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
index 261e3de..8b58e56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,14 +21,14 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.statusbar.connectivity.AccessPointController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.nullable
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -44,7 +44,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class InternetTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val inputHandler = FakeQSTileIntentUserInputHandler()
 
     private lateinit var underTest: InternetTileUserActionInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapperTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapperTest.kt
index 54a653d..da91f70 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.internet.domain
+package com.android.systemui.qs.tiles.impl.internet.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.os.fakeExecutorHandler
@@ -28,23 +28,23 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.shared.model.Text.Companion.loadText
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
 import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class InternetTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val internetTileConfig = kosmos.qsInternetTileConfig
     private val handler = kosmos.fakeExecutorHandler
     private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt
index 228e993..70828d5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.FakeColorInversionRepository
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt
index 3f77b86..e4a1ce2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt
@@ -24,9 +24,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.FakeColorInversionRepository
 import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapperTest.kt
similarity index 91%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapperTest.kt
index 780d58c..509a178 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.inversion.domain
+package com.android.systemui.qs.tiles.impl.inversion.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.widget.Switch
@@ -22,20 +22,20 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
 import com.android.systemui.qs.tiles.impl.inversion.qsColorInversionTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ColorInversionTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val colorInversionTileConfig = kosmos.qsColorInversionTileConfig
     private val subtitleArrayId =
         SubtitleArrayMapping.getSubtitleId(colorInversionTileConfig.tileSpec.spec)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractorTest.kt
similarity index 90%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractorTest.kt
index 7562ac0..1ee6ac6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractorTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.domain.interactor
 
 import android.os.Handler
 import android.os.UserHandle
@@ -22,13 +22,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.recordissue.IssueRecordingState
 import com.android.systemui.settings.fakeUserFileManager
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth
 import kotlinx.coroutines.flow.flowOf
@@ -42,7 +42,7 @@
 @RunWith(AndroidJUnit4::class)
 class IssueRecordingDataInteractorTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val userTracker = kosmos.userTracker
     private val userFileManager = kosmos.fakeUserFileManager
     private val testUser = UserHandle.of(1)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractorTest.kt
similarity index 91%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractorTest.kt
index 9c2be89..2791b74 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractorTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.domain.interactor
 
 import android.os.Handler
 import android.os.UserHandle
@@ -22,15 +22,15 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.dialogTransitionAnimator
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.activityStarter
 import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.qs.pipeline.domain.interactor.panelInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
 import com.android.systemui.recordissue.IssueRecordingState
 import com.android.systemui.recordissue.RecordIssueDialogDelegate
 import com.android.systemui.screenrecord.RecordingController
@@ -39,6 +39,7 @@
 import com.android.systemui.settings.userTracker
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth
 import kotlinx.coroutines.test.runTest
@@ -56,7 +57,7 @@
     @Mock private lateinit var recordingController: RecordingController
 
     val user = UserHandle(1)
-    val kosmos = Kosmos().also { it.testCase = this }
+    val kosmos = testKosmos().also { it.testCase = this }
 
     private lateinit var userContextProvider: UserContextProvider
     private lateinit var underTest: IssueRecordingUserActionInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapperTest.kt
similarity index 81%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapperTest.kt
index fa6d8bf..e469330 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,22 +14,23 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.ui.mapper
 
 import android.content.res.mainResources
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.qsEventLogger
 import com.android.systemui.qs.shared.model.TileCategory
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
 import com.android.systemui.recordissue.RecordIssueModule
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,7 +38,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class IssueRecordingMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val uiConfig =
         QSTileUIConfig.Resource(R.drawable.qs_record_issue_icon_off, R.string.qs_record_issue_label)
     private val config =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractorTest.kt
similarity index 90%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractorTest.kt
index 52ce95b..9c52714 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractorTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.location.interactor
+package com.android.systemui.qs.tiles.impl.location.domain.interactor
 
 import android.os.UserHandle
 import android.platform.test.annotations.EnabledOnRavenwood
@@ -24,8 +24,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
 import com.android.systemui.utils.leaks.FakeLocationController
 import com.google.common.truth.Truth
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractorTest.kt
similarity index 85%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractorTest.kt
index 8b21cb4..bd63253 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractorTest.kt
@@ -14,24 +14,23 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.location.interactor
+package com.android.systemui.qs.tiles.impl.location.domain.interactor
 
 import android.platform.test.annotations.EnabledOnRavenwood
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.longClick
-import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.longClick
 import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
 import com.android.systemui.statusbar.phone.FakeKeyguardStateController
 import com.android.systemui.statusbar.policy.LocationController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlinx.coroutines.test.runTest
@@ -58,7 +57,7 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        val kosmos = Kosmos()
+        val kosmos = testKosmos()
         underTest =
             LocationTileUserActionInteractor(
                 EmptyCoroutineContext,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapperTest.kt
similarity index 92%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapperTest.kt
index 4ebe23dc..807e373 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,18 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.location.domain
+package com.android.systemui.qs.tiles.impl.location.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
 import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
 import com.android.systemui.qs.tiles.impl.location.qsLocationTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth
 import junit.framework.Assert
 import org.junit.Test
@@ -34,7 +34,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class LocationTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsLocationTileConfig
 
     private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractorTest.kt
new file mode 100644
index 0000000..58883204
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractorTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.modes.domain.interactor
+
+import android.app.Flags
+import android.os.UserHandle
+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.settingslib.notification.modes.TestModeBuilder
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toCollection
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@EnableFlags(Flags.FLAG_MODES_UI)
+@RunWith(AndroidJUnit4::class)
+class ModesDndTileDataInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val dispatcher = kosmos.testDispatcher
+    private val zenModeRepository = kosmos.fakeZenModeRepository
+
+    private val underTest by lazy {
+        ModesDndTileDataInteractor(context, kosmos.zenModeInteractor, dispatcher)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI_DND_TILE)
+    fun availability_flagOn_isTrue() =
+        testScope.runTest {
+            val availability = underTest.availability(TEST_USER).toCollection(mutableListOf())
+
+            assertThat(availability).containsExactly(true)
+        }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MODES_UI_DND_TILE)
+    fun availability_flagOff_isFalse() =
+        testScope.runTest {
+            val availability = underTest.availability(TEST_USER).toCollection(mutableListOf())
+
+            assertThat(availability).containsExactly(false)
+        }
+
+    @Test
+    fun tileData_dndChanges_updateActivated() =
+        testScope.runTest {
+            val model by
+                collectLastValue(
+                    underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+                )
+
+            runCurrent()
+            assertThat(model!!.isActivated).isFalse()
+
+            zenModeRepository.activateMode(TestModeBuilder.MANUAL_DND)
+            runCurrent()
+            assertThat(model!!.isActivated).isTrue()
+
+            zenModeRepository.deactivateMode(TestModeBuilder.MANUAL_DND)
+            runCurrent()
+            assertThat(model!!.isActivated).isFalse()
+        }
+
+    @Test
+    fun tileData_otherModeChanges_notActivated() =
+        testScope.runTest {
+            val model by
+                collectLastValue(
+                    underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+                )
+
+            runCurrent()
+            assertThat(model!!.isActivated).isFalse()
+
+            zenModeRepository.addMode("Other mode")
+            runCurrent()
+            assertThat(model!!.isActivated).isFalse()
+
+            zenModeRepository.activateMode("Other mode")
+            runCurrent()
+            assertThat(model!!.isActivated).isFalse()
+        }
+
+    private companion object {
+        val TEST_USER = UserHandle.of(1)!!
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractorTest.kt
new file mode 100644
index 0000000..4ef61d9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractorTest.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.modes.domain.interactor
+
+import android.platform.test.annotations.EnableFlags
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.notification.modes.TestModeBuilder.MANUAL_DND
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.mainCoroutineContext
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
+import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(android.app.Flags.FLAG_MODES_UI)
+class ModesDndTileUserActionInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val inputHandler = kosmos.qsTileIntentUserInputHandler
+    private val mockDialogDelegate = kosmos.mockModesDialogDelegate
+    private val zenModeRepository = kosmos.zenModeRepository
+    private val zenModeInteractor = kosmos.zenModeInteractor
+    private val settingsPackageRepository = mock<QSSettingsPackageRepository>()
+
+    private val underTest =
+        ModesDndTileUserActionInteractor(
+            kosmos.mainCoroutineContext,
+            inputHandler,
+            mockDialogDelegate,
+            zenModeInteractor,
+            kosmos.modesDialogEventLogger,
+            settingsPackageRepository,
+        )
+
+    @Before
+    fun setUp() {
+        whenever(settingsPackageRepository.getSettingsPackageName()).thenReturn(SETTINGS_PACKAGE)
+    }
+
+    @Test
+    fun handleClick_dndActive_deactivatesDnd() =
+        testScope.runTest {
+            val dndMode by collectLastValue(zenModeInteractor.dndMode)
+            zenModeRepository.activateMode(MANUAL_DND)
+            assertThat(dndMode?.isActive).isTrue()
+
+            underTest.handleInput(QSTileInputTestKtx.click(data = ModesDndTileModel(true, null)))
+
+            assertThat(dndMode?.isActive).isFalse()
+        }
+
+    @Test
+    fun handleClick_dndInactive_activatesDnd() =
+        testScope.runTest {
+            val dndMode by collectLastValue(zenModeInteractor.dndMode)
+            assertThat(dndMode?.isActive).isFalse()
+
+            underTest.handleInput(QSTileInputTestKtx.click(data = ModesDndTileModel(false, null)))
+
+            assertThat(dndMode?.isActive).isTrue()
+        }
+
+    @Test
+    fun handleLongClick_active_opensSettings() =
+        testScope.runTest {
+            zenModeRepository.activateMode(MANUAL_DND)
+            runCurrent()
+
+            underTest.handleInput(QSTileInputTestKtx.longClick(ModesDndTileModel(true, null)))
+
+            QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+                assertThat(it.intent.`package`).isEqualTo(SETTINGS_PACKAGE)
+                assertThat(it.intent.action).isEqualTo(Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
+                assertThat(it.intent.getStringExtra(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID))
+                    .isEqualTo(MANUAL_DND.id)
+            }
+        }
+
+    @Test
+    fun handleLongClick_inactive_opensSettings() =
+        testScope.runTest {
+            zenModeRepository.activateMode(MANUAL_DND)
+            zenModeRepository.deactivateMode(MANUAL_DND)
+            runCurrent()
+
+            underTest.handleInput(QSTileInputTestKtx.longClick(ModesDndTileModel(false, null)))
+
+            QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+                assertThat(it.intent.`package`).isEqualTo(SETTINGS_PACKAGE)
+                assertThat(it.intent.action).isEqualTo(Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
+                assertThat(it.intent.getStringExtra(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID))
+                    .isEqualTo(MANUAL_DND.id)
+            }
+        }
+
+    companion object {
+        private const val SETTINGS_PACKAGE = "the.settings.package"
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index 0b641ce..1df2e1b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -34,7 +34,7 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
 import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
 import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
index 24e4279..9d0b26f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
@@ -30,9 +30,9 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.mainCoroutineContext
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
 import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
 import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapperTest.kt
new file mode 100644
index 0000000..318ce0e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapperTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.modes.ui
+
+import android.app.Flags
+import android.graphics.drawable.TestStubDrawable
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(Flags.FLAG_MODES_UI)
+class ModesDndTileMapperTest : SysuiTestCase() {
+    val config =
+        QSTileConfigTestBuilder.build {
+            uiConfig =
+                QSTileUIConfig.Resource(
+                    iconRes = R.drawable.qs_dnd_icon_off,
+                    labelRes = R.string.quick_settings_modes_label,
+                )
+        }
+
+    val underTest =
+        ModesDndTileMapper(
+            context.orCreateTestableResources
+                .apply {
+                    addOverride(R.drawable.qs_dnd_icon_on, TestStubDrawable())
+                    addOverride(R.drawable.qs_dnd_icon_off, TestStubDrawable())
+                }
+                .resources,
+            context.theme,
+        )
+
+    @Test
+    fun map_inactiveState() {
+        val model = ModesDndTileModel(isActivated = false, extraStatus = null)
+
+        val state = underTest.map(config, model)
+
+        assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.INACTIVE)
+        assertThat((state.icon as Icon.Loaded).res).isEqualTo(R.drawable.qs_dnd_icon_off)
+        assertThat(state.secondaryLabel).isNull() // Will use default label for activationState
+    }
+
+    @Test
+    fun map_activeState() {
+        val model = ModesDndTileModel(isActivated = true, extraStatus = null)
+
+        val state = underTest.map(config, model)
+
+        assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
+        assertThat((state.icon as Icon.Loaded).res).isEqualTo(R.drawable.qs_dnd_icon_on)
+        assertThat(state.secondaryLabel).isNull() // Will use default label for activationState
+    }
+
+    @Test
+    fun map_activeStateWithExtraStatus() {
+        val model = ModesDndTileModel(isActivated = true, extraStatus = "Until 14:00")
+
+        val state = underTest.map(config, model)
+
+        assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
+        assertThat((state.icon as Icon.Loaded).res).isEqualTo(R.drawable.qs_dnd_icon_on)
+        assertThat(state.secondaryLabel).isEqualTo("Until 14:00")
+        assertThat(state.contentDescription).isEqualTo("Do Not Disturb")
+        assertThat(state.stateDescription).isEqualTo("Until 14:00")
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapperTest.kt
similarity index 91%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapperTest.kt
index d73044f..e8a85f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.modes.ui
+package com.android.systemui.qs.tiles.impl.modes.ui.mapper
 
 import android.app.Flags
 import android.graphics.drawable.TestStubDrawable
@@ -23,10 +23,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
 import com.android.systemui.res.R
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
index a0aa2d4..a5e2922 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractorTest.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.accessibility.data.repository.NightDisplayRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import com.android.systemui.user.utils.UserScopedService
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
@@ -47,7 +47,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NightDisplayTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testUser = UserHandle.of(1)!!
     private val testStartTime = LocalTime.MIDNIGHT
     private val testEndTime = LocalTime.NOON
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
index adc8bcb..a0d0b23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
@@ -26,12 +26,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.NightDisplayRepository
 import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
 import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
+import com.android.systemui.testKosmos
 import com.android.systemui.user.utils.UserScopedService
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
@@ -51,7 +51,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NightDisplayTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
     private val testUser = UserHandle.of(1)
     private val colorDisplayManager =
@@ -89,7 +89,7 @@
         NightDisplayTileUserActionInteractor(
             nightDisplayRepository,
             qsTileIntentUserActionHandler,
-            kosmos.qsTileLogger
+            kosmos.qsTileLogger,
         )
 
     @Test
@@ -143,7 +143,7 @@
             underTest.handleInput(
                 QSTileInputTestKtx.longClick(
                     NightDisplayTileModel.AutoModeOff(enabledState, false),
-                    testUser
+                    testUser,
                 )
             )
 
@@ -163,7 +163,7 @@
             underTest.handleInput(
                 QSTileInputTestKtx.longClick(
                     NightDisplayTileModel.AutoModeOff(enabledState, false),
-                    testUser
+                    testUser,
                 )
             )
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapperTest.kt
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapperTest.kt
index 7c85326..25c7016 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.night.ui
+package com.android.systemui.qs.tiles.impl.night.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.service.quicksettings.Tile
@@ -24,13 +24,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
 import com.android.systemui.qs.tiles.impl.night.qsNightDisplayTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import java.time.LocalTime
 import java.time.format.DateTimeFormatter
@@ -41,7 +41,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NightDisplayTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val config = kosmos.qsNightDisplayTileConfig
 
     private val testStartTime = LocalTime.MIDNIGHT
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
index 4786fc4..51cad30 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
@@ -24,9 +24,9 @@
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.toCollection
@@ -38,12 +38,11 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NotesTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val testUser = UserHandle.of(1)
     private lateinit var underTest: NotesTileDataInteractor
 
-
     @EnableFlags(Flags.FLAG_NOTES_ROLE_QS_TILE)
     @Test
     fun availability_qsFlagEnabled_notesRoleEnabled_returnTrue() =
@@ -92,7 +91,7 @@
     fun tileData_notEmpty() = runTest {
         underTest = NotesTileDataInteractor(isNoteTaskEnabled = true)
         val flowValue by
-        collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
+            collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
 
         runCurrent()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
index 54911e6..c29a490 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
@@ -20,27 +20,27 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.notetask.NoteTaskController
 import com.android.systemui.notetask.NoteTaskEntryPoint
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NotesTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val inputHandler = FakeQSTileIntentUserInputHandler()
     private val panelInteractor = mock<PanelInteractor>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapperTest.kt
similarity index 88%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapperTest.kt
index b6caa22..0234584 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.notes.domain
+package com.android.systemui.qs.tiles.impl.notes.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.widget.Button
@@ -22,19 +22,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
 import com.android.systemui.qs.tiles.impl.notes.qsNotesTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import kotlin.test.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NotesTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsNotesTileConfig
 
     private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
index 59eb069..d0ea691 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
@@ -22,9 +22,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.oneHandedModeRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileDataInteractor
+import com.android.systemui.testKosmos
 import com.android.wm.shell.onehanded.OneHanded
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
@@ -37,7 +37,7 @@
 @RunWith(AndroidJUnit4::class)
 class OneHandedModeTileDataInteractorTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testUser = UserHandle.of(1)!!
     private val oneHandedModeRepository = kosmos.oneHandedModeRepository
     private val underTest: OneHandedModeTileDataInteractor =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileUserActionInteractorTest.kt
index 3f17d4c..a8f1808 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileUserActionInteractorTest.kt
@@ -23,9 +23,9 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.FakeOneHandedModeRepository
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
 import com.google.common.truth.Truth.assertThat
@@ -42,11 +42,7 @@
     private val repository = FakeOneHandedModeRepository()
     private val inputHandler = FakeQSTileIntentUserInputHandler()
 
-    private val underTest =
-        OneHandedModeTileUserActionInteractor(
-            repository,
-            inputHandler,
-        )
+    private val underTest = OneHandedModeTileUserActionInteractor(repository, inputHandler)
 
     @Test
     fun handleClickWhenEnabled() = runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapperTest.kt
similarity index 91%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapperTest.kt
index 5b39810..fe10457 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.onehanded.ui
+package com.android.systemui.qs.tiles.impl.onehanded.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.widget.Switch
@@ -22,13 +22,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
 import com.android.systemui.qs.tiles.impl.onehanded.qsOneHandedModeTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -36,7 +36,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class OneHandedModeTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val config = kosmos.qsOneHandedModeTileConfig
     private val subtitleArrayId = SubtitleArrayMapping.getSubtitleId(config.tileSpec.spec)
     private val subtitleArray by lazy { context.resources.getStringArray(subtitleArrayId) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt
index c41ce2f..82535db 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.Callback
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
index 312f180..8c0dae8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
@@ -21,12 +21,12 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
 import com.android.systemui.qs.tiles.impl.qr.qrCodeScannerTileUserActionInteractor
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -36,7 +36,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class QRCodeScannerTileUserActionInteractorTest : SysuiTestCase() {
-    val kosmos = Kosmos()
+    val kosmos = testKosmos()
     private val inputHandler = kosmos.qsTileIntentUserInputHandler
     private val underTest = kosmos.qrCodeScannerTileUserActionInteractor
     private val intent = mock<Intent>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapperTest.kt
similarity index 81%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapperTest.kt
index c572ff6..8e453ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.qr.ui
+package com.android.systemui.qs.tiles.impl.qr.ui.mapper
 
 import android.content.Intent
 import android.graphics.drawable.TestStubDrawable
@@ -23,11 +23,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
 import com.android.systemui.qs.tiles.impl.qr.qsQRCodeScannerTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import org.junit.Before
 import org.junit.Test
@@ -36,7 +37,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class QRCodeScannerTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val config = kosmos.qsQRCodeScannerTileConfig
 
     private lateinit var mapper: QRCodeScannerTileMapper
@@ -46,12 +47,7 @@
         mapper =
             QRCodeScannerTileMapper(
                 context.orCreateTestableResources
-                    .apply {
-                        addOverride(
-                            com.android.systemui.res.R.drawable.ic_qr_code_scanner,
-                            TestStubDrawable(),
-                        )
-                    }
+                    .apply { addOverride(R.drawable.ic_qr_code_scanner, TestStubDrawable()) }
                     .resources,
                 context.theme,
             )
@@ -91,9 +87,9 @@
         val label = context.getString(com.android.systemui.res.R.string.qr_code_scanner_title)
         return QSTileState(
             Icon.Loaded(
-                context.getDrawable(com.android.systemui.res.R.drawable.ic_qr_code_scanner)!!,
+                context.getDrawable(R.drawable.ic_qr_code_scanner)!!,
                 null,
-                com.android.systemui.res.R.drawable.ic_qr_code_scanner,
+                R.drawable.ic_qr_code_scanner,
             ),
             label,
             activationState,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
index dc3248d..db46a4f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
@@ -23,10 +23,10 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.reduceBrightColorsController
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
@@ -40,14 +40,14 @@
 class ReduceBrightColorsTileDataInteractorTest : SysuiTestCase() {
 
     private val isAvailable = true
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val reduceBrightColorsController = kosmos.reduceBrightColorsController
     private val underTest: ReduceBrightColorsTileDataInteractor =
         ReduceBrightColorsTileDataInteractor(
             testScope.testScheduler,
             isAvailable,
-            reduceBrightColorsController
+            reduceBrightColorsController,
         )
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
index 75b090c..221a6f1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
@@ -28,11 +28,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.extradim.ExtraDimDialogManager
 import com.android.systemui.accessibility.reduceBrightColorsController
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -48,7 +48,7 @@
 @RunWith(AndroidJUnit4::class)
 class ReduceBrightColorsTileUserActionInteractorTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val inputHandler = FakeQSTileIntentUserInputHandler()
     private val controller = kosmos.reduceBrightColorsController
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapperTest.kt
similarity index 91%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapperTest.kt
index 00017f9..dabf0b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.reducebrightness.ui
+package com.android.systemui.qs.tiles.impl.reducebrightness.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.service.quicksettings.Tile
@@ -23,12 +23,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
 import com.android.systemui.qs.tiles.impl.reducebrightness.qsReduceBrightColorsTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -36,7 +36,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ReduceBrightColorsTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val config = kosmos.qsReduceBrightColorsTileConfig
 
     private lateinit var mapper: ReduceBrightColorsTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
index 283fa60..ef67910 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
@@ -28,9 +28,9 @@
 import com.android.systemui.camera.data.repository.fakeCameraAutoRotateRepository
 import com.android.systemui.camera.data.repository.fakeCameraSensorPrivacyRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.utils.leaks.FakeBatteryController
@@ -48,7 +48,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class RotationLockTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val batteryController = FakeBatteryController(LeakCheck())
     private val rotationController = FakeRotationLockController(LeakCheck())
@@ -65,7 +65,7 @@
         whenever(
                 packageManager.checkPermission(
                     eq(Manifest.permission.CAMERA),
-                    eq(TEST_PACKAGE_NAME)
+                    eq(TEST_PACKAGE_NAME),
                 )
             )
             .thenReturn(PackageManager.PERMISSION_GRANTED)
@@ -81,7 +81,7 @@
                     .apply {
                         addOverride(com.android.internal.R.bool.config_allowRotationResolver, true)
                     }
-                    .resources
+                    .resources,
             )
     }
 
@@ -182,7 +182,7 @@
             whenever(
                     packageManager.checkPermission(
                         eq(Manifest.permission.CAMERA),
-                        eq(TEST_PACKAGE_NAME)
+                        eq(TEST_PACKAGE_NAME),
                     )
                 )
                 .thenReturn(PackageManager.PERMISSION_DENIED)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt
index 1653ce3..e60e210 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt
@@ -22,9 +22,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
 import com.android.systemui.utils.leaks.FakeRotationLockController
 import com.google.common.truth.Truth.assertThat
@@ -39,11 +39,7 @@
     private val controller = FakeRotationLockController(LeakCheck())
     private val inputHandler = FakeQSTileIntentUserInputHandler()
 
-    private val underTest =
-        RotationLockTileUserActionInteractor(
-            controller,
-            inputHandler,
-        )
+    private val underTest = RotationLockTileUserActionInteractor(controller, inputHandler)
 
     @Test
     fun handleClickWhenEnabled() = runTest {
@@ -69,14 +65,7 @@
     fun handleLongClickWhenDisabled() = runTest {
         val enabled = false
 
-        underTest.handleInput(
-            QSTileInputTestKtx.longClick(
-                RotationLockTileModel(
-                    enabled,
-                    false,
-                )
-            )
-        )
+        underTest.handleInput(QSTileInputTestKtx.longClick(RotationLockTileModel(enabled, false)))
 
         QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
             assertThat(it.intent.action).isEqualTo(Settings.ACTION_AUTO_ROTATE_SETTINGS)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
index 7401014..92c7a1e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -25,14 +25,14 @@
 import com.android.systemui.defaultDeviceState
 import com.android.systemui.deviceStateManager
 import com.android.systemui.foldedDeviceStateList
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
 import com.android.systemui.qs.tiles.impl.rotation.qsRotationLockTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.statusbar.policy.devicePostureController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -42,7 +42,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class RotationLockTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val rotationLockTileConfig = kosmos.qsRotationLockTileConfig
     private val devicePostureController = kosmos.devicePostureController
     private val deviceStateManager = kosmos.deviceStateManager
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt
index c286ea7..97bd1997 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
 import com.android.systemui.utils.leaks.FakeDataSaverController
 import com.google.common.truth.Truth
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt
index 87ac034a..ee57b25 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt
@@ -24,9 +24,9 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapperTest.kt
similarity index 91%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapperTest.kt
index 1fb5048..638b76d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.saver.domain
+package com.android.systemui.qs.tiles.impl.saver.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.widget.Switch
@@ -22,19 +22,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
 import com.android.systemui.qs.tiles.impl.saver.qsDataSaverTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DataSaverTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val dataSaverTileConfig = kosmos.qsDataSaverTileConfig
 
     // Using lazy (versus =) to make sure we override the right context -- see b/311612168
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
index 41174e7..0c98271 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
@@ -22,11 +22,11 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
@@ -38,7 +38,7 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class ScreenRecordTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val screenRecordRepo = kosmos.screenRecordRepository
     private val underTest: ScreenRecordTileDataInteractor =
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..9ffffa4 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
@@ -26,16 +26,16 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.screenrecord.RecordingController
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import com.android.systemui.testKosmos
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -49,7 +49,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val keyguardInteractor = kosmos.keyguardInteractor
     private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/mapper/ScreenRecordTileMapperTest.kt
similarity index 93%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/mapper/ScreenRecordTileMapperTest.kt
index 3632556..6acfc444 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/mapper/ScreenRecordTileMapperTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.screenrecord.ui
+package com.android.systemui.qs.tiles.impl.screenrecord.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.text.TextUtils
@@ -23,13 +23,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
-import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.ScreenRecordTileMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.mapper.ScreenRecordTileMapper
 import com.android.systemui.qs.tiles.impl.screenrecord.qsScreenRecordTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,7 +37,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ScreenRecordTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val config = kosmos.qsScreenRecordTileConfig
 
     private lateinit var mapper: ScreenRecordTileMapper
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
index 6c7bb1b..7d1d7d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
@@ -23,11 +23,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.impl.sensorprivacy.SensorPrivacyToggleTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -44,7 +43,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class SensorPrivacyToggleTileDataInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val mockSensorPrivacyController =
         mock<IndividualSensorPrivacyController> {
@@ -55,7 +54,7 @@
         SensorPrivacyToggleTileDataInteractor(
             testScope.testScheduler,
             mockSensorPrivacyController,
-            CAMERA
+            CAMERA,
         )
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
index 562e6eb..2630fdd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
@@ -26,14 +26,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.activityStarter
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
-import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.SensorPrivacyToggleTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -48,7 +47,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class SensorPrivacyToggleTileUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val inputHandler = FakeQSTileIntentUserInputHandler()
     private val keyguardInteractor = kosmos.keyguardInteractor
     // The keyguard repository below is the same one kosmos used to create the interactor above
@@ -64,7 +63,7 @@
             mockActivityStarter,
             mockSensorPrivacyController,
             fakeSafetyCenterManager,
-            CAMERA
+            CAMERA,
         )
 
     @Test
@@ -79,7 +78,7 @@
             .setSensorBlocked(
                 eq(SensorPrivacyManager.Sources.QS_TILE),
                 eq(CAMERA),
-                eq(!originalIsBlocked)
+                eq(!originalIsBlocked),
             )
     }
 
@@ -95,7 +94,7 @@
             .setSensorBlocked(
                 eq(SensorPrivacyManager.Sources.QS_TILE),
                 eq(CAMERA),
-                eq(!originalIsBlocked)
+                eq(!originalIsBlocked),
             )
     }
 
@@ -114,7 +113,7 @@
             .setSensorBlocked(
                 eq(SensorPrivacyManager.Sources.QS_TILE),
                 eq(CAMERA),
-                eq(!originalIsBlocked)
+                eq(!originalIsBlocked),
             )
         verify(mockActivityStarter).postQSRunnableDismissingKeyguard(any())
     }
@@ -150,7 +149,7 @@
                 mockActivityStarter,
                 mockSensorPrivacyController,
                 fakeSafetyCenterManager,
-                MICROPHONE
+                MICROPHONE,
             )
 
         micUserActionInteractor.handleInput(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapperTest.kt
similarity index 89%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapperTest.kt
index e4cd0e0..d9a30b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.sensorprivacy.ui
+package com.android.systemui.qs.tiles.impl.sensorprivacy.ui.mapper
 
 import android.graphics.drawable.TestStubDrawable
 import android.hardware.SensorPrivacyManager.Sensors.CAMERA
@@ -24,22 +24,23 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
 import com.android.systemui.qs.tiles.impl.sensorprivacy.qsCameraSensorPrivacyToggleTileConfig
 import com.android.systemui.qs.tiles.impl.sensorprivacy.qsMicrophoneSensorPrivacyToggleTileConfig
-import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyTileResources.CameraPrivacyTileResources
-import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyTileResources.MicrophonePrivacyTileResources
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model.SensorPrivacyTileResources
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model.SensorPrivacyTileResources.CameraPrivacyTileResources
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model.SensorPrivacyTileResources.MicrophonePrivacyTileResources
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class SensorPrivacyToggleTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val cameraConfig = kosmos.qsCameraSensorPrivacyToggleTileConfig
     private val micConfig = kosmos.qsMicrophoneSensorPrivacyToggleTileConfig
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractorTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileDataInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractorTest.kt
index 96538b7..36a60c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractorTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.uimodenight.domain
+package com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor
 
 import android.app.UiModeManager
 import android.content.res.Configuration
@@ -27,9 +27,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.UiModeNightTileDataInteractor
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.mockito.mock
@@ -77,7 +76,7 @@
                 uiModeManager,
                 batteryController,
                 locationController,
-                dateFormatUtil
+                dateFormatUtil,
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractorTest.kt
similarity index 89%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileUserActionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractorTest.kt
index eea6d16..894a6cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractorTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.uimodenight.domain
+package com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor
 
 import android.app.UiModeManager
 import android.platform.test.annotations.EnabledOnRavenwood
@@ -22,11 +22,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.uimodenight.UiModeNightTileModelHelper.createModel
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.UiModeNightTileUserActionInteractor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth
@@ -57,7 +56,7 @@
             UiModeNightTileUserActionInteractor(
                 EmptyCoroutineContext,
                 uiModeManager,
-                qsTileIntentUserActionHandler
+                qsTileIntentUserActionHandler,
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapperTest.kt
similarity index 98%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapperTest.kt
index 8f5f2d3..e7653f2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapperTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.uimodenight.domain
+package com.android.systemui.qs.tiles.impl.uimodenight.ui.mapper
 
 import android.app.UiModeManager
 import android.graphics.drawable.TestStubDrawable
@@ -25,12 +25,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.uimodenight.UiModeNightTileModelHelper.createModel
 import com.android.systemui.qs.tiles.impl.uimodenight.qsUiModeNightTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import kotlin.reflect.KClass
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,7 +38,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class UiModeNightTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsUiModeNightTileConfig
 
     private val mapper by lazy {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractorTest.kt
index 8651300..4f59416 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractorTest.kt
@@ -23,7 +23,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
 import com.android.systemui.utils.leaks.FakeManagedProfileController
 import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractorTest.kt
index 8a63e2c..3d08a01 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractorTest.kt
@@ -22,9 +22,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
 import com.android.systemui.utils.leaks.FakeManagedProfileController
 import com.google.common.truth.Truth.assertThat
@@ -40,11 +40,7 @@
     private val inputHandler = FakeQSTileIntentUserInputHandler()
     private val profileController = FakeManagedProfileController(LeakCheck())
 
-    private val underTest =
-        WorkModeTileUserActionInteractor(
-            profileController,
-            inputHandler,
-        )
+    private val underTest = WorkModeTileUserActionInteractor(profileController, inputHandler)
 
     @Test
     fun handleClickWhenEnabled() = runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapperTest.kt
similarity index 93%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapperTest.kt
index 2c81f39..b420d44 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapperTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.work.ui
+package com.android.systemui.qs.tiles.impl.work.ui.mapper
 
 import android.app.admin.DevicePolicyResources
 import android.app.admin.DevicePolicyResourcesManager
@@ -26,12 +26,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
 import com.android.systemui.qs.tiles.impl.work.qsWorkModeTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
@@ -43,7 +43,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class WorkModeTileMapperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val qsTileConfig = kosmos.qsWorkModeTileConfig
     private val devicePolicyManager = kosmos.devicePolicyManager
     private val testLabel = context.getString(R.string.quick_settings_work_mode_label)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index a8b005f..12ed3f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.display.data.repository.displayStateRepository
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -40,6 +39,7 @@
 import com.android.systemui.settings.brightness.MirrorController
 import com.android.systemui.shade.data.repository.fakeShadeRepository
 import com.android.systemui.shade.domain.interactor.shadeModeInteractor
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -65,7 +65,7 @@
 @RunWith(AndroidJUnit4::class)
 class QSSceneAdapterImplTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos().apply { testCase = this@QSSceneAdapterImplTest }
+    private val kosmos = testKosmos().apply { testCase = this@QSSceneAdapterImplTest }
     private val testDispatcher = kosmos.testDispatcher
     private val testScope = kosmos.testScope
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
index 7bcaeab..390a5d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
@@ -28,12 +28,12 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.dialogTransitionAnimator
 import com.android.systemui.concurrency.fakeExecutor
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
 import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.settings.userFileManager
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeGlobalSettings
 import com.android.traceur.TraceConfig
 import com.google.common.truth.Truth
@@ -52,7 +52,7 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class IssueRecordingServiceSessionTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val bgExecutor = kosmos.fakeExecutor
     private val userContextProvider: UserContextProvider = kosmos.userTracker
     private val dialogTransitionAnimator: DialogTransitionAnimator = kosmos.dialogTransitionAnimator
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
index 83bdcd2..0510e6ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
@@ -22,9 +22,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.settings.userFileManager
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeGlobalSettings
 import com.google.common.truth.Truth
 import org.junit.Before
@@ -40,7 +40,7 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class IssueRecordingStateTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private lateinit var underTest: IssueRecordingState
     @Mock private lateinit var resolver: ContentResolver
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
index 737b101..6f0dd16 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt
@@ -20,10 +20,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth
 import org.junit.Before
 import org.junit.Test
@@ -34,7 +34,7 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class ScreenRecordingStartTimeStoreTest : SysuiTestCase() {
-    private val userTracker: UserTracker = Kosmos().also { it.testCase = this }.userTracker
+    private val userTracker: UserTracker = testKosmos().also { it.testCase = this }.userTracker
 
     private lateinit var underTest: ScreenRecordingStartTimeStore
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
index a82a7de..7e9487b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -38,6 +37,7 @@
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
 import com.android.systemui.statusbar.notification.init.NotificationsController
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -57,7 +57,7 @@
 @RunWith(AndroidJUnit4::class)
 class WindowRootViewVisibilityInteractorTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val testDispatcher = StandardTestDispatcher()
     private val iStatusBarService = mock<IStatusBarService>()
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 9adf24f..1743e05 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
@@ -863,7 +863,7 @@
             whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
-                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
 
             setupBiometricAuth(hasUdfps = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -885,7 +885,7 @@
             whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
-                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
 
             setupBiometricAuth(hasUdfps = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -907,7 +907,7 @@
             whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
-                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
 
             setupBiometricAuth(hasSfps = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -930,7 +930,7 @@
             whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
-                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
 
             setupBiometricAuth(hasSfps = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -1033,7 +1033,7 @@
             whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
-                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
 
             setupBiometricAuth(hasSfps = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -1056,7 +1056,7 @@
             whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
-                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
 
             setupBiometricAuth(hasSfps = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -1079,7 +1079,7 @@
             whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
-                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
 
             setupBiometricAuth(hasSfps = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -1102,7 +1102,7 @@
             whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
-                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry)
 
             setupBiometricAuth(hasSfps = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
@@ -1160,7 +1160,7 @@
 
     @Test
     @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
-    fun playsFaceErrorHaptics_nonSfps_coEx() =
+    fun skipsFaceErrorHaptics_nonSfps_coEx() =
         testScope.runTest {
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
@@ -1172,15 +1172,14 @@
             underTest.start()
             updateFaceAuthStatus(isSuccess = false)
 
-            assertThat(playErrorHaptic).isNotNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
-            verify(vibratorHelper).vibrateAuthError(anyString())
+            assertThat(playErrorHaptic).isNull()
+            verify(vibratorHelper, never()).vibrateAuthError(anyString())
             verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
         }
 
     @Test
     @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
-    fun playsMSDLFaceErrorHaptics_nonSfps_coEx() =
+    fun skipsMSDLFaceErrorHaptics_nonSfps_coEx() =
         testScope.runTest {
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
@@ -1192,10 +1191,9 @@
             underTest.start()
             updateFaceAuthStatus(isSuccess = false)
 
-            assertThat(playErrorHaptic).isNotNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
-            assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
-            assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+            assertThat(playErrorHaptic).isNull()
+            assertThat(msdlPlayer.latestTokenPlayed).isNull()
+            assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
         }
 
     @Test
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 9724974..bd54166 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
@@ -21,10 +21,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.screenrecord.RecordingController
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -39,7 +39,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ScreenRecordRepositoryTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val recordingController = mock<RecordingController>()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 3407cd5..3788049 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -40,8 +40,10 @@
 import android.content.pm.ActivityInfo;
 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.FlagsParameterization;
-import android.provider.Settings;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
 import android.view.WindowManager;
@@ -50,6 +52,7 @@
 
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -71,11 +74,11 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
-import com.android.systemui.util.settings.FakeSettings;
 
 import com.google.common.util.concurrent.MoreExecutors;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -94,6 +97,9 @@
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
+    @Rule public final CheckFlagsRule checkFlagsRule =
+        DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Mock private ViewCaptureAwareWindowManager mWindowManager;
     @Mock private DozeParameters mDozeParameters;
     @Spy private final NotificationShadeWindowView mNotificationShadeWindowView = spy(
@@ -113,7 +119,6 @@
     @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
     @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
 
-    private FakeSettings mSecureSettings;
     private final Executor mMainExecutor = MoreExecutors.directExecutor();
     private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
     private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@@ -135,9 +140,6 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mSecureSettings = new FakeSettings();
-        mSecureSettings.putInt(Settings.Secure.DISABLE_SECURE_WINDOWS, 0);
-
         // Preferred refresh rate is equal to the first displayMode's refresh rate
         mPreferredRefreshRate = mContext.getDisplay().getSystemSupportedModes()[0].getRefreshRate();
         overrideResource(
@@ -171,7 +173,6 @@
                 () -> mSelectedUserInteractor,
                 mUserTracker,
                 mKosmos.getNotificationShadeWindowModel(),
-                mSecureSettings,
                 mKosmos::getCommunalInteractor,
                 mKosmos.getShadeLayoutParams());
         mNotificationShadeWindowController.setScrimsVisibilityListener((visibility) -> {});
@@ -272,6 +273,7 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_DISABLE_BLURRED_SHADE_VISIBLE)
     public void setBackgroundBlurRadius_expandedWithBlurs() {
         mNotificationShadeWindowController.setBackgroundBlurRadius(10);
         verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
@@ -355,19 +357,6 @@
     }
 
     @Test
-    public void setKeyguardShowingWithSecureWindowsDisabled_disablesSecureFlag() {
-        mSecureSettings.putInt(Settings.Secure.DISABLE_SECURE_WINDOWS, 1);
-        mNotificationShadeWindowController.setBouncerShowing(true);
-
-        verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
-        assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) == 0).isTrue();
-        assertThat(
-                (mLayoutParameters.getValue().inputFeatures & INPUT_FEATURE_SENSITIVE_FOR_PRIVACY)
-                        != 0)
-                .isTrue();
-    }
-
-    @Test
     public void setKeyguardNotShowing_disablesSecureFlag() {
         mNotificationShadeWindowController.setBouncerShowing(false);
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index c6ce581..0c90d07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.assist.AssistManager
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -43,6 +42,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -63,7 +63,7 @@
 @SmallTest
 class ShadeControllerImplTest : SysuiTestCase() {
     private val executor = FakeExecutor(FakeSystemClock())
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     @Mock private lateinit var commandQueue: CommandQueue
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
index 054c1b8..32eec56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -35,6 +34,7 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -52,7 +52,7 @@
 @RunWith(AndroidJUnit4::class)
 @EnableSceneContainer
 class ShadeControllerSceneImplTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
     private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
index e43c46b..dd0ba00 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade.display
 
+import android.platform.test.annotations.EnableFlags
 import android.view.Display
 import android.view.Display.TYPE_EXTERNAL
 import android.view.MotionEvent
@@ -31,6 +32,7 @@
 import com.android.systemui.shade.data.repository.statusBarTouchShadeDisplayPolicy
 import com.android.systemui.shade.domain.interactor.notificationElement
 import com.android.systemui.shade.domain.interactor.qsElement
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -41,6 +43,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
+@EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
 class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
     private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
index 03f546b..24593f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.res.Configuration
 import android.content.res.mockResources
+import android.platform.test.annotations.EnableFlags
 import android.view.Display
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -27,6 +28,7 @@
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.scene.ui.view.mockShadeRootView
 import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
 import com.android.systemui.statusbar.notification.row.notificationRebindingTracker
@@ -47,6 +49,7 @@
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
+@EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
 class ShadeDisplaysInteractorTest : SysuiTestCase() {
     private val kosmos = testKosmos().useUnconfinedTestDispatcher()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index a832f48..04eb709 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -94,6 +94,36 @@
         }
 
     @Test
+    fun showClock_wideLayout_returnsTrue() =
+        testScope.runTest {
+            kosmos.enableDualShade(wideLayout = true)
+
+            setupDualShadeState(scene = Scenes.Lockscreen, overlay = Overlays.NotificationsShade)
+            assertThat(underTest.showClock).isTrue()
+
+            setupDualShadeState(scene = Scenes.Lockscreen, overlay = Overlays.QuickSettingsShade)
+            assertThat(underTest.showClock).isTrue()
+        }
+
+    @Test
+    fun showClock_narrowLayoutOnNotificationsShade_returnsFalse() =
+        testScope.runTest {
+            kosmos.enableDualShade(wideLayout = false)
+            setupDualShadeState(scene = Scenes.Lockscreen, overlay = Overlays.NotificationsShade)
+
+            assertThat(underTest.showClock).isFalse()
+        }
+
+    @Test
+    fun showClock_narrowLayoutOnQuickSettingsShade_returnsTrue() =
+        testScope.runTest {
+            kosmos.enableDualShade(wideLayout = false)
+            setupDualShadeState(scene = Scenes.Lockscreen, overlay = Overlays.QuickSettingsShade)
+
+            assertThat(underTest.showClock).isTrue()
+        }
+
+    @Test
     fun onShadeCarrierGroupClicked_launchesNetworkSettings() =
         testScope.runTest {
             val activityStarter = kosmos.activityStarter
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ddad230..2f2fafa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -25,8 +25,8 @@
 import com.android.systemui.plugins.PluginLifecycleManager
 import com.android.systemui.plugins.PluginListener
 import com.android.systemui.plugins.PluginManager
+import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.systemui.plugins.clocks.ClockController
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ClockId
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockMetadata
@@ -543,7 +543,7 @@
 
     @Test
     fun jsonDeserialization_fontAxes() {
-        val expected = ClockSettings(axes = listOf(ClockFontAxisSetting("KEY", 10f)))
+        val expected = ClockSettings(axes = ClockAxisStyle("KEY", 10f))
         val json = JSONObject("""{"axes":[{"key":"KEY","value":10}]}""")
         val actual = ClockSettings.fromJson(json)
         assertEquals(expected, actual)
@@ -576,7 +576,7 @@
 
     @Test
     fun jsonSerialization_axisSettings() {
-        val settings = ClockSettings(axes = listOf(ClockFontAxisSetting("KEY", 10f)))
+        val settings = ClockSettings(axes = ClockAxisStyle("KEY", 10f))
         val actual = ClockSettings.toJson(settings)
         val expected = JSONObject("""{"metadata":{},"axes":[{"key":"KEY","value":10}]}""")
         assertEquals(expected.toString(), actual.toString())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index 15ec752..0642467 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -112,7 +112,7 @@
         verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA)
         verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA)
 
-        clock.initialize(true, 0f, 0f, {})
+        clock.initialize(true, 0f, 0f, null)
 
         // This is the default darkTheme color
         val expectedColor = context.resources.getColor(android.R.color.system_accent1_100)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
index 17509dc..8e2d1b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
@@ -3,64 +3,57 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.condition.testStart
+import com.android.systemui.condition.testStop
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ConditionExtensionsTest : SysuiTestCase() {
-    private lateinit var testScope: TestScope
-    private val testCallback =
-        Condition.Callback {
-            // This is a no-op
-        }
-
-    @Before
-    fun setUp() {
-        testScope = TestScope(UnconfinedTestDispatcher())
-    }
+    private val kosmos = testKosmos()
 
     @Test
     fun flowInitiallyTrue() =
-        testScope.runTest {
+        kosmos.runTest {
             val flow = flowOf(true)
-            val condition = flow.toCondition(scope = this, Condition.START_EAGERLY)
+            val condition = flow.toCondition(scope = testScope, Condition.START_EAGERLY)
 
             assertThat(condition.isConditionSet).isFalse()
 
-            condition.testStart()
+            testStart(condition)
             assertThat(condition.isConditionSet).isTrue()
             assertThat(condition.isConditionMet).isTrue()
         }
 
     @Test
     fun flowInitiallyFalse() =
-        testScope.runTest {
+        kosmos.runTest {
             val flow = flowOf(false)
-            val condition = flow.toCondition(scope = this, Condition.START_EAGERLY)
+            val condition = flow.toCondition(scope = testScope, Condition.START_EAGERLY)
 
             assertThat(condition.isConditionSet).isFalse()
 
-            condition.testStart()
+            testStart(condition)
             assertThat(condition.isConditionSet).isTrue()
             assertThat(condition.isConditionMet).isFalse()
         }
 
     @Test
     fun emptyFlowWithNoInitialValue() =
-        testScope.runTest {
+        kosmos.runTest {
             val flow = emptyFlow<Boolean>()
-            val condition = flow.toCondition(scope = this, Condition.START_EAGERLY)
-            condition.testStop()
+            val condition = flow.toCondition(scope = testScope, Condition.START_EAGERLY)
+            testStop(condition)
 
             assertThat(condition.isConditionSet).isFalse()
             assertThat(condition.isConditionMet).isFalse()
@@ -68,15 +61,15 @@
 
     @Test
     fun emptyFlowWithInitialValueOfTrue() =
-        testScope.runTest {
+        kosmos.runTest {
             val flow = emptyFlow<Boolean>()
             val condition =
                 flow.toCondition(
-                    scope = this,
+                    scope = testScope,
                     strategy = Condition.START_EAGERLY,
                     initialValue = true,
                 )
-            condition.testStart()
+            testStart(condition)
 
             assertThat(condition.isConditionSet).isTrue()
             assertThat(condition.isConditionMet).isTrue()
@@ -84,15 +77,15 @@
 
     @Test
     fun emptyFlowWithInitialValueOfFalse() =
-        testScope.runTest {
+        kosmos.runTest {
             val flow = emptyFlow<Boolean>()
             val condition =
                 flow.toCondition(
-                    scope = this,
+                    scope = testScope,
                     strategy = Condition.START_EAGERLY,
                     initialValue = false,
                 )
-            condition.testStart()
+            testStart(condition)
 
             assertThat(condition.isConditionSet).isTrue()
             assertThat(condition.isConditionMet).isFalse()
@@ -100,42 +93,36 @@
 
     @Test
     fun conditionUpdatesWhenFlowEmitsNewValue() =
-        testScope.runTest {
+        kosmos.runTest {
             val flow = MutableStateFlow(false)
-            val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY)
-            condition.testStart()
+            val condition = flow.toCondition(scope = testScope, strategy = Condition.START_EAGERLY)
+            testStart(condition)
 
             assertThat(condition.isConditionSet).isTrue()
             assertThat(condition.isConditionMet).isFalse()
 
             flow.value = true
+            runCurrent()
             assertThat(condition.isConditionMet).isTrue()
 
             flow.value = false
+            runCurrent()
             assertThat(condition.isConditionMet).isFalse()
 
-            condition.testStop()
+            testStop(condition)
         }
 
     @Test
     fun stoppingConditionUnsubscribesFromFlow() =
-        testScope.runTest {
+        kosmos.runTest {
             val flow = MutableSharedFlow<Boolean>()
-            val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY)
+            val condition = flow.toCondition(scope = testScope, strategy = Condition.START_EAGERLY)
             assertThat(flow.subscriptionCount.value).isEqualTo(0)
 
-            condition.testStart()
+            testStart(condition)
             assertThat(flow.subscriptionCount.value).isEqualTo(1)
 
-            condition.testStop()
+            testStop(condition)
             assertThat(flow.subscriptionCount.value).isEqualTo(0)
         }
-
-    fun Condition.testStart() {
-        addCallback(testCallback)
-    }
-
-    fun Condition.testStop() {
-        removeCallback(testCallback)
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
deleted file mode 100644
index 267f22b..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
+++ /dev/null
@@ -1,621 +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.shared.condition;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.log.TableLogBufferBase;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ConditionMonitorTest extends SysuiTestCase {
-    private FakeCondition mCondition1;
-    private FakeCondition mCondition2;
-    private FakeCondition mCondition3;
-    private HashSet<Condition> mConditions;
-    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
-
-    @Mock
-    private CoroutineScope mScope;
-    @Mock
-    private TableLogBufferBase mLogBuffer;
-
-    private Monitor mConditionMonitor;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        mCondition1 = spy(new FakeCondition(mScope));
-        mCondition2 = spy(new FakeCondition(mScope));
-        mCondition3 = spy(new FakeCondition(mScope));
-        mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
-
-        mConditionMonitor = new Monitor(mExecutor);
-    }
-
-    public Monitor.Subscription.Builder getDefaultBuilder(
-            Monitor.Callback callback) {
-        return new Monitor.Subscription.Builder(callback)
-                .addConditions(mConditions);
-    }
-
-    private Condition createMockCondition() {
-        final Condition condition = Mockito.mock(
-                Condition.class);
-        when(condition.isConditionSet()).thenReturn(true);
-        return condition;
-    }
-
-    @Test
-    public void testOverridingCondition() {
-        final Condition overridingCondition = createMockCondition();
-        final Condition regularCondition = createMockCondition();
-        final Monitor.Callback callback = Mockito.mock(
-                Monitor.Callback.class);
-
-        final Monitor.Callback referenceCallback = Mockito.mock(
-                Monitor.Callback.class);
-
-        final Monitor
-                monitor = new Monitor(mExecutor);
-
-        monitor.addSubscription(getDefaultBuilder(callback)
-                .addCondition(overridingCondition)
-                .addCondition(regularCondition)
-                .build());
-
-        monitor.addSubscription(getDefaultBuilder(referenceCallback)
-                .addCondition(regularCondition)
-                .build());
-
-        mExecutor.runAllReady();
-
-        when(overridingCondition.isOverridingCondition()).thenReturn(true);
-        when(overridingCondition.isConditionMet()).thenReturn(true);
-        when(regularCondition.isConditionMet()).thenReturn(false);
-
-        final ArgumentCaptor<Condition.Callback> mCallbackCaptor =
-                ArgumentCaptor.forClass(Condition.Callback.class);
-
-        verify(overridingCondition).addCallback(mCallbackCaptor.capture());
-
-        mCallbackCaptor.getValue().onConditionChanged(overridingCondition);
-        mExecutor.runAllReady();
-
-        verify(callback).onConditionsChanged(eq(true));
-        verify(referenceCallback).onConditionsChanged(eq(false));
-        Mockito.clearInvocations(callback);
-        Mockito.clearInvocations(referenceCallback);
-
-        when(regularCondition.isConditionMet()).thenReturn(true);
-        when(overridingCondition.isConditionMet()).thenReturn(false);
-
-        mCallbackCaptor.getValue().onConditionChanged(overridingCondition);
-        mExecutor.runAllReady();
-
-        verify(callback).onConditionsChanged(eq(false));
-        verify(referenceCallback, never()).onConditionsChanged(anyBoolean());
-    }
-
-    /**
-     * Ensures that when multiple overriding conditions are present, it is the aggregate of those
-     * conditions that are considered.
-     */
-    @Test
-    public void testMultipleOverridingConditions() {
-        final Condition overridingCondition = createMockCondition();
-        final Condition overridingCondition2 = createMockCondition();
-        final Condition regularCondition = createMockCondition();
-        final Monitor.Callback callback = Mockito.mock(
-                Monitor.Callback.class);
-
-        final Monitor
-                monitor = new Monitor(mExecutor);
-
-        monitor.addSubscription(getDefaultBuilder(callback)
-                .addCondition(overridingCondition)
-                .addCondition(overridingCondition2)
-                .build());
-
-        mExecutor.runAllReady();
-
-        when(overridingCondition.isOverridingCondition()).thenReturn(true);
-        when(overridingCondition.isConditionMet()).thenReturn(true);
-        when(overridingCondition2.isOverridingCondition()).thenReturn(true);
-        when(overridingCondition.isConditionMet()).thenReturn(false);
-        when(regularCondition.isConditionMet()).thenReturn(true);
-
-        final ArgumentCaptor<Condition.Callback> mCallbackCaptor =
-                ArgumentCaptor.forClass(Condition.Callback.class);
-
-        verify(overridingCondition).addCallback(mCallbackCaptor.capture());
-
-        mCallbackCaptor.getValue().onConditionChanged(overridingCondition);
-        mExecutor.runAllReady();
-
-        verify(callback).onConditionsChanged(eq(false));
-        Mockito.clearInvocations(callback);
-    }
-
-    // Ensure that updating a callback that is removed doesn't result in an exception due to the
-    // absence of the condition.
-    @Test
-    public void testUpdateRemovedCallback() {
-        final Monitor.Callback callback1 =
-                mock(Monitor.Callback.class);
-        final Monitor.Subscription.Token subscription1 =
-                mConditionMonitor.addSubscription(getDefaultBuilder(callback1).build());
-        ArgumentCaptor<Condition.Callback> monitorCallback =
-                ArgumentCaptor.forClass(Condition.Callback.class);
-        mExecutor.runAllReady();
-        verify(mCondition1).addCallback(monitorCallback.capture());
-        // This will execute first before the handler for onConditionChanged.
-        mConditionMonitor.removeSubscription(subscription1);
-        monitorCallback.getValue().onConditionChanged(mCondition1);
-        mExecutor.runAllReady();
-    }
-
-    @Test
-    public void addCallback_addFirstCallback_addCallbackToAllConditions() {
-        final Monitor.Callback callback1 =
-                mock(Monitor.Callback.class);
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback1).build());
-        mExecutor.runAllReady();
-        mConditions.forEach(condition -> verify(condition).addCallback(any()));
-
-        final Monitor.Callback callback2 =
-                mock(Monitor.Callback.class);
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback2).build());
-        mExecutor.runAllReady();
-        mConditions.forEach(condition -> verify(condition, times(1)).addCallback(any()));
-    }
-
-    @Test
-    public void addCallback_addFirstCallback_reportWithDefaultValue() {
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(false);
-    }
-
-    @Test
-    public void addCallback_addSecondCallback_reportWithExistingValue() {
-        final Monitor.Callback callback1 =
-                mock(Monitor.Callback.class);
-        final Condition condition = mock(
-                Condition.class);
-        when(condition.isConditionMet()).thenReturn(true);
-        final Monitor
-                monitor = new Monitor(mExecutor);
-        monitor.addSubscription(new Monitor.Subscription.Builder(callback1)
-                .addCondition(condition)
-                .build());
-
-        final Monitor.Callback callback2 =
-                mock(Monitor.Callback.class);
-        monitor.addSubscription(new Monitor.Subscription.Builder(callback2)
-                .addCondition(condition)
-                .build());
-        mExecutor.runAllReady();
-        verify(callback2).onConditionsChanged(eq(true));
-    }
-
-    @Test
-    public void addCallback_noConditions_reportAllConditionsMet() {
-        final Monitor
-                monitor = new Monitor(mExecutor);
-        final Monitor.Callback callback = mock(
-                Monitor.Callback.class);
-
-        monitor.addSubscription(new Monitor.Subscription.Builder(callback).build());
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(true);
-    }
-
-    @Test
-    public void addCallback_preCondition_noConditions_reportAllConditionsMet() {
-        final Monitor
-                monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(mCondition1)));
-        final Monitor.Callback callback = mock(
-                Monitor.Callback.class);
-
-        monitor.addSubscription(new Monitor.Subscription.Builder(callback).build());
-        mExecutor.runAllReady();
-        verify(callback, never()).onConditionsChanged(true);
-        mCondition1.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(true);
-    }
-
-    @Test
-    public void removeCallback_noFailureOnDoubleRemove() {
-        final Condition condition = mock(
-                Condition.class);
-        final Monitor
-                monitor = new Monitor(mExecutor);
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-        final Monitor.Subscription.Token token = monitor.addSubscription(
-                new Monitor.Subscription.Builder(callback).addCondition(condition).build()
-        );
-        monitor.removeSubscription(token);
-        mExecutor.runAllReady();
-        // Ensure second removal doesn't cause an exception.
-        monitor.removeSubscription(token);
-        mExecutor.runAllReady();
-    }
-
-    @Test
-    public void removeCallback_shouldNoLongerReceiveUpdate() {
-        final Condition condition = mock(
-                Condition.class);
-        final Monitor
-                monitor = new Monitor(mExecutor);
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-        final Monitor.Subscription.Token token = monitor.addSubscription(
-                new Monitor.Subscription.Builder(callback).addCondition(condition).build()
-        );
-        monitor.removeSubscription(token);
-        mExecutor.runAllReady();
-        clearInvocations(callback);
-
-        final ArgumentCaptor<Condition.Callback> conditionCallbackCaptor =
-                ArgumentCaptor.forClass(Condition.Callback.class);
-        verify(condition).addCallback(conditionCallbackCaptor.capture());
-
-        final Condition.Callback conditionCallback = conditionCallbackCaptor.getValue();
-        verify(condition).removeCallback(conditionCallback);
-    }
-
-    @Test
-    public void removeCallback_removeLastCallback_removeCallbackFromAllConditions() {
-        final Monitor.Callback callback1 =
-                mock(Monitor.Callback.class);
-        final Monitor.Callback callback2 =
-                mock(Monitor.Callback.class);
-        final Monitor.Subscription.Token subscription1 =
-                mConditionMonitor.addSubscription(getDefaultBuilder(callback1).build());
-        final Monitor.Subscription.Token subscription2 =
-                mConditionMonitor.addSubscription(getDefaultBuilder(callback2).build());
-
-        mConditionMonitor.removeSubscription(subscription1);
-        mExecutor.runAllReady();
-        mConditions.forEach(condition -> verify(condition, never()).removeCallback(any()));
-
-        mConditionMonitor.removeSubscription(subscription2);
-        mExecutor.runAllReady();
-        mConditions.forEach(condition -> verify(condition).removeCallback(any()));
-    }
-
-    @Test
-    public void updateCallbacks_allConditionsMet_reportTrue() {
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
-        clearInvocations(callback);
-
-        mCondition1.fakeUpdateCondition(true);
-        mCondition2.fakeUpdateCondition(true);
-        mCondition3.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-
-        verify(callback).onConditionsChanged(true);
-    }
-
-    @Test
-    public void updateCallbacks_oneConditionStoppedMeeting_reportFalse() {
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
-
-        mCondition1.fakeUpdateCondition(true);
-        mCondition2.fakeUpdateCondition(true);
-        mCondition3.fakeUpdateCondition(true);
-        clearInvocations(callback);
-
-        mCondition1.fakeUpdateCondition(false);
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(false);
-    }
-
-    @Test
-    public void updateCallbacks_shouldOnlyUpdateWhenValueChanges() {
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(false);
-        clearInvocations(callback);
-
-        mCondition1.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-        verify(callback, never()).onConditionsChanged(anyBoolean());
-
-        mCondition2.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-        verify(callback, never()).onConditionsChanged(anyBoolean());
-
-        mCondition3.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(true);
-    }
-
-    @Test
-    public void clearCondition_shouldUpdateValue() {
-        mCondition1.fakeUpdateCondition(false);
-        mCondition2.fakeUpdateCondition(true);
-        mCondition3.fakeUpdateCondition(true);
-
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(false);
-
-        mCondition1.clearCondition();
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(true);
-    }
-
-    @Test
-    public void unsetCondition_shouldNotAffectValue() {
-        final FakeCondition settableCondition = new FakeCondition(mScope, null, false);
-        mCondition1.fakeUpdateCondition(true);
-        mCondition2.fakeUpdateCondition(true);
-        mCondition3.fakeUpdateCondition(true);
-
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback)
-                .addCondition(settableCondition)
-                .build());
-
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(true);
-    }
-
-    @Test
-    public void setUnsetCondition_shouldAffectValue() {
-        final FakeCondition settableCondition = new FakeCondition(mScope, null, false);
-        mCondition1.fakeUpdateCondition(true);
-        mCondition2.fakeUpdateCondition(true);
-        mCondition3.fakeUpdateCondition(true);
-
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback)
-                .addCondition(settableCondition)
-                .build());
-
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(true);
-        clearInvocations(callback);
-
-        settableCondition.fakeUpdateCondition(false);
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(false);
-        clearInvocations(callback);
-
-
-        settableCondition.clearCondition();
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(true);
-    }
-
-    @Test
-    public void clearingOverridingCondition_shouldBeExcluded() {
-        final FakeCondition overridingCondition = new FakeCondition(mScope, true, true);
-        mCondition1.fakeUpdateCondition(false);
-        mCondition2.fakeUpdateCondition(false);
-        mCondition3.fakeUpdateCondition(false);
-
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback)
-                .addCondition(overridingCondition)
-                .build());
-
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(true);
-        clearInvocations(callback);
-
-        overridingCondition.clearCondition();
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(false);
-    }
-
-    @Test
-    public void settingUnsetOverridingCondition_shouldBeIncluded() {
-        final FakeCondition overridingCondition = new FakeCondition(mScope, null, true);
-        mCondition1.fakeUpdateCondition(false);
-        mCondition2.fakeUpdateCondition(false);
-        mCondition3.fakeUpdateCondition(false);
-
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-
-        mConditionMonitor.addSubscription(getDefaultBuilder(callback)
-                .addCondition(overridingCondition)
-                .build());
-
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(false);
-        clearInvocations(callback);
-
-        overridingCondition.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-        verify(callback).onConditionsChanged(true);
-    }
-
-    /**
-     * Ensures that the result of a condition being true leads to its nested condition being
-     * activated.
-     */
-    @Test
-    public void testNestedCondition() {
-        mCondition1.fakeUpdateCondition(false);
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-
-        mCondition2.fakeUpdateCondition(false);
-
-        // Create a nested condition
-        mConditionMonitor.addSubscription(new Monitor.Subscription.Builder(
-                new Monitor.Subscription.Builder(callback)
-                        .addCondition(mCondition2)
-                        .build())
-                .addCondition(mCondition1)
-                .build());
-
-        mExecutor.runAllReady();
-
-        // Ensure the nested condition callback is not called at all.
-        verify(callback, never()).onActiveChanged(anyBoolean());
-        verify(callback, never()).onConditionsChanged(anyBoolean());
-
-        // Update the inner condition to true and ensure that the nested condition is not triggered.
-        mCondition2.fakeUpdateCondition(true);
-        verify(callback, never()).onConditionsChanged(anyBoolean());
-        mCondition2.fakeUpdateCondition(false);
-
-        // Set outer condition and make sure the inner condition becomes active and reports that
-        // conditions aren't met
-        mCondition1.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-
-        verify(callback).onActiveChanged(eq(true));
-        verify(callback).onConditionsChanged(eq(false));
-
-        Mockito.clearInvocations(callback);
-
-        // Update the inner condition and make sure the callback is updated.
-        mCondition2.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-
-        verify(callback).onConditionsChanged(true);
-
-        Mockito.clearInvocations(callback);
-        // Invalidate outer condition and make sure callback is informed, but the last state is
-        // not affected.
-        mCondition1.fakeUpdateCondition(false);
-        mExecutor.runAllReady();
-
-        verify(callback).onActiveChanged(eq(false));
-        verify(callback, never()).onConditionsChanged(anyBoolean());
-    }
-
-    /**
-     * Ensure preconditions are applied to every subscription added to a monitor.
-     */
-    @Test
-    public void testPreconditionMonitor() {
-        final Monitor.Callback callback =
-                mock(Monitor.Callback.class);
-
-        mCondition2.fakeUpdateCondition(true);
-        final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(mCondition1)));
-
-        monitor.addSubscription(new Monitor.Subscription.Builder(callback)
-                .addCondition(mCondition2)
-                .build());
-
-        mExecutor.runAllReady();
-
-        verify(callback, never()).onActiveChanged(anyBoolean());
-        verify(callback, never()).onConditionsChanged(anyBoolean());
-
-        mCondition1.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-
-        verify(callback).onActiveChanged(eq(true));
-        verify(callback).onConditionsChanged(eq(true));
-    }
-
-    @Test
-    public void testLoggingCallback() {
-        final Monitor monitor = new Monitor(mExecutor, Collections.emptySet(), mLogBuffer);
-
-        final FakeCondition condition = new FakeCondition(mScope);
-        final FakeCondition overridingCondition = new FakeCondition(
-                mScope,
-                /* initialValue= */ false,
-                /* overriding= */ true);
-
-        final Monitor.Callback callback = mock(Monitor.Callback.class);
-        monitor.addSubscription(getDefaultBuilder(callback)
-                .addCondition(condition)
-                .addCondition(overridingCondition)
-                .build());
-        mExecutor.runAllReady();
-
-        // condition set to true
-        condition.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-        verify(mLogBuffer).logChange("", "FakeCondition", "True");
-
-        // condition set to false
-        condition.fakeUpdateCondition(false);
-        mExecutor.runAllReady();
-        verify(mLogBuffer).logChange("", "FakeCondition", "False");
-
-        // condition unset
-        condition.fakeClearCondition();
-        mExecutor.runAllReady();
-        verify(mLogBuffer).logChange("", "FakeCondition", "Invalid");
-
-        // overriding condition set to true
-        overridingCondition.fakeUpdateCondition(true);
-        mExecutor.runAllReady();
-        verify(mLogBuffer).logChange("", "FakeCondition[OVRD]", "True");
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionMonitorTest.kt
new file mode 100644
index 0000000..dc45c53
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionMonitorTest.kt
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.condition
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.log.TableLogBufferBase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import java.util.Arrays
+import java.util.function.Consumer
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+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
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ConditionMonitorTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+
+    private lateinit var condition1: FakeCondition
+    private lateinit var condition2: FakeCondition
+    private lateinit var condition3: FakeCondition
+    private lateinit var conditions: HashSet<Condition>
+    private val executor = FakeExecutor(FakeSystemClock())
+
+    @Mock private lateinit var logBuffer: TableLogBufferBase
+
+    private lateinit var conditionMonitor: Monitor
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        condition1 = Mockito.spy(FakeCondition(kosmos.testScope))
+        condition2 = Mockito.spy(FakeCondition(kosmos.testScope))
+        condition3 = Mockito.spy(FakeCondition(kosmos.testScope))
+        conditions = HashSet(listOf(condition1, condition2, condition3))
+
+        conditionMonitor = Monitor(executor)
+    }
+
+    fun getDefaultBuilder(callback: Monitor.Callback): Monitor.Subscription.Builder {
+        return Monitor.Subscription.Builder(callback).addConditions(conditions)
+    }
+
+    private fun createMockCondition(): Condition {
+        val condition: Condition = mock()
+        whenever(condition.isConditionSet).thenReturn(true)
+        return condition
+    }
+
+    @Test
+    fun testOverridingCondition() =
+        kosmos.runTest {
+            val overridingCondition = createMockCondition()
+            val regularCondition = createMockCondition()
+            val callback: Monitor.Callback = mock()
+            val referenceCallback: Monitor.Callback = mock()
+
+            val monitor = Monitor(executor)
+
+            monitor.addSubscription(
+                getDefaultBuilder(callback)
+                    .addCondition(overridingCondition)
+                    .addCondition(regularCondition)
+                    .build()
+            )
+
+            monitor.addSubscription(
+                getDefaultBuilder(referenceCallback).addCondition(regularCondition).build()
+            )
+
+            executor.runAllReady()
+
+            whenever(overridingCondition.isOverridingCondition).thenReturn(true)
+            whenever(overridingCondition.isConditionMet).thenReturn(true)
+            whenever(regularCondition.isConditionMet).thenReturn(false)
+
+            val callbackCaptor = argumentCaptor<Condition.Callback>()
+
+            Mockito.verify(overridingCondition).addCallback(callbackCaptor.capture())
+
+            callbackCaptor.lastValue.onConditionChanged(overridingCondition)
+            executor.runAllReady()
+
+            Mockito.verify(callback).onConditionsChanged(eq(true))
+            Mockito.verify(referenceCallback).onConditionsChanged(eq(false))
+            Mockito.clearInvocations(callback)
+            Mockito.clearInvocations(referenceCallback)
+
+            whenever(regularCondition.isConditionMet).thenReturn(true)
+            whenever(overridingCondition.isConditionMet).thenReturn(false)
+
+            callbackCaptor.lastValue.onConditionChanged(overridingCondition)
+            executor.runAllReady()
+
+            Mockito.verify(callback).onConditionsChanged(eq(false))
+            Mockito.verify(referenceCallback, Mockito.never()).onConditionsChanged(anyBoolean())
+        }
+
+    /**
+     * Ensures that when multiple overriding conditions are present, it is the aggregate of those
+     * conditions that are considered.
+     */
+    @Test
+    fun testMultipleOverridingConditions() =
+        kosmos.runTest {
+            val overridingCondition = createMockCondition()
+            val overridingCondition2 = createMockCondition()
+            val regularCondition = createMockCondition()
+            val callback: Monitor.Callback = mock()
+
+            val monitor = Monitor(executor)
+
+            monitor.addSubscription(
+                getDefaultBuilder(callback)
+                    .addCondition(overridingCondition)
+                    .addCondition(overridingCondition2)
+                    .build()
+            )
+
+            executor.runAllReady()
+
+            whenever(overridingCondition.isOverridingCondition).thenReturn(true)
+            whenever(overridingCondition.isConditionMet).thenReturn(true)
+            whenever(overridingCondition2.isOverridingCondition).thenReturn(true)
+            whenever(overridingCondition.isConditionMet).thenReturn(false)
+            whenever(regularCondition.isConditionMet).thenReturn(true)
+
+            val mCallbackCaptor = argumentCaptor<Condition.Callback>()
+
+            Mockito.verify(overridingCondition).addCallback(mCallbackCaptor.capture())
+
+            mCallbackCaptor.lastValue.onConditionChanged(overridingCondition)
+            executor.runAllReady()
+
+            Mockito.verify(callback).onConditionsChanged(eq(false))
+            Mockito.clearInvocations(callback)
+        }
+
+    // Ensure that updating a callback that is removed doesn't result in an exception due to the
+    // absence of the condition.
+    @Test
+    fun testUpdateRemovedCallback() =
+        kosmos.runTest {
+            val callback1: Monitor.Callback = mock()
+            val subscription1 =
+                conditionMonitor.addSubscription(getDefaultBuilder(callback1).build())
+            val monitorCallback = argumentCaptor<Condition.Callback>()
+            executor.runAllReady()
+            Mockito.verify(condition1).addCallback(monitorCallback.capture())
+            // This will execute first before the handler for onConditionChanged.
+            conditionMonitor.removeSubscription(subscription1)
+            monitorCallback.lastValue.onConditionChanged(condition1)
+            executor.runAllReady()
+        }
+
+    @Test
+    fun addCallback_addFirstCallback_addCallbackToAllConditions() {
+        val callback1: Monitor.Callback = mock()
+        conditionMonitor.addSubscription(getDefaultBuilder(callback1).build())
+        executor.runAllReady()
+        conditions.forEach(
+            Consumer { condition: Condition -> Mockito.verify(condition).addCallback(any()) }
+        )
+
+        val callback2: Monitor.Callback = mock()
+        conditionMonitor.addSubscription(getDefaultBuilder(callback2).build())
+        executor.runAllReady()
+        conditions.forEach(
+            Consumer { condition: Condition ->
+                Mockito.verify(condition, Mockito.times(1)).addCallback(any())
+            }
+        )
+    }
+
+    @Test
+    fun addCallback_addFirstCallback_reportWithDefaultValue() =
+        kosmos.runTest {
+            val callback: Monitor.Callback = mock()
+            conditionMonitor.addSubscription(getDefaultBuilder(callback).build())
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(false)
+        }
+
+    @Test
+    fun addCallback_addSecondCallback_reportWithExistingValue() =
+        kosmos.runTest {
+            val callback1: Monitor.Callback = mock()
+            val condition: Condition = mock()
+
+            whenever(condition.isConditionMet).thenReturn(true)
+            val monitor = Monitor(executor)
+            monitor.addSubscription(
+                Monitor.Subscription.Builder(callback1).addCondition(condition).build()
+            )
+
+            val callback2: Monitor.Callback = mock()
+            monitor.addSubscription(
+                Monitor.Subscription.Builder(callback2).addCondition(condition).build()
+            )
+            executor.runAllReady()
+            Mockito.verify(callback2).onConditionsChanged(eq(true))
+        }
+
+    @Test
+    fun addCallback_noConditions_reportAllConditionsMet() =
+        kosmos.runTest {
+            val monitor = Monitor(executor)
+            val callback: Monitor.Callback = mock()
+
+            monitor.addSubscription(Monitor.Subscription.Builder(callback).build())
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(true)
+        }
+
+    @Test
+    fun addCallback_preCondition_noConditions_reportAllConditionsMet() =
+        kosmos.runTest {
+            val monitor = Monitor(executor, HashSet<Condition?>(Arrays.asList(condition1)))
+            val callback: Monitor.Callback = mock()
+
+            monitor.addSubscription(Monitor.Subscription.Builder(callback).build())
+            executor.runAllReady()
+            Mockito.verify(callback, Mockito.never()).onConditionsChanged(true)
+            condition1.fakeUpdateCondition(true)
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(true)
+        }
+
+    @Test
+    fun removeCallback_noFailureOnDoubleRemove() =
+        kosmos.runTest {
+            val condition: Condition = mock()
+            val monitor = Monitor(executor)
+            val callback: Monitor.Callback = mock()
+            val token =
+                monitor.addSubscription(
+                    Monitor.Subscription.Builder(callback).addCondition(condition).build()
+                )
+            monitor.removeSubscription(token)
+            executor.runAllReady()
+            // Ensure second removal doesn't cause an exception.
+            monitor.removeSubscription(token)
+            executor.runAllReady()
+        }
+
+    @Test
+    fun removeCallback_shouldNoLongerReceiveUpdate() =
+        kosmos.runTest {
+            val condition: Condition = mock()
+            val monitor = Monitor(executor)
+            val callback: Monitor.Callback = mock()
+            val token =
+                monitor.addSubscription(
+                    Monitor.Subscription.Builder(callback).addCondition(condition).build()
+                )
+            monitor.removeSubscription(token)
+            executor.runAllReady()
+            Mockito.clearInvocations(callback)
+
+            val conditionCallbackCaptor = argumentCaptor<Condition.Callback>()
+            Mockito.verify(condition).addCallback(conditionCallbackCaptor.capture())
+
+            val conditionCallback = conditionCallbackCaptor.lastValue
+            Mockito.verify(condition).removeCallback(conditionCallback)
+        }
+
+    @Test
+    fun removeCallback_removeLastCallback_removeCallbackFromAllConditions() =
+        kosmos.runTest {
+            val callback1: Monitor.Callback = mock()
+            val callback2: Monitor.Callback = mock()
+            val subscription1 =
+                conditionMonitor.addSubscription(getDefaultBuilder(callback1).build())
+            val subscription2 =
+                conditionMonitor.addSubscription(getDefaultBuilder(callback2).build())
+
+            conditionMonitor.removeSubscription(subscription1)
+            executor.runAllReady()
+            conditions.forEach(
+                Consumer { condition: Condition ->
+                    verify(condition, Mockito.never()).removeCallback(any())
+                }
+            )
+
+            conditionMonitor.removeSubscription(subscription2)
+            executor.runAllReady()
+            conditions.forEach(
+                Consumer { condition: Condition -> Mockito.verify(condition).removeCallback(any()) }
+            )
+        }
+
+    @Test
+    fun updateCallbacks_allConditionsMet_reportTrue() =
+        kosmos.runTest {
+            val callback: Monitor.Callback = mock()
+            conditionMonitor.addSubscription(getDefaultBuilder(callback).build())
+            Mockito.clearInvocations(callback)
+
+            condition1.fakeUpdateCondition(true)
+            condition2.fakeUpdateCondition(true)
+            condition3.fakeUpdateCondition(true)
+            executor.runAllReady()
+
+            Mockito.verify(callback).onConditionsChanged(true)
+        }
+
+    @Test
+    fun updateCallbacks_oneConditionStoppedMeeting_reportFalse() =
+        kosmos.runTest {
+            val callback: Monitor.Callback = mock()
+            conditionMonitor.addSubscription(getDefaultBuilder(callback).build())
+
+            condition1.fakeUpdateCondition(true)
+            condition2.fakeUpdateCondition(true)
+            condition3.fakeUpdateCondition(true)
+            Mockito.clearInvocations(callback)
+
+            condition1.fakeUpdateCondition(false)
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(false)
+        }
+
+    @Test
+    fun updateCallbacks_shouldOnlyUpdateWhenValueChanges() =
+        kosmos.runTest {
+            val callback: Monitor.Callback = mock()
+            conditionMonitor.addSubscription(getDefaultBuilder(callback).build())
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(false)
+            Mockito.clearInvocations(callback)
+
+            condition1.fakeUpdateCondition(true)
+            executor.runAllReady()
+            Mockito.verify(callback, Mockito.never()).onConditionsChanged(anyBoolean())
+
+            condition2.fakeUpdateCondition(true)
+            executor.runAllReady()
+            Mockito.verify(callback, Mockito.never()).onConditionsChanged(anyBoolean())
+
+            condition3.fakeUpdateCondition(true)
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(true)
+        }
+
+    @Test
+    fun clearCondition_shouldUpdateValue() =
+        kosmos.runTest {
+            condition1.fakeUpdateCondition(false)
+            condition2.fakeUpdateCondition(true)
+            condition3.fakeUpdateCondition(true)
+
+            val callback: Monitor.Callback = mock()
+            conditionMonitor.addSubscription(getDefaultBuilder(callback).build())
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(false)
+
+            condition1.clearCondition()
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(true)
+        }
+
+    @Test
+    fun unsetCondition_shouldNotAffectValue() =
+        kosmos.runTest {
+            val settableCondition = FakeCondition(testScope, null, false)
+            condition1.fakeUpdateCondition(true)
+            condition2.fakeUpdateCondition(true)
+            condition3.fakeUpdateCondition(true)
+
+            val callback: Monitor.Callback = mock()
+
+            conditionMonitor.addSubscription(
+                getDefaultBuilder(callback).addCondition(settableCondition).build()
+            )
+
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(true)
+        }
+
+    @Test
+    fun setUnsetCondition_shouldAffectValue() =
+        kosmos.runTest {
+            val settableCondition = FakeCondition(testScope, null, false)
+            condition1.fakeUpdateCondition(true)
+            condition2.fakeUpdateCondition(true)
+            condition3.fakeUpdateCondition(true)
+
+            val callback: Monitor.Callback = mock()
+
+            conditionMonitor.addSubscription(
+                getDefaultBuilder(callback).addCondition(settableCondition).build()
+            )
+
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(true)
+            Mockito.clearInvocations(callback)
+
+            settableCondition.fakeUpdateCondition(false)
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(false)
+            Mockito.clearInvocations(callback)
+
+            settableCondition.clearCondition()
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(true)
+        }
+
+    @Test
+    fun clearingOverridingCondition_shouldBeExcluded() =
+        kosmos.runTest {
+            val overridingCondition = FakeCondition(testScope, true, true)
+            condition1.fakeUpdateCondition(false)
+            condition2.fakeUpdateCondition(false)
+            condition3.fakeUpdateCondition(false)
+
+            val callback: Monitor.Callback = mock()
+
+            conditionMonitor.addSubscription(
+                getDefaultBuilder(callback).addCondition(overridingCondition).build()
+            )
+
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(true)
+            Mockito.clearInvocations(callback)
+
+            overridingCondition.clearCondition()
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(false)
+        }
+
+    @Test
+    fun settingUnsetOverridingCondition_shouldBeIncluded() =
+        kosmos.runTest {
+            val overridingCondition = FakeCondition(testScope, null, true)
+            condition1.fakeUpdateCondition(false)
+            condition2.fakeUpdateCondition(false)
+            condition3.fakeUpdateCondition(false)
+
+            val callback: Monitor.Callback = mock()
+
+            conditionMonitor.addSubscription(
+                getDefaultBuilder(callback).addCondition(overridingCondition).build()
+            )
+
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(false)
+            Mockito.clearInvocations(callback)
+
+            overridingCondition.fakeUpdateCondition(true)
+            executor.runAllReady()
+            Mockito.verify(callback).onConditionsChanged(true)
+        }
+
+    /**
+     * Ensures that the result of a condition being true leads to its nested condition being
+     * activated.
+     */
+    @Test
+    fun testNestedCondition() =
+        kosmos.runTest {
+            condition1.fakeUpdateCondition(false)
+            val callback: Monitor.Callback = mock()
+
+            condition2.fakeUpdateCondition(false)
+
+            // Create a nested condition
+            conditionMonitor.addSubscription(
+                Monitor.Subscription.Builder(
+                        Monitor.Subscription.Builder(callback).addCondition(condition2).build()
+                    )
+                    .addCondition(condition1)
+                    .build()
+            )
+
+            executor.runAllReady()
+
+            // Ensure the nested condition callback is not called at all.
+            Mockito.verify(callback, Mockito.never()).onActiveChanged(anyBoolean())
+            Mockito.verify(callback, Mockito.never()).onConditionsChanged(anyBoolean())
+
+            // Update the inner condition to true and ensure that the nested condition is not
+            // triggered.
+            condition2.fakeUpdateCondition(true)
+            Mockito.verify(callback, Mockito.never()).onConditionsChanged(anyBoolean())
+            condition2.fakeUpdateCondition(false)
+
+            // Set outer condition and make sure the inner condition becomes active and reports that
+            // conditions aren't met
+            condition1.fakeUpdateCondition(true)
+            executor.runAllReady()
+
+            Mockito.verify(callback).onActiveChanged(eq(true))
+            Mockito.verify(callback).onConditionsChanged(eq(false))
+
+            Mockito.clearInvocations(callback)
+
+            // Update the inner condition and make sure the callback is updated.
+            condition2.fakeUpdateCondition(true)
+            executor.runAllReady()
+
+            Mockito.verify(callback).onConditionsChanged(true)
+
+            Mockito.clearInvocations(callback)
+            // Invalidate outer condition and make sure callback is informed, but the last state is
+            // not affected.
+            condition1.fakeUpdateCondition(false)
+            executor.runAllReady()
+
+            Mockito.verify(callback).onActiveChanged(eq(false))
+            Mockito.verify(callback, Mockito.never()).onConditionsChanged(anyBoolean())
+        }
+
+    /** Ensure preconditions are applied to every subscription added to a monitor. */
+    @Test
+    fun testPreconditionMonitor() {
+        val callback: Monitor.Callback = mock()
+
+        condition2.fakeUpdateCondition(true)
+        val monitor = Monitor(executor, HashSet<Condition?>(listOf(condition1)))
+
+        monitor.addSubscription(
+            Monitor.Subscription.Builder(callback).addCondition(condition2).build()
+        )
+
+        executor.runAllReady()
+
+        Mockito.verify(callback, Mockito.never()).onActiveChanged(anyBoolean())
+        Mockito.verify(callback, Mockito.never()).onConditionsChanged(anyBoolean())
+
+        condition1.fakeUpdateCondition(true)
+        executor.runAllReady()
+
+        Mockito.verify(callback).onActiveChanged(eq(true))
+        Mockito.verify(callback).onConditionsChanged(eq(true))
+    }
+
+    @Test
+    fun testLoggingCallback() =
+        kosmos.runTest {
+            val monitor = Monitor(executor, emptySet(), logBuffer)
+
+            val condition = FakeCondition(testScope)
+            val overridingCondition =
+                FakeCondition(testScope, /* initialValue= */ false, /* overriding= */ true)
+
+            val callback: Monitor.Callback = mock()
+            monitor.addSubscription(
+                getDefaultBuilder(callback)
+                    .addCondition(condition)
+                    .addCondition(overridingCondition)
+                    .build()
+            )
+            executor.runAllReady()
+
+            // condition set to true
+            condition.fakeUpdateCondition(true)
+            executor.runAllReady()
+            Mockito.verify(logBuffer).logChange("", "FakeCondition", "True")
+
+            // condition set to false
+            condition.fakeUpdateCondition(false)
+            executor.runAllReady()
+            Mockito.verify(logBuffer).logChange("", "FakeCondition", "False")
+
+            // condition unset
+            condition.fakeClearCondition()
+            executor.runAllReady()
+            Mockito.verify(logBuffer).logChange("", "FakeCondition", "Invalid")
+
+            // overriding condition set to true
+            overridingCondition.fakeUpdateCondition(true)
+            executor.runAllReady()
+            Mockito.verify(logBuffer).logChange("", "FakeCondition[OVRD]", "True")
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionTest.java
deleted file mode 100644
index a224843..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionTest.java
+++ /dev/null
@@ -1,163 +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.shared.condition;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import kotlinx.coroutines.CoroutineScope;
-
-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)
-public class ConditionTest extends SysuiTestCase {
-    @Mock
-    CoroutineScope mScope;
-
-    private FakeCondition mCondition;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mCondition = spy(new FakeCondition(mScope));
-    }
-
-    @Test
-    public void addCallback_addFirstCallback_triggerStart() {
-        final Condition.Callback callback = mock(
-                Condition.Callback.class);
-        mCondition.addCallback(callback);
-        verify(mCondition).start();
-    }
-
-    @Test
-    public void addCallback_addMultipleCallbacks_triggerStartOnlyOnce() {
-        final Condition.Callback callback1 = mock(
-                Condition.Callback.class);
-        final Condition.Callback callback2 = mock(
-                Condition.Callback.class);
-        final Condition.Callback callback3 = mock(
-                Condition.Callback.class);
-
-        mCondition.addCallback(callback1);
-        mCondition.addCallback(callback2);
-        mCondition.addCallback(callback3);
-
-        verify(mCondition, times(1)).start();
-    }
-
-    @Test
-    public void addCallback_alreadyStarted_triggerUpdate() {
-        final Condition.Callback callback1 = mock(
-                Condition.Callback.class);
-        mCondition.addCallback(callback1);
-
-        mCondition.fakeUpdateCondition(true);
-
-        final Condition.Callback callback2 = mock(
-                Condition.Callback.class);
-        mCondition.addCallback(callback2);
-        verify(callback2).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionMet()).isTrue();
-    }
-
-    @Test
-    public void removeCallback_removeLastCallback_triggerStop() {
-        final Condition.Callback callback = mock(
-                Condition.Callback.class);
-        mCondition.addCallback(callback);
-        verify(mCondition, never()).stop();
-
-        mCondition.removeCallback(callback);
-        verify(mCondition).stop();
-    }
-
-    @Test
-    public void updateCondition_falseToTrue_reportTrue() {
-        mCondition.fakeUpdateCondition(false);
-
-        final Condition.Callback callback = mock(
-                Condition.Callback.class);
-        mCondition.addCallback(callback);
-
-        mCondition.fakeUpdateCondition(true);
-        verify(callback).onConditionChanged(eq(mCondition));
-        assertThat(mCondition.isConditionMet()).isTrue();
-    }
-
-    @Test
-    public void updateCondition_trueToFalse_reportFalse() {
-        mCondition.fakeUpdateCondition(true);
-
-        final Condition.Callback callback = mock(
-                Condition.Callback.class);
-        mCondition.addCallback(callback);
-
-        mCondition.fakeUpdateCondition(false);
-        verify(callback).onConditionChanged(eq(mCondition));
-        assertThat(mCondition.isConditionMet()).isFalse();
-    }
-
-    @Test
-    public void updateCondition_trueToTrue_reportNothing() {
-        mCondition.fakeUpdateCondition(true);
-
-        final Condition.Callback callback = mock(
-                Condition.Callback.class);
-        mCondition.addCallback(callback);
-
-        mCondition.fakeUpdateCondition(true);
-        verify(callback, never()).onConditionChanged(eq(mCondition));
-    }
-
-    @Test
-    public void updateCondition_falseToFalse_reportNothing() {
-        mCondition.fakeUpdateCondition(false);
-
-        final Condition.Callback callback = mock(
-                Condition.Callback.class);
-        mCondition.addCallback(callback);
-
-        mCondition.fakeUpdateCondition(false);
-        verify(callback, never()).onConditionChanged(eq(mCondition));
-    }
-
-    @Test
-    public void clearCondition_reportsNotSet() {
-        mCondition.fakeUpdateCondition(false);
-        assertThat(mCondition.isConditionSet()).isTrue();
-        mCondition.clearCondition();
-        assertThat(mCondition.isConditionSet()).isFalse();
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionTest.kt
new file mode 100644
index 0000000..f72185b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.condition
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ConditionTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private lateinit var underTest: FakeCondition
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        underTest = Mockito.spy(FakeCondition(kosmos.testScope))
+    }
+
+    @Test
+    fun addCallback_addFirstCallback_triggerStart() =
+        kosmos.runTest {
+            val callback = mock<Condition.Callback>()
+            underTest.addCallback(callback)
+            runCurrent()
+            Mockito.verify(underTest).start()
+        }
+
+    @Test
+    fun addCallback_addMultipleCallbacks_triggerStartOnlyOnce() =
+        kosmos.runTest {
+            val callback1 = mock<Condition.Callback>()
+            val callback2 = mock<Condition.Callback>()
+            val callback3 = mock<Condition.Callback>()
+
+            underTest.addCallback(callback1)
+            underTest.addCallback(callback2)
+            underTest.addCallback(callback3)
+
+            runCurrent()
+            Mockito.verify(underTest).start()
+        }
+
+    @Test
+    fun addCallback_alreadyStarted_triggerUpdate() =
+        kosmos.runTest {
+            val callback1 = mock<Condition.Callback>()
+            underTest.addCallback(callback1)
+
+            underTest.fakeUpdateCondition(true)
+
+            val callback2 = mock<Condition.Callback>()
+            underTest.addCallback(callback2)
+            Mockito.verify(callback2).onConditionChanged(underTest)
+            Truth.assertThat(underTest.isConditionMet).isTrue()
+        }
+
+    @Test
+    fun removeCallback_removeLastCallback_triggerStop() =
+        kosmos.runTest {
+            val callback = mock<Condition.Callback>()
+            underTest.addCallback(callback)
+            Mockito.verify(underTest, Mockito.never()).stop()
+
+            underTest.removeCallback(callback)
+            Mockito.verify(underTest).stop()
+        }
+
+    @Test
+    fun updateCondition_falseToTrue_reportTrue() =
+        kosmos.runTest {
+            underTest.fakeUpdateCondition(false)
+
+            val callback = mock<Condition.Callback>()
+            underTest.addCallback(callback)
+
+            underTest.fakeUpdateCondition(true)
+            Mockito.verify(callback).onConditionChanged(eq(underTest))
+            Truth.assertThat(underTest.isConditionMet).isTrue()
+        }
+
+    @Test
+    fun updateCondition_trueToFalse_reportFalse() =
+        kosmos.runTest {
+            underTest.fakeUpdateCondition(true)
+
+            val callback = mock<Condition.Callback>()
+            underTest.addCallback(callback)
+
+            underTest.fakeUpdateCondition(false)
+            Mockito.verify(callback).onConditionChanged(eq(underTest))
+            Truth.assertThat(underTest.isConditionMet).isFalse()
+        }
+
+    @Test
+    fun updateCondition_trueToTrue_reportNothing() =
+        kosmos.runTest {
+            underTest.fakeUpdateCondition(true)
+
+            val callback = mock<Condition.Callback>()
+            underTest.addCallback(callback)
+
+            underTest.fakeUpdateCondition(true)
+            Mockito.verify(callback, Mockito.never()).onConditionChanged(eq(underTest))
+        }
+
+    @Test
+    fun updateCondition_falseToFalse_reportNothing() =
+        kosmos.runTest {
+            underTest.fakeUpdateCondition(false)
+
+            val callback = mock<Condition.Callback>()
+            underTest.addCallback(callback)
+
+            underTest.fakeUpdateCondition(false)
+            Mockito.verify(callback, Mockito.never()).onConditionChanged(eq(underTest))
+        }
+
+    @Test
+    fun clearCondition_reportsNotSet() =
+        kosmos.runTest {
+            underTest.fakeUpdateCondition(false)
+            Truth.assertThat(underTest.isConditionSet).isTrue()
+            underTest.clearCondition()
+            Truth.assertThat(underTest.isConditionSet).isFalse()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java
deleted file mode 100644
index da660e2..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.java
+++ /dev/null
@@ -1,54 +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.shared.condition;
-
-import kotlinx.coroutines.CoroutineScope;
-
-/**
- * Fake implementation of {@link Condition}, and provides a way for tests to update
- * condition fulfillment.
- */
-public class FakeCondition extends Condition {
-    FakeCondition(CoroutineScope scope) {
-        super(scope);
-    }
-
-    FakeCondition(CoroutineScope scope, Boolean initialValue, boolean overriding) {
-        super(scope, initialValue, overriding);
-    }
-
-    @Override
-    public void start() {
-    }
-
-    @Override
-    public void stop() {
-    }
-
-    @Override
-    public int getStartStrategy() {
-        return START_EAGERLY;
-    }
-
-    public void fakeUpdateCondition(boolean isConditionMet) {
-        updateCondition(isConditionMet);
-    }
-
-    public void fakeClearCondition() {
-        clearCondition();
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.kt
new file mode 100644
index 0000000..340249c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/FakeCondition.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.condition
+
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * Fake implementation of [Condition], and provides a way for tests to update condition fulfillment.
+ */
+class FakeCondition : Condition {
+    constructor(scope: CoroutineScope) : super(scope)
+
+    constructor(
+        scope: CoroutineScope,
+        initialValue: Boolean?,
+        overriding: Boolean,
+    ) : super(scope, initialValue, overriding)
+
+    public override suspend fun start() {}
+
+    public override fun stop() {}
+
+    override val startStrategy: Int
+        get() = START_EAGERLY
+
+    fun fakeUpdateCondition(isConditionMet: Boolean) {
+        updateCondition(isConditionMet)
+    }
+
+    fun fakeClearCondition() {
+        clearCondition()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index 064fd48..b80ff34 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -33,6 +33,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.log.LogAssertKt;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginLifecycleManager;
 import com.android.systemui.plugins.PluginListener;
@@ -138,11 +139,12 @@
 
         mVersionCheckResult = false;
         assertFalse(mPluginInstance.hasError());
-
         mPluginInstanceFactory.create(
                 mContext, mAppInfo, wrongVersionTestPluginComponentName,
                 TestPlugin.class, mPluginListener);
-        mPluginInstance.onCreate();
+        LogAssertKt.assertRunnableLogsWtf(()-> {
+            mPluginInstance.onCreate();
+        });
         assertTrue(mPluginInstance.hasError());
         assertNull(mPluginInstance.getPlugin());
     }
@@ -193,8 +195,10 @@
         mPluginInstance.onCreate();
         assertFalse(mPluginInstance.hasError());
 
-        Object result = mPluginInstance.getPlugin().methodThrowsError();
-        assertNotNull(result);  // Wrapper function should return non-null;
+        LogAssertKt.assertRunnableLogsWtf(()-> {
+            Object result = mPluginInstance.getPlugin().methodThrowsError();
+            assertNotNull(result);  // Wrapper function should return non-null;
+        });
         assertTrue(mPluginInstance.hasError());
         assertNull(mPluginInstance.getPlugin());
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt
new file mode 100644
index 0000000..e04162b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.platform.test.flag.junit.FlagsParameterization
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
+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 NotificationGroupingUtilTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+    private lateinit var underTest: NotificationGroupingUtil
+
+    private lateinit var testHelper: NotificationTestHelper
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(NotificationBundleUi.FLAG_NAME)
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    @Before
+    fun setup() {
+        testHelper = NotificationTestHelper(mContext, mDependency)
+    }
+
+    @Test
+    fun showsTime() {
+        val row = testHelper.createRow()
+
+        underTest = NotificationGroupingUtil(row)
+        assertThat(underTest.showsTime(row)).isTrue()
+    }
+}
\ No newline at end of file
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 f54c28f..998a7ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -33,7 +33,7 @@
 
 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 com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_OTP;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -82,7 +82,6 @@
 import com.android.systemui.flags.EnableSceneContainer;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.log.LogWtfHandlerRule;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.UserTracker;
@@ -108,7 +107,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.Mock;
@@ -207,8 +205,6 @@
     private final FakeExecutor mBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
     private final Executor mMainExecutor = Runnable::run; // Direct executor
 
-    @Rule public final LogWtfHandlerRule wtfHandlerRule = new LogWtfHandlerRule();
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -697,7 +693,7 @@
         mLockscreenUserManager.mConnectedToWifi.set(false);
 
         // Sensitive Content notifications are always redacted
-        assertEquals(REDACTION_TYPE_SENSITIVE_CONTENT,
+        assertEquals(REDACTION_TYPE_OTP,
                 mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index c9d910c..3c19179 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -20,16 +20,29 @@
 
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityOptions;
 import android.app.Notification;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.testing.TestableLooper;
+import android.util.Pair;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -42,19 +55,37 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
 import com.android.systemui.util.kotlin.JavaAdapter;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper
 public class NotificationRemoteInputManagerTest extends SysuiTestCase {
+
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.allCombinationsOf(NotificationBundleUi.FLAG_NAME);
+    }
+
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = 0;
 
@@ -69,14 +100,34 @@
     @Mock private NotificationClickNotifier mClickNotifier;
     @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
     @Mock private PowerInteractor mPowerInteractor;
+    @Mock
+    NotificationRemoteInputManager.RemoteInputListener mRemoteInputListener;
+    private ActionClickLogger mActionClickLogger;
+    @Captor
+    ArgumentCaptor<NotificationRemoteInputManager.ClickHandler> mClickHandlerArgumentCaptor;
+    private Context mSpyContext;
 
+    private NotificationTestHelper mTestHelper;
     private TestableNotificationRemoteInputManager mRemoteInputManager;
     private NotificationEntry mEntry;
 
+   public NotificationRemoteInputManagerTest(FlagsParameterization flags) {
+       super();
+       mSetFlagsRule.setFlagsParameterization(flags);
+   }
+
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        mSpyContext = spy(mContext);
+        doNothing().when(mSpyContext).startIntentSender(
+                any(), any(), anyInt(), anyInt(), anyInt(), any());
+
+
+        mTestHelper = new NotificationTestHelper(mSpyContext, mDependency);
+        mActionClickLogger = spy(new ActionClickLogger(logcatLogBuffer()));
+
         mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext,
                 mock(NotifPipelineFlags.class),
                 mLockscreenUserManager,
@@ -87,9 +138,10 @@
                 mRemoteInputUriController,
                 new RemoteInputControllerLogger(logcatLogBuffer()),
                 mClickNotifier,
-                new ActionClickLogger(logcatLogBuffer()),
+                mActionClickLogger,
                 mock(JavaAdapter.class),
                 mock(ShadeInteractor.class));
+        mRemoteInputManager.setRemoteInputListener(mRemoteInputListener);
         mEntry = new NotificationEntryBuilder()
                 .setPkg(TEST_PACKAGE_NAME)
                 .setOpPkg(TEST_PACKAGE_NAME)
@@ -133,6 +185,70 @@
         assertTrue(mRemoteInputManager.shouldKeepForSmartReplyHistory(mEntry));
     }
 
+    @Test
+    public void testActionClick() throws Exception {
+        RemoteViews.RemoteResponse response = mock(RemoteViews.RemoteResponse.class);
+        when(response.getLaunchOptions(any())).thenReturn(
+                Pair.create(mock(Intent.class), mock(ActivityOptions.class)));
+        ExpandableNotificationRow row = getRowWithReplyAction();
+        View actionView = ((LinearLayout) row.getPrivateLayout().getExpandedChild().findViewById(
+                com.android.internal.R.id.actions)).getChildAt(0);
+        Notification n = getNotification(row);
+        CountDownLatch latch = new CountDownLatch(1);
+        Consumer<NotificationEntry> consumer = notificationEntry -> latch.countDown();
+        if (!NotificationBundleUi.isEnabled()) {
+            mRemoteInputManager.addActionPressListener(consumer);
+        }
+
+        mRemoteInputManager.getRemoteViewsOnClickHandler().onInteraction(
+                actionView,
+                n.actions[0].actionIntent,
+                response);
+
+        verify(mActionClickLogger).logInitialClick(row.getKey(), 0, n.actions[0].actionIntent);
+        verify(mClickNotifier).onNotificationActionClick(
+                eq(row.getKey()), eq(0), eq(n.actions[0]), any(), eq(false));
+        verify(mCallback).handleRemoteViewClick(eq(actionView), eq(n.actions[0].actionIntent),
+                eq(false), eq(0), mClickHandlerArgumentCaptor.capture());
+
+        mClickHandlerArgumentCaptor.getValue().handleClick();
+        verify(mActionClickLogger).logStartingIntentWithDefaultHandler(
+                row.getKey(), n.actions[0].actionIntent, 0);
+
+        verify(mRemoteInputListener).releaseNotificationIfKeptForRemoteInputHistory(row.getKey());
+        if (NotificationBundleUi.isEnabled()) {
+            verify(row.getEntryAdapter()).onNotificationActionClicked();
+        } else {
+            latch.await(10, TimeUnit.MILLISECONDS);
+        }
+    }
+
+    private Notification getNotification(ExpandableNotificationRow row) {
+        if (NotificationBundleUi.isEnabled()) {
+            return row.getEntryAdapter().getSbn().getNotification();
+        } else {
+            return row.getEntryLegacy().getSbn().getNotification();
+        }
+    }
+
+    private ExpandableNotificationRow getRowWithReplyAction() throws Exception {
+        PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"),
+                PendingIntent.FLAG_IMMUTABLE);
+        Notification n = new Notification.Builder(mSpyContext, "")
+                .setSmallIcon(com.android.systemui.res.R.drawable.ic_person)
+                .addAction(new Notification.Action(com.android.systemui.res.R.drawable.ic_person,
+                        "reply", pi))
+                .build();
+        ExpandableNotificationRow row = mTestHelper.createRow(n);
+        row.onNotificationUpdated();
+        row.getPrivateLayout().setExpandedChild(Notification.Builder.recoverBuilder(mSpyContext, n)
+                .createBigContentView().apply(
+                        mSpyContext,
+                        row.getPrivateLayout(),
+                        mRemoteInputManager.getRemoteViewsOnClickHandler()));
+        return row;
+    }
+
     private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager {
 
         TestableNotificationRemoteInputManager(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 8165d45..3ecf302 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -32,23 +32,19 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeExpansionChangeEvent
-import com.android.systemui.shared.Flags as SharedFlags
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.FakeConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.WallpaperController
 import com.android.systemui.util.mockito.eq
+import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
 import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
 import com.android.wm.shell.appzoomout.AppZoomOut
 import com.google.common.truth.Truth.assertThat
-import java.util.Optional
-import java.util.function.Consumer
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -69,6 +65,8 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
+import java.util.Optional
+import java.util.function.Consumer
 
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
@@ -76,7 +74,7 @@
 class NotificationShadeDepthControllerTest : SysuiTestCase() {
     private val kosmos = testKosmos()
 
-    @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+    private val applicationScope = kosmos.testScope.backgroundScope
     @Mock private lateinit var statusBarStateController: StatusBarStateController
     @Mock private lateinit var blurUtils: BlurUtils
     @Mock private lateinit var biometricUnlockController: BiometricUnlockController
@@ -84,7 +82,10 @@
     @Mock private lateinit var keyguardInteractor: KeyguardInteractor
     @Mock private lateinit var choreographer: Choreographer
     @Mock private lateinit var wallpaperController: WallpaperController
+    @Mock private lateinit var wallpaperInteractor: WallpaperInteractor
     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+    @Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+    @Mock private lateinit var shadeModeInteractor: ShadeModeInteractor
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var appZoomOutOptional: Optional<AppZoomOut>
     @Mock private lateinit var root: View
@@ -101,7 +102,6 @@
     private var statusBarState = StatusBarState.SHADE
     private val maxBlur = 150
     private lateinit var notificationShadeDepthController: NotificationShadeDepthController
-    private val configurationController = FakeConfigurationController()
 
     @Before
     fun setup() {
@@ -128,14 +128,14 @@
                 keyguardInteractor,
                 choreographer,
                 wallpaperController,
+                wallpaperInteractor,
                 notificationShadeWindowController,
                 dozeParameters,
-                context,
-                ResourcesSplitShadeStateController(),
+                shadeModeInteractor,
                 windowRootViewBlurInteractor,
                 appZoomOutOptional,
-                dumpManager,
-                configurationController,
+                applicationScope,
+                dumpManager
             )
         notificationShadeDepthController.shadeAnimation = shadeAnimation
         notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
@@ -310,22 +310,24 @@
     }
 
     @Test
-    @DisableFlags(SharedFlags.FLAG_AMBIENT_AOD)
-    fun onDozeAmountChanged_appliesBlur() {
-        statusBarStateListener.onDozeAmountChanged(1f, 1f)
-        notificationShadeDepthController.updateBlurCallback.doFrame(0)
-        verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
-    }
-
-    @Test
-    @EnableFlags(SharedFlags.FLAG_AMBIENT_AOD)
     fun onDozeAmountChanged_doesNotApplyBlurWithAmbientAod() {
+        notificationShadeDepthController.wallpaperSupportsAmbientMode = false
+
         statusBarStateListener.onDozeAmountChanged(1f, 1f)
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(blurUtils).applyBlur(any(), eq(0), eq(false))
     }
 
     @Test
+    fun onDozeAmountChanged_appliesBlurWithAmbientAod() {
+        notificationShadeDepthController.wallpaperSupportsAmbientMode = true
+
+        statusBarStateListener.onDozeAmountChanged(1f, 1f)
+        notificationShadeDepthController.updateBlurCallback.doFrame(0)
+        verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
+    }
+
+    @Test
     fun setFullShadeTransition_appliesBlur_onlyIfSupported() {
         reset(blurUtils)
         `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
@@ -486,15 +488,10 @@
     }
 
     private fun enableSplitShade() {
-        setSplitShadeEnabled(true)
+        `when` (shadeModeInteractor.isSplitShade).thenReturn(true)
     }
 
     private fun disableSplitShade() {
-        setSplitShadeEnabled(false)
-    }
-
-    private fun setSplitShadeEnabled(enabled: Boolean) {
-        overrideResource(R.bool.config_use_split_notification_shade, enabled)
-        configurationController.notifyConfigurationChanged()
+        `when` (shadeModeInteractor.isSplitShade).thenReturn(false)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
index 03dee3a..72d21f1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
@@ -24,13 +24,13 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.DarkIconDispatcher
 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.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.kotlin.JavaAdapter
@@ -54,7 +54,7 @@
     private lateinit var underTest: OperatorNameViewController
     private lateinit var airplaneModeInteractor: AirplaneModeInteractor
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = TestScope()
 
     private val view = OperatorNameView(mContext)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index fda4ab0..8120c2d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -17,45 +17,59 @@
 package com.android.systemui.statusbar.chips.call.ui.viewmodel
 
 import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Intent
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import android.view.View
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.plugins.activityStarter
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.core.StatusBarRootModernization
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
 import com.android.systemui.testKosmos
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
-import kotlinx.coroutines.test.runTest
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class CallChipViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
     private val kosmos = testKosmos().useUnconfinedTestDispatcher()
 
     private val chipBackgroundView = mock<ChipBackgroundContainer>()
@@ -71,7 +85,7 @@
     private val mockExpandable: Expandable =
         mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }
 
-    private val underTest by lazy { kosmos.callChipViewModel }
+    private val Kosmos.underTest by Kosmos.Fixture { callChipViewModel }
 
     @Test
     fun chip_noCall_isHidden() =
@@ -88,9 +102,10 @@
         kosmos.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            addOngoingCallState(startTimeMs = 0)
+            addOngoingCallState(startTimeMs = 0, isAppVisible = false)
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
         }
 
     @Test
@@ -98,9 +113,10 @@
         kosmos.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            addOngoingCallState(startTimeMs = -2)
+            addOngoingCallState(startTimeMs = -2, isAppVisible = false)
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
         }
 
     @Test
@@ -108,9 +124,82 @@
         kosmos.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            addOngoingCallState(startTimeMs = 345)
+            addOngoingCallState(startTimeMs = 345, isAppVisible = false)
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+        }
+
+    @Test
+    @DisableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    fun chipLegacy_inCallWithVisibleApp_zeroStartTime_isHiddenAsInactive() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            addOngoingCallState(startTimeMs = 0, isAppVisible = true)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+        }
+
+    @Test
+    @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    @EnableChipsModernization
+    fun chipWithReturnAnimation_inCallWithVisibleApp_zeroStartTime_isHiddenAsIconOnly() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            addOngoingCallState(startTimeMs = 0, isAppVisible = true)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue()
+        }
+
+    @Test
+    @DisableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    fun chipLegacy_inCallWithVisibleApp_negativeStartTime_isHiddenAsInactive() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            addOngoingCallState(startTimeMs = -2, isAppVisible = true)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+        }
+
+    @Test
+    @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    @EnableChipsModernization
+    fun chipWithReturnAnimation_inCallWithVisibleApp_negativeStartTime_isHiddenAsIconOnly() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            addOngoingCallState(startTimeMs = -2, isAppVisible = true)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue()
+        }
+
+    @Test
+    @DisableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    fun chipLegacy_inCallWithVisibleApp_positiveStartTime_isHiddenAsInactive() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            addOngoingCallState(startTimeMs = 345, isAppVisible = true)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+        }
+
+    @Test
+    @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    @EnableChipsModernization
+    fun chipWithReturnAnimation_inCallWithVisibleApp_positiveStartTime_isHiddenAsTimer() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            addOngoingCallState(startTimeMs = 345, isAppVisible = true)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue()
         }
 
     @Test
@@ -398,6 +487,303 @@
             verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(pendingIntent, null)
         }
 
+    @Test
+    @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    @EnableChipsModernization
+    fun chipWithReturnAnimation_updatesCorrectly_withStateAndTransitionState() =
+        kosmos.runTest {
+            val pendingIntent = mock<PendingIntent>()
+            val intent = mock<Intent>()
+            whenever(pendingIntent.intent).thenReturn(intent)
+            val component = mock<ComponentName>()
+            whenever(intent.component).thenReturn(component)
+
+            val expandable = mock<Expandable>()
+            val activityController = mock<ActivityTransitionAnimator.Controller>()
+            whenever(
+                    expandable.activityTransitionController(
+                        anyOrNull(),
+                        anyOrNull(),
+                        any(),
+                        anyOrNull(),
+                        any(),
+                    )
+                )
+                .thenReturn(activityController)
+
+            val latest by collectLastValue(underTest.chip)
+
+            // Start off with no call.
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+            assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+
+            // Call starts [NoCall -> InCall(isAppVisible=true), NoTransition].
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue()
+            assertThat(latest!!.transitionManager!!.hideChipForTransition).isFalse()
+            val factory = latest!!.transitionManager!!.controllerFactory
+            assertThat(factory!!.component).isEqualTo(component)
+
+            // Request a return transition [InCall(isAppVisible=true), NoTransition ->
+            // ReturnRequested].
+            factory.onCompose(expandable)
+            var controller = factory.createController(forLaunch = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+            assertThat(latest!!.transitionManager!!.hideChipForTransition).isTrue()
+
+            // Start the return transition [InCall(isAppVisible=true), ReturnRequested ->
+            // Returning].
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+            assertThat(latest!!.transitionManager!!.hideChipForTransition).isFalse()
+
+            // End the return transition [InCall(isAppVisible=true), Returning -> NoTransition].
+            controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+            assertThat(latest!!.transitionManager!!.hideChipForTransition).isFalse()
+
+            // Settle the return transition [InCall(isAppVisible=true) ->
+            // InCall(isAppVisible=false), NoTransition].
+            kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+            assertThat(latest!!.transitionManager!!.hideChipForTransition).isFalse()
+
+            // Trigger a launch transition [InCall(isAppVisible=false) -> InCall(isAppVisible=true),
+            // NoTransition].
+            kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, true)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+            assertThat(latest!!.transitionManager!!.hideChipForTransition).isFalse()
+
+            // Request the return transition [InCall(isAppVisible=true), NoTransition ->
+            // LaunchRequested].
+            controller = factory.createController(forLaunch = true)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+            assertThat(latest!!.transitionManager!!.hideChipForTransition).isFalse()
+
+            // Start the return transition [InCall(isAppVisible=true), LaunchRequested ->
+            // Launching].
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+            assertThat(latest!!.transitionManager!!.hideChipForTransition).isFalse()
+
+            // End the return transition [InCall(isAppVisible=true), Launching -> NoTransition].
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+            assertThat(latest!!.transitionManager!!.controllerFactory).isEqualTo(factory)
+            assertThat(latest!!.transitionManager!!.hideChipForTransition).isFalse()
+
+            // End the call with the app visible [InCall(isAppVisible=true) -> NoCall,
+            // NoTransition].
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+            assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+
+            // End the call with the app hidden [InCall(isAppVisible=false) -> NoCall,
+            // NoTransition].
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                isAppVisible = false,
+            )
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+            assertThat(latest!!.transitionManager!!.controllerFactory).isNull()
+        }
+
+    @Test
+    @DisableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    fun chipLegacy_hasNoTransitionAnimationInformation() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            // NoCall
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest!!.transitionManager).isNull()
+
+            // InCall with visible app
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            assertThat(latest!!.transitionManager).isNull()
+
+            // InCall with hidden app
+            kosmos.activityManagerRepository.fake.setIsAppVisible(NOTIFICATION_UID, false)
+            assertThat(latest!!.transitionManager).isNull()
+        }
+
+    @Test
+    @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    @EnableChipsModernization
+    fun chipWithReturnAnimation_chipDataChangesMidTransition() =
+        kosmos.runTest {
+            val pendingIntent = mock<PendingIntent>()
+            val intent = mock<Intent>()
+            whenever(pendingIntent.intent).thenReturn(intent)
+            val component = mock<ComponentName>()
+            whenever(intent.component).thenReturn(component)
+
+            val expandable = mock<Expandable>()
+            val activityController = mock<ActivityTransitionAnimator.Controller>()
+            whenever(
+                    expandable.activityTransitionController(
+                        anyOrNull(),
+                        anyOrNull(),
+                        any(),
+                        anyOrNull(),
+                        any(),
+                    )
+                )
+                .thenReturn(activityController)
+
+            val latest by collectLastValue(underTest.chip)
+
+            // Start with the app visible and trigger a return animation.
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            var factory = latest!!.transitionManager!!.controllerFactory!!
+            factory.onCompose(expandable)
+            var controller = factory.createController(forLaunch = false)
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+            // The chip changes state.
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 0,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+            // Reset the state and trigger a launch animation.
+            controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            factory = latest!!.transitionManager!!.controllerFactory!!
+            factory.onCompose(expandable)
+            controller = factory.createController(forLaunch = true)
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+            // The chip changes state.
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = -2,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+        }
+
+    @Test
+    @EnableFlags(StatusBarChipsReturnAnimations.FLAG_NAME)
+    @EnableChipsModernization
+    fun chipWithReturnAnimation_chipDisappearsMidTransition() =
+        kosmos.runTest {
+            val pendingIntent = mock<PendingIntent>()
+            val intent = mock<Intent>()
+            whenever(pendingIntent.intent).thenReturn(intent)
+            val component = mock<ComponentName>()
+            whenever(intent.component).thenReturn(component)
+
+            val expandable = mock<Expandable>()
+            val activityController = mock<ActivityTransitionAnimator.Controller>()
+            whenever(
+                    expandable.activityTransitionController(
+                        anyOrNull(),
+                        anyOrNull(),
+                        any(),
+                        anyOrNull(),
+                        any(),
+                    )
+                )
+                .thenReturn(activityController)
+
+            val latest by collectLastValue(underTest.chip)
+
+            // Start with the app visible and trigger a return animation.
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            var factory = latest!!.transitionManager!!.controllerFactory!!
+            factory.onCompose(expandable)
+            var controller = factory.createController(forLaunch = false)
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+            // The chip disappears.
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+
+            // Reset the state and trigger a launch animation.
+            controller.onTransitionAnimationEnd(isExpandingFullyAbove = false)
+            addOngoingCallState(
+                key = NOTIFICATION_KEY,
+                startTimeMs = 345,
+                contentIntent = pendingIntent,
+                uid = NOTIFICATION_UID,
+                isAppVisible = true,
+            )
+            factory = latest!!.transitionManager!!.controllerFactory!!
+            factory.onCompose(expandable)
+            controller = factory.createController(forLaunch = true)
+            controller.onTransitionAnimationStart(isExpandingFullyAbove = false)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+            assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+
+            // The chip disappears.
+            removeOngoingCallState(key = NOTIFICATION_KEY)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
+        }
+
     companion object {
         fun createStatusBarIconViewOrNull(): StatusBarIconView? =
             if (StatusBarConnectedDisplays.isEnabled) {
@@ -417,7 +803,22 @@
                 }
                 .build()
 
+        private const val NOTIFICATION_KEY = "testKey"
+        private const val NOTIFICATION_UID = 12345
         private const val PROMOTED_BACKGROUND_COLOR = 65
         private const val PROMOTED_PRIMARY_TEXT_COLOR = 98
+
+        @get:Parameters(name = "{0}")
+        @JvmStatic
+        val flags: List<FlagsParameterization>
+            get() = buildList {
+                addAll(
+                    FlagsParameterization.allCombinationsOf(
+                        StatusBarRootModernization.FLAG_NAME,
+                        StatusBarChipsModernization.FLAG_NAME,
+                        StatusBarChipsReturnAnimations.FLAG_NAME,
+                    )
+                )
+            }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
index b2174c1..21a4560 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/domian/interactor/MediaRouterChipInteractorTest.kt
@@ -20,12 +20,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediarouter.data.repository.fakeMediaRouterRepository
 import com.android.systemui.statusbar.chips.casttootherdevice.domain.interactor.mediaRouterChipInteractor
 import com.android.systemui.statusbar.chips.casttootherdevice.domain.model.MediaRouterCastModel
 import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -35,7 +35,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MediaRouterChipInteractorTest : SysuiTestCase() {
-    val kosmos = Kosmos()
+    val kosmos = testKosmos()
     val testScope = kosmos.testScope
     val mediaRouterRepository = kosmos.fakeMediaRouterRepository
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
index 274efbb..5681297 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastScreenToOtherDeviceDialogDelegateTest.kt
@@ -30,7 +30,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -41,6 +40,7 @@
 import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
 import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -57,7 +57,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EndCastScreenToOtherDeviceDialogDelegateTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val sysuiDialog = mock<SystemUIDialog>()
     private lateinit var underTest: EndCastScreenToOtherDeviceDialogDelegate
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
index 88207d1..30a415c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndGenericCastToOtherDeviceDialogDelegateTest.kt
@@ -26,7 +26,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediarouter.data.repository.fakeMediaRouterRepository
@@ -35,6 +34,7 @@
 import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -51,7 +51,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EndGenericCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val sysuiDialog = mock<SystemUIDialog>()
     private lateinit var underTest: EndGenericCastToOtherDeviceDialogDelegate
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index ccc844a..d921ab3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -52,6 +51,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
 import com.android.systemui.statusbar.policy.CastDevice
+import com.android.systemui.testKosmos
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -69,7 +69,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
     private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
     private val mediaRouterRepo = kosmos.fakeMediaRouterRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
index 795988f..fac50b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
@@ -24,12 +24,12 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import org.junit.runner.RunWith
@@ -42,7 +42,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EndMediaProjectionDialogHelperTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
 
     private val underTest = kosmos.endMediaProjectionDialogHelper
 
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 5d19506..7f8f5f4 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
@@ -27,6 +27,7 @@
 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.shared.StatusBarNotifChips
 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
@@ -39,6 +40,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarNotifChips.FLAG_NAME)
 class SingleNotificationChipInteractorTest : SysuiTestCase() {
     private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     val factory = kosmos.singleNotificationChipInteractorFactory
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 4993b56..b5cfc7e 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
@@ -573,6 +573,8 @@
             assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
             assertThat((latest!![0] as OngoingActivityChipModel.Active.Timer).startTimeMs)
                 .isEqualTo(whenElapsed)
+            assertThat((latest!![0] as OngoingActivityChipModel.Active.Timer).isEventInFuture)
+                .isFalse()
         }
 
     @Test
@@ -608,6 +610,8 @@
             assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
             assertThat((latest!![0] as OngoingActivityChipModel.Active.Timer).startTimeMs)
                 .isEqualTo(whenElapsed)
+            assertThat((latest!![0] as OngoingActivityChipModel.Active.Timer).isEventInFuture)
+                .isTrue()
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
index f560ee7..981c952 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
@@ -30,7 +30,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -39,6 +38,7 @@
 import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
 import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.screenRecordChipInteractor
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -55,7 +55,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EndScreenRecordingDialogDelegateTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
 
     private val sysuiDialog = mock<SystemUIDialog>()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
index 95aa6cd..b2e90ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareScreenToAppDialogDelegateTest.kt
@@ -30,7 +30,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -41,6 +40,7 @@
 import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
 import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runCurrent
@@ -57,7 +57,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EndShareScreenToAppDialogDelegateTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
     private val sysuiDialog = mock<SystemUIDialog>()
     private lateinit var underTest: EndShareScreenToAppDialogDelegate
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
index e3a84fd..6d91fb5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
@@ -22,12 +22,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -39,7 +39,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ChipTransitionHelperTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt
index 4e92540..cd9970c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerStateTest.kt
@@ -35,55 +35,153 @@
 @RunWith(AndroidJUnit4::class)
 class ChronometerStateTest : SysuiTestCase() {
 
-    private lateinit var mockTimeSource: MutableTimeSource
+    private lateinit var fakeTimeSource: MutableTimeSource
 
     @Before
     fun setup() {
-        mockTimeSource = MutableTimeSource()
+        fakeTimeSource = MutableTimeSource()
     }
 
     @Test
-    fun initialText_isCorrect() = runTest {
-        val state = ChronometerState(mockTimeSource, 0L)
-        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(0))
+    fun initialText_isEventInFutureFalse_timeIsNow() = runTest {
+        fakeTimeSource.time = 3_000
+        val state =
+            ChronometerState(fakeTimeSource, eventTimeMillis = 3_000, isEventInFuture = false)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 0))
     }
 
     @Test
-    fun textUpdates_withTime() = runTest {
-        val startTime = 1000L
-        val state = ChronometerState(mockTimeSource, startTime)
+    fun initialText_isEventInFutureFalse_timeInPast() = runTest {
+        fakeTimeSource.time = 3_000
+        val state =
+            ChronometerState(fakeTimeSource, eventTimeMillis = 1_000, isEventInFuture = false)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 2))
+    }
+
+    @Test
+    fun initialText_isEventInFutureFalse_timeInFuture() = runTest {
+        fakeTimeSource.time = 3_000
+        val state =
+            ChronometerState(fakeTimeSource, eventTimeMillis = 5_000, isEventInFuture = false)
+        // When isEventInFuture=false, eventTimeMillis needs to be in the past if we want text to
+        // show
+        assertThat(state.currentTimeText).isNull()
+    }
+
+    @Test
+    fun initialText_isEventInFutureTrue_timeIsNow() = runTest {
+        fakeTimeSource.time = 3_000
+        val state =
+            ChronometerState(fakeTimeSource, eventTimeMillis = 3_000, isEventInFuture = true)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 0))
+    }
+
+    @Test
+    fun initialText_isEventInFutureTrue_timeInFuture() = runTest {
+        fakeTimeSource.time = 3_000
+        val state =
+            ChronometerState(fakeTimeSource, eventTimeMillis = 5_000, isEventInFuture = true)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 2))
+    }
+
+    @Test
+    fun initialText_isEventInFutureTrue_timeInPast() = runTest {
+        fakeTimeSource.time = 3_000
+        val state =
+            ChronometerState(fakeTimeSource, eventTimeMillis = 1_000, isEventInFuture = true)
+        // When isEventInFuture=true, eventTimeMillis needs to be in the future if we want text to
+        // show
+        assertThat(state.currentTimeText).isNull()
+    }
+
+    @Test
+    fun textUpdates_isEventInFutureFalse_timeInPast() = runTest {
+        val eventTime = 1000L
+        val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = false)
         val job = launch { state.run() }
 
         val elapsedTime = 5000L
-        mockTimeSource.time = startTime + elapsedTime
+        fakeTimeSource.time = eventTime + elapsedTime
         advanceTimeBy(elapsedTime)
         assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(elapsedTime / 1000))
 
+        val additionalTime = 6000L
+        fakeTimeSource.time += additionalTime
+        advanceTimeBy(additionalTime)
+        assertThat(state.currentTimeText)
+            .isEqualTo(formatElapsedTime((elapsedTime + additionalTime) / 1000))
+
         job.cancelAndJoin()
     }
 
     @Test
-    fun textUpdates_toLargerValue() = runTest {
-        val startTime = 1000L
-        val state = ChronometerState(mockTimeSource, startTime)
+    fun textUpdates_isEventInFutureFalse_timeChangesFromFutureToPast() = runTest {
+        val eventTime = 15_000L
+        val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = false)
         val job = launch { state.run() }
 
-        val elapsedTime = 15000L
-        mockTimeSource.time = startTime + elapsedTime
-        advanceTimeBy(elapsedTime)
-        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(elapsedTime / 1000))
+        // WHEN the time is 5 but the eventTime is 15
+        fakeTimeSource.time = 5_000L
+        advanceTimeBy(5_000L)
+        // THEN no text is shown
+        assertThat(state.currentTimeText).isNull()
+
+        // WHEN the time advances to 40
+        fakeTimeSource.time = 40_000L
+        advanceTimeBy(35_000)
+        // THEN text is shown as 25 seconds (40 - 15)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 25))
 
         job.cancelAndJoin()
     }
 
     @Test
-    fun textUpdates_afterResettingBase() = runTest {
+    fun textUpdates_isEventInFutureTrue_timeInFuture() = runTest {
+        val eventTime = 15_000L
+        val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = true)
+        val job = launch { state.run() }
+
+        fakeTimeSource.time = 5_000L
+        advanceTimeBy(5_000L)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 10))
+
+        val additionalTime = 6000L
+        fakeTimeSource.time += additionalTime
+        advanceTimeBy(additionalTime)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 4))
+
+        job.cancelAndJoin()
+    }
+
+    @Test
+    fun textUpdates_isEventInFutureTrue_timeChangesFromFutureToPast() = runTest {
+        val eventTime = 15_000L
+        val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = true)
+        val job = launch { state.run() }
+
+        // WHEN the time is 5 and the eventTime is 15
+        fakeTimeSource.time = 5_000L
+        advanceTimeBy(5_000L)
+        // THEN 10 seconds is shown
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 10))
+
+        // WHEN the time advances to 40 (past the event time)
+        fakeTimeSource.time = 40_000L
+        advanceTimeBy(35_000)
+        // THEN no text is shown
+        assertThat(state.currentTimeText).isNull()
+
+        job.cancelAndJoin()
+    }
+
+    @Test
+    fun textUpdates_afterResettingBase_isEventInFutureFalse() = runTest {
         val initialElapsedTime = 30000L
         val startTime = 50000L
-        val state = ChronometerState(mockTimeSource, startTime)
+        val state = ChronometerState(fakeTimeSource, startTime, isEventInFuture = false)
         val job = launch { state.run() }
 
-        mockTimeSource.time = startTime + initialElapsedTime
+        fakeTimeSource.time = startTime + initialElapsedTime
         advanceTimeBy(initialElapsedTime)
         assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(initialElapsedTime / 1000))
 
@@ -91,15 +189,68 @@
 
         val newElapsedTime = 5000L
         val newStartTime = 100000L
-        val newState = ChronometerState(mockTimeSource, newStartTime)
+        val newState = ChronometerState(fakeTimeSource, newStartTime, isEventInFuture = false)
         val newJob = launch { newState.run() }
 
-        mockTimeSource.time = newStartTime + newElapsedTime
+        fakeTimeSource.time = newStartTime + newElapsedTime
         advanceTimeBy(newElapsedTime)
         assertThat(newState.currentTimeText).isEqualTo(formatElapsedTime(newElapsedTime / 1000))
 
         newJob.cancelAndJoin()
     }
+
+    @Test
+    fun textUpdates_afterResettingBase_isEventInFutureTrue() = runTest {
+        val initialElapsedTime = 40_000L
+        val eventTime = 50_000L
+        val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = true)
+        val job = launch { state.run() }
+
+        fakeTimeSource.time = initialElapsedTime
+        advanceTimeBy(initialElapsedTime)
+        // Time should be 50 - 40 = 10
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 10))
+
+        job.cancelAndJoin()
+
+        val newElapsedTime = 75_000L
+        val newEventTime = 100_000L
+        val newState = ChronometerState(fakeTimeSource, newEventTime, isEventInFuture = true)
+        val newJob = launch { newState.run() }
+
+        fakeTimeSource.time = newElapsedTime
+        advanceTimeBy(newElapsedTime - initialElapsedTime)
+        // Time should be 100 - 75 = 25
+        assertThat(newState.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 25))
+
+        newJob.cancelAndJoin()
+    }
+
+    @Test
+    fun textUpdates_afterResettingisEventInFuture() = runTest {
+        val initialElapsedTime = 40_000L
+        val eventTime = 50_000L
+        val state = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = true)
+        val job = launch { state.run() }
+
+        fakeTimeSource.time = initialElapsedTime
+        advanceTimeBy(initialElapsedTime)
+        // Time should be 50 - 40 = 10
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 10))
+
+        job.cancelAndJoin()
+
+        val newElapsedTime = 70_000L
+        val newState = ChronometerState(fakeTimeSource, eventTime, isEventInFuture = false)
+        val newJob = launch { newState.run() }
+
+        fakeTimeSource.time = newElapsedTime
+        advanceTimeBy(newElapsedTime - initialElapsedTime)
+        // Time should be 70 - 50 = 20
+        assertThat(newState.currentTimeText).isEqualTo(formatElapsedTime(/* elapsedSeconds= */ 20))
+
+        newJob.cancelAndJoin()
+    }
 }
 
 /** A fake implementation of [TimeSource] that allows the caller to set the current time */
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index e2d1498..e39fa70 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -137,7 +137,7 @@
         kosmos.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
 
-            addOngoingCallState()
+            addOngoingCallState(isAppVisible = false)
 
             val latest by collectLastValue(underTest.primaryChip)
 
@@ -163,7 +163,7 @@
             screenRecordState.value = ScreenRecordModel.DoingNothing
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
-            addOngoingCallState()
+            addOngoingCallState(isAppVisible = false)
 
             val latest by collectLastValue(underTest.primaryChip)
 
@@ -178,7 +178,7 @@
             // MediaProjection covers both share-to-app and cast-to-other-device
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            addOngoingCallState(key = notificationKey)
+            addOngoingCallState(key = notificationKey, isAppVisible = false)
 
             val latest by collectLastValue(underTest.primaryChip)
 
@@ -190,7 +190,7 @@
         kosmos.runTest {
             // Start with just the lowest priority chip shown
             val callNotificationKey = "call"
-            addOngoingCallState(key = callNotificationKey)
+            addOngoingCallState(key = callNotificationKey, isAppVisible = false)
             // And everything else hidden
             mediaProjectionState.value = MediaProjectionState.NotProjecting
             screenRecordState.value = ScreenRecordModel.DoingNothing
@@ -225,7 +225,7 @@
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
             val callNotificationKey = "call"
-            addOngoingCallState(key = callNotificationKey)
+            addOngoingCallState(key = callNotificationKey, isAppVisible = false)
 
             val latest by collectLastValue(underTest.primaryChip)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index 96c4a59..7135cf0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -62,6 +62,7 @@
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.data.repository.addNotif
 import com.android.systemui.statusbar.notification.data.repository.addNotifs
+import com.android.systemui.statusbar.notification.data.repository.removeNotif
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -235,7 +236,7 @@
     fun primaryChip_screenRecordShowAndCallShow_screenRecordShown() =
         kosmos.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
-            addOngoingCallState("call")
+            addOngoingCallState("call", isAppVisible = false)
 
             val latest by collectLastValue(underTest.primaryChip)
 
@@ -248,7 +249,7 @@
         kosmos.runTest {
             val callNotificationKey = "call"
             screenRecordState.value = ScreenRecordModel.Recording
-            addOngoingCallState(callNotificationKey)
+            addOngoingCallState(callNotificationKey, isAppVisible = false)
 
             val latest by collectLastValue(underTest.chipsLegacy)
             val unused by collectLastValue(underTest.chips)
@@ -295,7 +296,7 @@
     @Test
     fun chipsLegacy_oneChip_notSquished() =
         kosmos.runTest {
-            addOngoingCallState()
+            addOngoingCallState(isAppVisible = false)
 
             val latest by collectLastValue(underTest.chipsLegacy)
 
@@ -322,7 +323,7 @@
     fun chipsLegacy_twoTimerChips_isSmallPortrait_bothSquished() =
         kosmos.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
-            addOngoingCallState(key = "call")
+            addOngoingCallState(key = "call", isAppVisible = false)
 
             val latest by collectLastValue(underTest.chipsLegacy)
 
@@ -349,12 +350,42 @@
                 .isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
         }
 
+    @EnableChipsModernization
+    @Test
+    fun chips_threeChips_isSmallPortrait_allSquished() =
+        kosmos.runTest {
+            screenRecordState.value = ScreenRecordModel.Recording
+            addOngoingCallState(key = "call")
+
+            val promotedContentBuilder =
+                PromotedNotificationContentModel.Builder("notif").apply {
+                    this.shortCriticalText = "Some text here"
+                }
+            activeNotificationListRepository.addNotif(
+                activeNotificationModel(
+                    key = "notif",
+                    statusBarChipIcon = createStatusBarIconViewOrNull(),
+                    promotedContent = promotedContentBuilder.build(),
+                )
+            )
+
+            val latest by collectLastValue(underTest.chips)
+
+            // Squished chips are icon only
+            assertThat(latest!!.active[0])
+                .isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+            assertThat(latest!!.active[1])
+                .isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+            assertThat(latest!!.active[2])
+                .isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+        }
+
     @DisableChipsModernization
     @Test
     fun chipsLegacy_countdownChipAndTimerChip_countdownNotSquished_butTimerSquished() =
         kosmos.runTest {
             screenRecordState.value = ScreenRecordModel.Starting(millisUntilStarted = 2000)
-            addOngoingCallState(key = "call")
+            addOngoingCallState(key = "call", isAppVisible = false)
 
             val latest by collectLastValue(underTest.chipsLegacy)
 
@@ -400,7 +431,7 @@
                 .isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
 
             // WHEN there's 2 chips
-            addOngoingCallState(key = "call")
+            addOngoingCallState(key = "call", isAppVisible = false)
 
             // THEN they both become squished
             assertThat(latest!!.primary)
@@ -456,7 +487,7 @@
     fun chipsLegacy_twoChips_isLandscape_notSquished() =
         kosmos.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
-            addOngoingCallState(key = "call")
+            addOngoingCallState(key = "call", isAppVisible = false)
 
             // WHEN we're in landscape
             val config =
@@ -502,7 +533,7 @@
     fun chipsLegacy_twoChips_isLargeScreen_notSquished() =
         kosmos.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
-            addOngoingCallState(key = "call")
+            addOngoingCallState(key = "call", isAppVisible = false)
 
             // WHEN we're on a large screen
             kosmos.displayStateRepository.setIsLargeScreen(true)
@@ -596,7 +627,7 @@
             screenRecordState.value = ScreenRecordModel.DoingNothing
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
-            addOngoingCallState(key = "call")
+            addOngoingCallState(key = "call", isAppVisible = false)
 
             val latest by collectLastValue(underTest.primaryChip)
 
@@ -611,7 +642,7 @@
             screenRecordState.value = ScreenRecordModel.DoingNothing
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
-            addOngoingCallState(key = "call")
+            addOngoingCallState(key = "call", isAppVisible = false)
 
             val latest by collectLastValue(underTest.chipsLegacy)
             val unused by collectLastValue(underTest.chips)
@@ -650,7 +681,7 @@
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
             val callNotificationKey = "call"
-            addOngoingCallState(key = callNotificationKey)
+            addOngoingCallState(key = callNotificationKey, isAppVisible = false)
 
             val latest by collectLastValue(underTest.primaryChip)
 
@@ -666,7 +697,7 @@
             // MediaProjection covers both share-to-app and cast-to-other-device
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            addOngoingCallState(key = callNotificationKey)
+            addOngoingCallState(key = callNotificationKey, isAppVisible = false)
 
             val latest by collectLastValue(underTest.chipsLegacy)
             val unused by collectLastValue(underTest.chips)
@@ -851,7 +882,7 @@
 
     @EnableChipsModernization
     @Test
-    fun chips_threePromotedNotifs_topTwoActiveThirdInOverflow() =
+    fun chips_fourPromotedNotifs_topThreeActiveFourthInOverflow() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
             val unused by collectLastValue(underTest.chipsLegacy)
@@ -859,6 +890,7 @@
             val firstIcon = createStatusBarIconViewOrNull()
             val secondIcon = createStatusBarIconViewOrNull()
             val thirdIcon = createStatusBarIconViewOrNull()
+            val fourthIcon = createStatusBarIconViewOrNull()
             setNotifs(
                 listOf(
                     activeNotificationModel(
@@ -879,20 +911,28 @@
                         promotedContent =
                             PromotedNotificationContentModel.Builder("thirdNotif").build(),
                     ),
+                    activeNotificationModel(
+                        key = "fourthNotif",
+                        statusBarChipIcon = fourthIcon,
+                        promotedContent =
+                            PromotedNotificationContentModel.Builder("fourthNotif").build(),
+                    ),
                 )
             )
 
-            assertThat(latest!!.active.size).isEqualTo(2)
+            assertThat(latest!!.active.size).isEqualTo(3)
             assertIsNotifChip(latest!!.active[0], context, firstIcon, "firstNotif")
             assertIsNotifChip(latest!!.active[1], context, secondIcon, "secondNotif")
+            assertIsNotifChip(latest!!.active[2], context, thirdIcon, "thirdNotif")
             assertThat(latest!!.overflow.size).isEqualTo(1)
-            assertIsNotifChip(latest!!.overflow[0], context, thirdIcon, "thirdNotif")
+            assertIsNotifChip(latest!!.overflow[0], context, fourthIcon, "fourthNotif")
             assertThat(latest!!.inactive.size).isEqualTo(4)
             assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
         }
 
     @Test
-    fun visibleChipKeys_threePromotedNotifs_topTwoInList() =
+    @DisableChipsModernization
+    fun visibleChipKeys_chipsModOff_threePromotedNotifs_topTwoInList() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.visibleChipKeys)
 
@@ -922,6 +962,44 @@
             assertThat(latest).containsExactly("firstNotif", "secondNotif").inOrder()
         }
 
+    @Test
+    @EnableChipsModernization
+    fun visibleChipKeys_chipsModOn_fourPromotedNotifs_topThreeInList() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.visibleChipKeys)
+
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "firstNotif",
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
+                        promotedContent =
+                            PromotedNotificationContentModel.Builder("firstNotif").build(),
+                    ),
+                    activeNotificationModel(
+                        key = "secondNotif",
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
+                        promotedContent =
+                            PromotedNotificationContentModel.Builder("secondNotif").build(),
+                    ),
+                    activeNotificationModel(
+                        key = "thirdNotif",
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
+                        promotedContent =
+                            PromotedNotificationContentModel.Builder("thirdNotif").build(),
+                    ),
+                    activeNotificationModel(
+                        key = "fourthNotif",
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
+                        promotedContent =
+                            PromotedNotificationContentModel.Builder("fourthNotif").build(),
+                    ),
+                )
+            )
+
+            assertThat(latest).containsExactly("firstNotif", "secondNotif", "thirdNotif").inOrder()
+        }
+
     @DisableChipsModernization
     @Test
     fun chipsLegacy_callAndPromotedNotifs_primaryIsCallSecondaryIsNotif() =
@@ -930,7 +1008,7 @@
             val unused by collectLastValue(underTest.chips)
 
             val callNotificationKey = "call"
-            addOngoingCallState(callNotificationKey)
+            addOngoingCallState(callNotificationKey, isAppVisible = false)
 
             val firstIcon = createStatusBarIconViewOrNull()
             activeNotificationListRepository.addNotifs(
@@ -957,7 +1035,7 @@
 
     @EnableChipsModernization
     @Test
-    fun chips_callAndPromotedNotifs_callAndFirstNotifActiveSecondNotifInOverflow() =
+    fun chips_callAndPromotedNotifs_callAndFirstTwoNotifsActive_thirdNotifInOverflow() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
             val unused by collectLastValue(underTest.chipsLegacy)
@@ -965,6 +1043,7 @@
             val callNotificationKey = "call"
             val firstIcon = createStatusBarIconViewOrNull()
             val secondIcon = createStatusBarIconViewOrNull()
+            val thirdIcon = createStatusBarIconViewOrNull()
             addOngoingCallState(key = callNotificationKey)
             activeNotificationListRepository.addNotifs(
                 listOf(
@@ -980,27 +1059,34 @@
                         promotedContent =
                             PromotedNotificationContentModel.Builder("secondNotif").build(),
                     ),
+                    activeNotificationModel(
+                        key = "thirdNotif",
+                        statusBarChipIcon = thirdIcon,
+                        promotedContent =
+                            PromotedNotificationContentModel.Builder("thirdNotif").build(),
+                    ),
                 )
             )
 
-            assertThat(latest!!.active.size).isEqualTo(2)
+            assertThat(latest!!.active.size).isEqualTo(3)
             assertIsCallChip(latest!!.active[0], callNotificationKey, context)
             assertIsNotifChip(latest!!.active[1], context, firstIcon, "firstNotif")
+            assertIsNotifChip(latest!!.active[2], context, secondIcon, "secondNotif")
             assertThat(latest!!.overflow.size).isEqualTo(1)
-            assertIsNotifChip(latest!!.overflow[0], context, secondIcon, "secondNotif")
+            assertIsNotifChip(latest!!.overflow[0], context, thirdIcon, "thirdNotif")
             assertThat(latest!!.inactive.size).isEqualTo(3)
             assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
         }
 
     @DisableChipsModernization
     @Test
-    fun chipsLegacy_screenRecordAndCallAndPromotedNotifs_notifsNotShown() =
+    fun chipsLegacy_screenRecordAndCallAndPromotedNotif_notifNotShown() =
         kosmos.runTest {
             val callNotificationKey = "call"
             val latest by collectLastValue(underTest.chipsLegacy)
             val unused by collectLastValue(underTest.chips)
 
-            addOngoingCallState(callNotificationKey)
+            addOngoingCallState(callNotificationKey, isAppVisible = false)
             screenRecordState.value = ScreenRecordModel.Recording
             activeNotificationListRepository.addNotif(
                 activeNotificationModel(
@@ -1016,7 +1102,8 @@
         }
 
     @Test
-    fun visibleChipKeys_screenRecordAndCallAndPromotedNotifs_topTwoInList() =
+    @DisableChipsModernization
+    fun visibleChipKeys_chipsModOff_screenRecordAndCallAndPromotedNotifs_topTwoInList() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.visibleChipKeys)
 
@@ -1025,9 +1112,16 @@
             screenRecordState.value = ScreenRecordModel.Recording
             activeNotificationListRepository.addNotif(
                 activeNotificationModel(
-                    key = "notif",
+                    key = "notif1",
                     statusBarChipIcon = createStatusBarIconViewOrNull(),
-                    promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+                    promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+                )
+            )
+            activeNotificationListRepository.addNotif(
+                activeNotificationModel(
+                    key = "notif2",
+                    statusBarChipIcon = createStatusBarIconViewOrNull(),
+                    promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
                 )
             )
 
@@ -1036,9 +1130,38 @@
                 .inOrder()
         }
 
+    @Test
+    @EnableChipsModernization
+    fun visibleChipKeys_chipsModOn_screenRecordAndCallAndPromotedNotifs_topThreeInList() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.visibleChipKeys)
+
+            val callNotificationKey = "call"
+            addOngoingCallState(callNotificationKey)
+            screenRecordState.value = ScreenRecordModel.Recording
+            activeNotificationListRepository.addNotif(
+                activeNotificationModel(
+                    key = "notif1",
+                    statusBarChipIcon = createStatusBarIconViewOrNull(),
+                    promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+                )
+            )
+            activeNotificationListRepository.addNotif(
+                activeNotificationModel(
+                    key = "notif2",
+                    statusBarChipIcon = createStatusBarIconViewOrNull(),
+                    promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+                )
+            )
+
+            assertThat(latest)
+                .containsExactly(ScreenRecordChipViewModel.KEY, callNotificationKey, "notif1")
+                .inOrder()
+        }
+
     @EnableChipsModernization
     @Test
-    fun chips_screenRecordAndCallAndPromotedNotif_notifInOverflow() =
+    fun chips_screenRecordAndCallAndPromotedNotifs_secondNotifInOverflow() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
             val unused by collectLastValue(underTest.chipsLegacy)
@@ -1055,11 +1178,22 @@
             )
             addOngoingCallState(key = callNotificationKey)
 
-            assertThat(latest!!.active.size).isEqualTo(2)
+            // This is the overflow notif
+            val notifIcon2 = createStatusBarIconViewOrNull()
+            activeNotificationListRepository.addNotif(
+                activeNotificationModel(
+                    key = "notif2",
+                    statusBarChipIcon = notifIcon2,
+                    promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+                )
+            )
+
+            assertThat(latest!!.active.size).isEqualTo(3)
             assertIsScreenRecordChip(latest!!.active[0])
             assertIsCallChip(latest!!.active[1], callNotificationKey, context)
+            assertIsNotifChip(latest!!.active[2], context, notifIcon, "notif")
             assertThat(latest!!.overflow.size).isEqualTo(1)
-            assertIsNotifChip(latest!!.overflow[0], context, notifIcon, "notif")
+            assertIsNotifChip(latest!!.overflow[0], context, notifIcon2, "notif2")
             assertThat(latest!!.inactive.size).isEqualTo(2)
             assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
         }
@@ -1089,7 +1223,7 @@
             assertIsNotifChip(latest, context, notifIcon, "notif")
 
             // WHEN the higher priority call chip is added
-            addOngoingCallState(callNotificationKey)
+            addOngoingCallState(callNotificationKey, isAppVisible = false)
 
             // THEN the higher priority call chip is used
             assertIsCallChip(latest, callNotificationKey, context)
@@ -1120,7 +1254,7 @@
             screenRecordState.value = ScreenRecordModel.Recording
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
-            addOngoingCallState(callNotificationKey)
+            addOngoingCallState(callNotificationKey, isAppVisible = false)
             val notifIcon = createStatusBarIconViewOrNull()
             activeNotificationListRepository.addNotif(
                 activeNotificationModel(
@@ -1182,7 +1316,7 @@
             assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModel())
 
             // WHEN the higher priority call chip is added
-            addOngoingCallState(callNotificationKey)
+            addOngoingCallState(callNotificationKey, isAppVisible = false)
 
             // THEN the higher priority call chip is used as primary and notif is demoted to
             // secondary
@@ -1234,15 +1368,16 @@
     @Test
     fun chips_movesChipsAroundAccordingToPriority() =
         kosmos.runTest {
+            systemClock.setCurrentTimeMillis(10_000)
             val callNotificationKey = "call"
             // Start with just the lowest priority chip active
-            val notifIcon = createStatusBarIconViewOrNull()
+            val notif1Icon = createStatusBarIconViewOrNull()
             setNotifs(
                 listOf(
                     activeNotificationModel(
-                        key = "notif",
-                        statusBarChipIcon = notifIcon,
-                        promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+                        key = "notif1",
+                        statusBarChipIcon = notif1Icon,
+                        promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
                     )
                 )
             )
@@ -1254,7 +1389,7 @@
             val unused by collectLastValue(underTest.chipsLegacy)
 
             assertThat(latest!!.active.size).isEqualTo(1)
-            assertIsNotifChip(latest!!.active[0], context, notifIcon, "notif")
+            assertIsNotifChip(latest!!.active[0], context, notif1Icon, "notif1")
             assertThat(latest!!.overflow).isEmpty()
             assertThat(latest!!.inactive.size).isEqualTo(4)
             assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
@@ -1262,10 +1397,10 @@
             // WHEN the higher priority call chip is added
             addOngoingCallState(key = callNotificationKey)
 
-            // THEN the higher priority call chip and notif are active in that order
+            // THEN the higher priority call chip and notif1 are active in that order
             assertThat(latest!!.active.size).isEqualTo(2)
             assertIsCallChip(latest!!.active[0], callNotificationKey, context)
-            assertIsNotifChip(latest!!.active[1], context, notifIcon, "notif")
+            assertIsNotifChip(latest!!.active[1], context, notif1Icon, "notif1")
             assertThat(latest!!.overflow).isEmpty()
             assertThat(latest!!.inactive.size).isEqualTo(3)
             assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
@@ -1278,56 +1413,63 @@
                     createTask(taskId = 1),
                 )
 
-            // THEN the higher priority media projection chip and call are active in that order, and
-            // notif is demoted to overflow
-            assertThat(latest!!.active.size).isEqualTo(2)
+            // THEN media projection, then call, then notif1 are active
+            assertThat(latest!!.active.size).isEqualTo(3)
             assertIsShareToAppChip(latest!!.active[0])
             assertIsCallChip(latest!!.active[1], callNotificationKey, context)
-            assertThat(latest!!.overflow.size).isEqualTo(1)
-            assertIsNotifChip(latest!!.overflow[0], context, notifIcon, "notif")
+            assertIsNotifChip(latest!!.active[2], context, notif1Icon, "notif1")
+            assertThat(latest!!.overflow).isEmpty()
             assertThat(latest!!.inactive.size).isEqualTo(2)
             assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
 
-            // WHEN the higher priority screen record chip is added
+            // WHEN the screen record chip is added, which replaces media projection
             screenRecordState.value = ScreenRecordModel.Recording
-
-            // THEN the higher priority screen record chip and call are active in that order, and
-            // media projection and notif are demoted in overflow
-            assertThat(latest!!.active.size).isEqualTo(2)
-            assertIsScreenRecordChip(latest!!.active[0])
-            assertIsCallChip(latest!!.active[1], callNotificationKey, context)
-            assertThat(latest!!.overflow.size).isEqualTo(2)
-            assertIsShareToAppChip(latest!!.overflow[0])
-            assertIsNotifChip(latest!!.overflow[1], context, notifIcon, "notif")
-            assertThat(latest!!.inactive.size).isEqualTo(1)
-            assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
-
-            // WHEN screen record and call is dropped
-            screenRecordState.value = ScreenRecordModel.DoingNothing
-            setNotifs(
-                listOf(
-                    activeNotificationModel(
-                        key = "notif",
-                        statusBarChipIcon = notifIcon,
-                        promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
-                    )
+            // AND another notification is added
+            systemClock.advanceTime(2_000)
+            val notif2Icon = createStatusBarIconViewOrNull()
+            activeNotificationListRepository.addNotif(
+                activeNotificationModel(
+                    key = "notif2",
+                    statusBarChipIcon = notif2Icon,
+                    promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
                 )
             )
 
-            // THEN media projection and notif remain
-            assertThat(latest!!.active.size).isEqualTo(2)
+            // THEN screen record, then call, then notif2 are active
+            assertThat(latest!!.active.size).isEqualTo(3)
+            assertIsScreenRecordChip(latest!!.active[0])
+            assertIsCallChip(latest!!.active[1], callNotificationKey, context)
+            assertIsNotifChip(latest!!.active[2], context, notif2Icon, "notif2")
+
+            // AND notif1 and media projection is demoted in overflow
+            assertThat(latest!!.overflow.size).isEqualTo(2)
+            assertIsShareToAppChip(latest!!.overflow[0])
+            assertIsNotifChip(latest!!.overflow[1], context, notif1Icon, "notif1")
+            assertThat(latest!!.inactive.size).isEqualTo(1)
+            assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
+
+            // WHEN screen record and call are dropped
+            screenRecordState.value = ScreenRecordModel.DoingNothing
+            removeOngoingCallState(callNotificationKey)
+
+            // THEN media projection, notif2, and notif1 remain
+            assertThat(latest!!.active.size).isEqualTo(3)
             assertIsShareToAppChip(latest!!.active[0])
-            assertIsNotifChip(latest!!.active[1], context, notifIcon, "notif")
+            assertIsNotifChip(latest!!.active[1], context, notif2Icon, "notif2")
+            assertIsNotifChip(latest!!.active[2], context, notif1Icon, "notif1")
             assertThat(latest!!.overflow).isEmpty()
             assertThat(latest!!.inactive.size).isEqualTo(3)
             assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
 
             // WHEN media projection is dropped
             mediaProjectionState.value = MediaProjectionState.NotProjecting
+            // AND notif2 is dropped
+            systemClock.advanceTime(2_000)
+            activeNotificationListRepository.removeNotif("notif2")
 
-            // THEN only notif is active
+            // THEN only notif1 is active
             assertThat(latest!!.active.size).isEqualTo(1)
-            assertIsNotifChip(latest!!.active[0], context, notifIcon, "notif")
+            assertIsNotifChip(latest!!.active[0], context, notif1Icon, "notif1")
             assertThat(latest!!.overflow).isEmpty()
             assertThat(latest!!.inactive.size).isEqualTo(4)
             assertThat(unused).isEqualTo(MultipleOngoingActivityChipsModelLegacy())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 2f6bedb..ea61b71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -29,7 +29,6 @@
 import com.android.internal.view.AppearanceRegion
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.data.model.StatusBarMode
 import com.android.systemui.statusbar.layout.BoundsPair
@@ -42,6 +41,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -59,7 +59,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class StatusBarModeRepositoryImplTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope = TestScope()
     private val commandQueue = mock<CommandQueue>()
     private val letterboxAppearanceCalculator = mock<LetterboxAppearanceCalculator>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationCloseButtonTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationCloseButtonTest.kt
new file mode 100644
index 0000000..e4b2e6c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationCloseButtonTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import android.app.Notification
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper
+import android.view.MotionEvent
+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.SysuiTestCase
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.stub
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import org.junit.Before
+
+private fun getCloseButton(row: ExpandableNotificationRow): View {
+    val contractedView = row.showingLayout?.contractedChild!!
+    return contractedView.findViewById(com.android.internal.R.id.close_button)
+}
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationCloseButtonTest : SysuiTestCase() {
+    private lateinit var helper: NotificationTestHelper
+
+    @Before
+    fun setUp() {
+        helper = NotificationTestHelper(
+            mContext,
+            mDependency,
+            TestableLooper.get(this)
+        )
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_NOTIFICATION_ADD_X_ON_HOVER_TO_DISMISS)
+    fun verifyWhenFeatureDisabled() {
+        // Enable the notification row to dismiss.
+        helper.dismissibilityProvider.stub {
+            on { isDismissable(any()) } doReturn true
+        }
+
+        // By default, the close button should be gone.
+        val row = createNotificationRow()
+        val closeButton = getCloseButton(row)
+        assertThat(closeButton).isNotNull()
+        assertThat(closeButton.visibility).isEqualTo(View.GONE)
+
+        val hoverEnterEvent = MotionEvent.obtain(
+            0/*downTime=*/,
+            0/*eventTime=*/,
+            MotionEvent.ACTION_HOVER_ENTER,
+            0f/*x=*/,
+            0f/*y=*/,
+            0/*metaState*/
+        )
+
+        // The close button should not show if the feature is disabled.
+        row.onInterceptHoverEvent(hoverEnterEvent)
+        assertThat(closeButton.visibility).isEqualTo(View.GONE)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NOTIFICATION_ADD_X_ON_HOVER_TO_DISMISS)
+    fun verifyOnDismissableNotification() {
+        // Enable the notification row to dismiss.
+        helper.dismissibilityProvider.stub {
+            on { isDismissable(any()) } doReturn true
+        }
+
+        // By default, the close button should be gone.
+        val row = createNotificationRow()
+        val closeButton = getCloseButton(row)
+        assertThat(closeButton).isNotNull()
+        assertThat(closeButton.visibility).isEqualTo(View.GONE)
+
+        val hoverEnterEvent = MotionEvent.obtain(
+            0/*downTime=*/,
+            0/*eventTime=*/,
+            MotionEvent.ACTION_HOVER_ENTER,
+            0f/*x=*/,
+            0f/*y=*/,
+            0/*metaState*/
+        )
+
+        // When the row is hovered, the close button should show.
+        row.onInterceptHoverEvent(hoverEnterEvent)
+        assertThat(closeButton.visibility).isEqualTo(View.VISIBLE)
+
+        val hoverExitEvent = MotionEvent.obtain(
+            0/*downTime=*/,
+            0/*eventTime=*/,
+            MotionEvent.ACTION_HOVER_EXIT,
+            0f/*x=*/,
+            0f/*y=*/,
+            0/*metaState*/
+        )
+
+        // When hover exits the row, the close button should be gone again.
+        row.onInterceptHoverEvent(hoverExitEvent)
+        assertThat(closeButton.visibility).isEqualTo(View.GONE)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NOTIFICATION_ADD_X_ON_HOVER_TO_DISMISS)
+    fun verifyOnUndismissableNotification() {
+        // By default, the close button should be gone.
+        val row = createNotificationRow()
+        val closeButton = getCloseButton(row)
+        assertThat(closeButton).isNotNull()
+        assertThat(closeButton.visibility).isEqualTo(View.GONE)
+
+        val hoverEnterEvent = MotionEvent.obtain(
+            0/*downTime=*/,
+            0/*eventTime=*/,
+            MotionEvent.ACTION_HOVER_ENTER,
+            0f/*x=*/,
+            0f/*y=*/,
+            0/*metaState*/
+        )
+
+        // Because the host notification cannot be dismissed, the close button should not show.
+        row.onInterceptHoverEvent(hoverEnterEvent)
+        assertThat(closeButton.visibility).isEqualTo(View.GONE)
+    }
+
+    private fun createNotificationRow(): ExpandableNotificationRow {
+        val notification = Notification.Builder(context, "channel")
+            .setContentTitle("title")
+            .setContentText("text")
+            .setSmallIcon(R.drawable.ic_person)
+            .build()
+
+       return helper.createRow(notification)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
index a13b864..9faab58 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
@@ -24,6 +24,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -152,4 +153,10 @@
     fun canShowFullScreen() {
         assertThat(underTest.isFullScreenCapable()).isFalse()
     }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun getPeopleNotificationType() {
+        assertThat(underTest.getPeopleNotificationType()).isEqualTo(TYPE_NON_PERSON)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
index b6889af..12ade62 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
@@ -29,8 +29,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.RankingBuilder
+import com.android.systemui.statusbar.notification.mockNotificationActivityStarter
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.entryAdapterFactory
+import com.android.systemui.statusbar.notification.row.mockNotificationActionClickManager
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
 import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
 import com.android.systemui.testKosmos
@@ -39,6 +42,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito
+import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 
 @SmallTest
@@ -332,6 +336,16 @@
 
     @Test
     @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun getPeopleNotificationType() {
+        val entry = kosmos.msgStyleBubbleableFullPerson
+
+        underTest = factory.create(entry) as NotificationEntryAdapter
+
+        assertThat(underTest.peopleNotificationType).isEqualTo(TYPE_FULL_PERSON)
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
     fun canShowFullScreen() {
         val notification: Notification =
             Notification.Builder(mContext, "")
@@ -351,20 +365,79 @@
 
     @Test
     @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun onDragSuccess() {
+        val notification: Notification =
+            Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .addAction(mock(Notification.Action::class.java))
+                .build()
+        val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+        underTest = factory.create(entry) as NotificationEntryAdapter
+
+        underTest.onDragSuccess()
+        verify(kosmos.mockNotificationActivityStarter).onDragSuccess(entry)
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
     fun onNotificationBubbleIconClicked() {
         val notification: Notification =
             Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
 
-        val entry =
-            NotificationEntryBuilder()
-                .setNotification(notification)
-                .setImportance(NotificationManager.IMPORTANCE_MIN)
-                .build()
+        val entry = NotificationEntryBuilder().setNotification(notification).build()
 
         underTest = factory.create(entry) as NotificationEntryAdapter
 
         underTest.onNotificationBubbleIconClicked()
-        verify((factory as? EntryAdapterFactoryImpl)?.getNotificationActivityStarter())
-            ?.onNotificationBubbleIconClicked(entry)
+        verify(kosmos.mockNotificationActivityStarter).onNotificationBubbleIconClicked(entry)
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun onNotificationActionClicked() {
+        val notification: Notification =
+            Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .addAction(Mockito.mock(Notification.Action::class.java))
+                .build()
+
+        val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+        underTest = factory.create(entry) as NotificationEntryAdapter
+        underTest.onNotificationActionClicked()
+        verify(kosmos.mockNotificationActionClickManager).onNotificationActionClicked(entry)
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun getDismissState() {
+        val notification: Notification =
+            Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+        val entry = NotificationEntryBuilder().setNotification(notification).build()
+        entry.dismissState = NotificationEntry.DismissState.PARENT_DISMISSED
+
+        underTest = factory.create(entry) as NotificationEntryAdapter
+
+        assertThat(underTest.dismissState).isEqualTo(entry.dismissState)
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun onEntryClicked() {
+        val notification: Notification =
+            Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .addAction(mock(Notification.Action::class.java))
+                .build()
+        val entry = NotificationEntryBuilder().setNotification(notification).build()
+        val row = mock(ExpandableNotificationRow::class.java)
+
+        underTest = factory.create(entry) as NotificationEntryAdapter
+
+
+        underTest.onEntryClicked(row)
+        verify(kosmos.mockNotificationActivityStarter).onNotificationClicked(entry, row)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 790b2c3..bfd700d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -58,6 +58,7 @@
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SbnBuilder;
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -151,7 +152,8 @@
                 .build();
 
         NotificationEntry entry =
-                new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+                new NotificationEntry(sbn, ranking,
+                        UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
 
         assertFalse(entry.isBlockable());
     }
@@ -251,7 +253,8 @@
                 .build();
 
         NotificationEntry entry =
-                new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+                new NotificationEntry(sbn, ranking,
+                        UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
 
         assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
         assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
@@ -365,7 +368,8 @@
                 .setKey(sbn.getKey())
                 .build();
         NotificationEntry entry =
-                new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+                new NotificationEntry(sbn, ranking,
+                        UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
 
         assertFalse(entry.isChannelVisibilityPrivate());
     }
@@ -378,7 +382,8 @@
                 .setKey(sbn.getKey())
                 .build();
         NotificationEntry entry =
-                new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+                new NotificationEntry(sbn, ranking,
+                        UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
 
         assertFalse(entry.isChannelVisibilityPrivate());
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt
index 8a9720e..7321808 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt
@@ -34,6 +34,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
+import kotlin.test.assertEquals
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -87,6 +88,18 @@
         isFalse()
     }
 
+    @Test
+    fun testBundler_getBundleIdOrNull_returnBundleId() {
+        val classifiedEntry = makeEntryOfChannelType(PROMOTIONS_ID)
+        assertEquals(coordinator.bundler.getBundleIdOrNull(classifiedEntry), PROMOTIONS_ID)
+    }
+
+    @Test
+    fun testBundler_getBundleIdOrNull_returnNull() {
+        val unclassifiedEntry = makeEntryOfChannelType("not system channel")
+        assertEquals(coordinator.bundler.getBundleIdOrNull(unclassifiedEntry), null)
+    }
+
     private fun makeEntryOfChannelType(
         type: String,
         buildBlock: NotificationEntryBuilder.() -> Unit = {}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt
index 7fa157f..ba2d40b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt
@@ -27,7 +27,6 @@
 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.applicationCoroutineScope
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
@@ -35,10 +34,10 @@
 import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.core.StatusBarRootModernization
-import com.android.systemui.statusbar.notification.buildNotificationEntry
-import com.android.systemui.statusbar.notification.buildOngoingCallEntry
-import com.android.systemui.statusbar.notification.buildPromotedOngoingEntry
 import com.android.systemui.statusbar.notification.collection.buildEntry
+import com.android.systemui.statusbar.notification.collection.buildNotificationEntry
+import com.android.systemui.statusbar.notification.collection.buildOngoingCallEntry
+import com.android.systemui.statusbar.notification.collection.buildPromotedOngoingEntry
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.notifPipeline
@@ -49,7 +48,6 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.withArgCaptor
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
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 3098355..44d88c3 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,6 +50,8 @@
 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.mockNotificationActionClickManager
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
 import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
 import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -138,6 +140,7 @@
                 headsUpViewBinder,
                 visualInterruptionDecisionProvider,
                 remoteInputManager,
+                kosmos.mockNotificationActionClickManager,
                 launchFullScreenIntentProvider,
                 flags,
                 statusBarNotificationChipsInteractor,
@@ -161,8 +164,14 @@
             verify(notifPipeline).addOnBeforeFinalizeFilterListener(capture())
         }
         onHeadsUpChangedListener = withArgCaptor { verify(headsUpManager).addListener(capture()) }
-        actionPressListener = withArgCaptor {
-            verify(remoteInputManager).addActionPressListener(capture())
+        actionPressListener = if (NotificationBundleUi.isEnabled) {
+            withArgCaptor {
+                verify(kosmos.mockNotificationActionClickManager).addActionClickListener(capture())
+            }
+        } else {
+            withArgCaptor {
+                verify(remoteInputManager).addActionPressListener(capture())
+            }
         }
         given(headsUpManager.allEntries).willAnswer { huns.stream() }
         given(headsUpManager.isHeadsUpEntry(anyString())).willAnswer { invocation ->
@@ -260,7 +269,7 @@
         addHUN(entry)
 
         actionPressListener.accept(entry)
-        verify(headsUpManager, times(1)).setUserActionMayIndirectlyRemove(entry)
+        verify(headsUpManager, times(1)).setUserActionMayIndirectlyRemove(entry.key)
 
         whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(true)
         assertFalse(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt
index a905394..e28e587 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.collection.BundleEntry
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -280,6 +281,8 @@
         val group = GroupEntryBuilder().setSummary(parent).addChild(child1).addChild(child2).build()
         val listEntryList = listOf(group, solo1, solo2)
         val notificationEntryList = listOf(solo1, solo2, parent, child1, child2)
+        val bundle = BundleEntry("bundleKey")
+        val bundleList = listOf(bundle)
 
         runCoordinatorTest {
             // All entries are added (and now unseen)
@@ -300,6 +303,11 @@
             assertThatTopOngoingKey().isEqualTo(null)
             assertThatTopUnseenKey().isEqualTo(solo1.key)
 
+            // TEST: bundle is not picked
+            onBeforeTransformGroupsListener.onBeforeTransformGroups(bundleList)
+            assertThatTopOngoingKey().isEqualTo(null)
+            assertThatTopUnseenKey().isEqualTo(null)
+
             // TEST: if top-ranked unseen is colorized, fall back to #2 ranked unseen
             solo1.setColorizedFgs(true)
             onBeforeTransformGroupsListener.onBeforeTransformGroups(listEntryList)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index c5752691d..f9405af 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -16,7 +16,9 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator;
 
+import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
 
@@ -45,6 +47,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.collection.BundleEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -273,6 +276,18 @@
     }
 
     @Test
+    public void testSilentSectioner_acceptsBundle() {
+        BundleEntry bundleEntry = new BundleEntry("testBundleKey");
+        assertTrue(mSilentSectioner.isInSection(bundleEntry));
+    }
+
+    @Test
+    public void testMinimizedSectioner_rejectsBundle() {
+        BundleEntry bundleEntry = new BundleEntry("testBundleKey");
+        assertFalse(mMinimizedSectioner.isInSection(bundleEntry));
+    }
+
+    @Test
     public void testMinSection() {
         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
         setRankingAmbient(true);
@@ -327,6 +342,13 @@
     }
 
     @Test
+    public void testAlertingSectioner_rejectsBundle() {
+        for (String id : SYSTEM_RESERVED_IDS) {
+            assertFalse(mAlertingSectioner.isInSection(makeClassifiedNotifEntry(id)));
+        }
+    }
+
+    @Test
     public void statusBarStateCallbackTest() {
         mStatusBarStateCallback.onDozeAmountChanged(1f, 1f);
         verify(mInvalidationListener, times(1))
@@ -379,4 +401,11 @@
                 .build());
         assertEquals(ambient, mEntry.getRanking().isAmbient());
     }
+
+    private NotificationEntry makeClassifiedNotifEntry(String channelId) {
+        NotificationChannel channel = new NotificationChannel(channelId, channelId, IMPORTANCE_LOW);
+        return new NotificationEntryBuilder()
+                .updateRanking((rankingBuilder -> rankingBuilder.setChannel(channel)))
+                .build();
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
index ef0a416..d532010 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
@@ -50,6 +50,7 @@
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener
 import com.android.systemui.statusbar.notification.collection.notifPipeline
@@ -323,7 +324,10 @@
             setPulsing(true)
 
             // WHEN we temporarily allow section changes for this notification entry
-            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+            underTest.temporarilyAllowSectionChanges(
+                entry,
+                UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+            )
 
             // THEN group changes aren't allowed
             assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
@@ -349,7 +353,10 @@
             setPulsing(false)
 
             // WHEN we temporarily allow section changes for this notification entry
-            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.uptimeMillis())
+            underTest.temporarilyAllowSectionChanges(
+                entry,
+                UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+            )
 
             // THEN the notification list is invalidated
             verifyStabilityManagerWasInvalidated(times(1))
@@ -365,7 +372,10 @@
             setPulsing(false)
 
             // WHEN we temporarily allow section changes for this notification entry
-            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+            underTest.temporarilyAllowSectionChanges(
+                entry,
+                UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+            )
 
             // THEN invalidate is not called because this entry was never suppressed from reordering
             verifyStabilityManagerWasInvalidated(never())
@@ -382,7 +392,10 @@
             assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
 
             // WHEN we temporarily allow section changes for this notification entry
-            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+            underTest.temporarilyAllowSectionChanges(
+                entry,
+                UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+            )
 
             // THEN invalidate is not called because this entry was never suppressed from
             // reordering;
@@ -415,7 +428,10 @@
             setPulsing(true)
 
             // WHEN we temporarily allow section changes for this notification entry
-            underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+            underTest.temporarilyAllowSectionChanges(
+                entry,
+                UseElapsedRealtimeForCreationTime.getCurrentTime(fakeSystemClock),
+            )
             // can now reorder, so invalidates
             verifyStabilityManagerWasInvalidated(times(1))
 
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 e22acd5..8560b66 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
@@ -37,6 +37,7 @@
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.log.assertLogsWtfs
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shadeTestUtil
@@ -205,8 +206,7 @@
     fun pinnedHeadsUpStatuses_pinnedByUser_butFlagOff_returnsNotPinned() {
         val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
         entry.row = testHelper.createRow()
-        underTest.showNotification(entry, isPinnedByUser = true)
-
+        assertLogsWtfs { underTest.showNotification(entry, isPinnedByUser = true) }
         assertThat(underTest.hasPinnedHeadsUp()).isFalse()
         assertThat(underTest.pinnedHeadsUpStatus()).isEqualTo(PinnedStatus.NotPinned)
     }
@@ -1071,7 +1071,7 @@
 
         assertThat(underTest.canRemoveImmediately(notifEntry.key)).isFalse()
 
-        underTest.setUserActionMayIndirectlyRemove(notifEntry)
+        underTest.setUserActionMayIndirectlyRemove(notifEntry.key)
 
         assertThat(underTest.canRemoveImmediately(notifEntry.key)).isTrue()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index 3116143..893c179 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -376,12 +376,67 @@
     @Test
     @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
     fun extractContent_fromBigTextStyle() {
-        val entry = createEntry { setStyle(BigTextStyle()) }
+        val entry = createEntry {
+            setContentTitle(TEST_CONTENT_TITLE)
+            setContentText(TEST_CONTENT_TEXT)
+            setStyle(
+                BigTextStyle()
+                    .bigText(TEST_BIG_TEXT)
+                    .setBigContentTitle(TEST_BIG_CONTENT_TITLE)
+                    .setSummaryText(TEST_SUMMARY_TEXT)
+            )
+        }
 
         val content = extractContent(entry)
 
         assertThat(content).isNotNull()
         assertThat(content?.style).isEqualTo(Style.BigText)
+        assertThat(content?.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
+        assertThat(content?.text).isEqualTo(TEST_BIG_TEXT)
+    }
+
+    @Test
+    @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+    fun extractContent_fromBigTextStyle_fallbackToContentTitle() {
+        val entry = createEntry {
+            setContentTitle(TEST_CONTENT_TITLE)
+            setContentText(TEST_CONTENT_TEXT)
+            setStyle(
+                BigTextStyle()
+                    .bigText(TEST_BIG_TEXT)
+                    // bigContentTitle unset
+                    .setSummaryText(TEST_SUMMARY_TEXT)
+            )
+        }
+
+        val content = extractContent(entry)
+
+        assertThat(content).isNotNull()
+        assertThat(content?.style).isEqualTo(Style.BigText)
+        assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE)
+        assertThat(content?.text).isEqualTo(TEST_BIG_TEXT)
+    }
+
+    @Test
+    @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+    fun extractContent_fromBigTextStyle_fallbackToContentText() {
+        val entry = createEntry {
+            setContentTitle(TEST_CONTENT_TITLE)
+            setContentText(TEST_CONTENT_TEXT)
+            setStyle(
+                BigTextStyle()
+                    // bigText unset
+                    .setBigContentTitle(TEST_BIG_CONTENT_TITLE)
+                    .setSummaryText(TEST_SUMMARY_TEXT)
+            )
+        }
+
+        val content = extractContent(entry)
+
+        assertThat(content).isNotNull()
+        assertThat(content?.style).isEqualTo(Style.BigText)
+        assertThat(content?.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
+        assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT)
     }
 
     @Test
@@ -498,6 +553,10 @@
         private const val TEST_CONTENT_TEXT = "content text"
         private const val TEST_SHORT_CRITICAL_TEXT = "short"
 
+        private const val TEST_BIG_CONTENT_TITLE = "big content title"
+        private const val TEST_BIG_TEXT = "big text"
+        private const val TEST_SUMMARY_TEXT = "summary text"
+
         private const val TEST_PROGRESS = 50
         private const val TEST_PROGRESS_MAX = 100
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt
index 6926677..6192399 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt
@@ -28,9 +28,9 @@
 import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.core.StatusBarRootModernization
-import com.android.systemui.statusbar.notification.buildNotificationEntry
-import com.android.systemui.statusbar.notification.buildOngoingCallEntry
-import com.android.systemui.statusbar.notification.buildPromotedOngoingEntry
+import com.android.systemui.statusbar.notification.collection.buildNotificationEntry
+import com.android.systemui.statusbar.notification.collection.buildOngoingCallEntry
+import com.android.systemui.statusbar.notification.collection.buildPromotedOngoingEntry
 import com.android.systemui.statusbar.notification.domain.interactor.renderNotificationListInteractor
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
 import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index bb12eff..d306a5b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -58,11 +58,13 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.SmartReplyConstants
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.withArgCaptor
 import com.android.systemui.util.time.SystemClock
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
 import com.google.android.msdl.domain.MSDLPlayer
 import junit.framework.Assert
 import org.junit.After
@@ -84,6 +86,8 @@
     private val appName = "MyApp"
     private val notifKey = "MyNotifKey"
 
+    private val kosmos = testKosmos()
+
     private val view: ExpandableNotificationRow = mock()
     private val activableNotificationViewController: ActivatableNotificationViewController = mock()
     private val rivSubComponentFactory: RemoteInputViewSubcomponent.Factory = mock()
@@ -160,6 +164,7 @@
                 msdlPlayer,
                 rebindingTracker,
                 entryAdapterFactory,
+                kosmos.windowRootViewBlurInteractor,
             )
         whenever(view.childrenContainer).thenReturn(childrenContainer)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 9f35d63..99f2596 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC;
-import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_OTP;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
@@ -472,7 +472,7 @@
                 com.android.systemui.res.R.drawable.ic_person).setStyle(messagingStyle).build();
         ExpandableNotificationRow row = mHelper.createRow(messageNotif);
         inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
-                REDACTION_TYPE_SENSITIVE_CONTENT, row);
+                REDACTION_TYPE_OTP, row);
         NotificationContentView publicView = row.getPublicLayout();
         assertNotNull(publicView);
         // The display name should be included, but not the content or message text
@@ -493,7 +493,7 @@
                 .build();
         ExpandableNotificationRow row = mHelper.createRow(notif);
         inflateAndWait(false, mNotificationInflater, FLAG_CONTENT_VIEW_PUBLIC,
-                REDACTION_TYPE_SENSITIVE_CONTENT, row);
+                REDACTION_TYPE_OTP, row);
         NotificationContentView publicView = row.getPublicLayout();
         assertNotNull(publicView);
         assertFalse(hasText(publicView, contentText));
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 1b8d64d..387c62d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -71,6 +71,7 @@
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
 import com.android.systemui.statusbar.notification.row.icon.appIconProvider
@@ -80,6 +81,9 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.util.kotlin.JavaAdapter
 import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import kotlin.test.assertNotNull
+import kotlin.test.fail
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
@@ -107,9 +111,6 @@
 import org.mockito.kotlin.whenever
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
-import java.util.Optional
-import kotlin.test.assertNotNull
-import kotlin.test.fail
 
 /** Tests for [NotificationGutsManager]. */
 @SmallTest
@@ -149,6 +150,7 @@
     @Mock private lateinit var launcherApps: LauncherApps
     @Mock private lateinit var shortcutManager: ShortcutManager
     @Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+    @Mock private lateinit var packageDemotionInteractor: PackageDemotionInteractor
     @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
     @Mock private lateinit var contextTracker: UserContextProvider
     @Mock private lateinit var bubblesManager: BubblesManager
@@ -214,6 +216,7 @@
                 launcherApps,
                 shortcutManager,
                 channelEditorDialogController,
+                packageDemotionInteractor,
                 contextTracker,
                 assistantFeedbackController,
                 Optional.of(bubblesManager),
@@ -509,6 +512,7 @@
             .setImportance(NotificationManager.IMPORTANCE_HIGH)
             .build()
 
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
         val statusBarNotification = entry.sbn
         gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -521,6 +525,7 @@
                 any<NotificationIconStyleProvider>(),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                any<PackageDemotionInteractor>(),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -530,6 +535,7 @@
                 any<UiEventLogger>(),
                 /* isDeviceProvisioned = */ eq(false),
                 /* isNonblockable = */ eq(false),
+                /* isDismissable = */ eq(true),
                 /* wasShownHighPriority = */ eq(true),
                 eq(assistantFeedbackController),
                 eq(metricsLogger),
@@ -545,6 +551,7 @@
         NotificationEntryHelper.modifyRanking(row.entry)
             .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
             .build()
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         val statusBarNotification = row.entry.sbn
         val entry = row.entry
 
@@ -560,6 +567,7 @@
                 any<NotificationIconStyleProvider>(),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                any<PackageDemotionInteractor>(),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -569,6 +577,7 @@
                 any<UiEventLogger>(),
                 /* isDeviceProvisioned = */ eq(true),
                 /* isNonblockable = */ eq(false),
+                /* isDismissable = */ eq(true),
                 /* wasShownHighPriority = */ eq(false),
                 eq(assistantFeedbackController),
                 eq(metricsLogger),
@@ -584,6 +593,7 @@
         NotificationEntryHelper.modifyRanking(row.entry)
             .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
             .build()
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         val statusBarNotification = row.entry.sbn
         val entry = row.entry
 
@@ -597,6 +607,7 @@
                 any<NotificationIconStyleProvider>(),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                any<PackageDemotionInteractor>(),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -606,6 +617,7 @@
                 any<UiEventLogger>(),
                 /* isDeviceProvisioned = */ eq(false),
                 /* isNonblockable = */ eq(false),
+                /* isDismissable = */ eq(true),
                 /* wasShownHighPriority = */ eq(false),
                 eq(assistantFeedbackController),
                 eq(metricsLogger),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
index 96ae070..0ac5fe9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
@@ -49,6 +49,7 @@
 import android.view.View.VISIBLE
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.core.view.isVisible
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
@@ -57,17 +58,18 @@
 import com.android.internal.logging.uiEventLoggerFake
 import com.android.systemui.Dependency
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.RankingBuilder
 import com.android.systemui.statusbar.notification.AssistantFeedbackController
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
 import com.android.systemui.statusbar.notification.row.icon.appIconProvider
 import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
+import com.android.systemui.testKosmos
 import com.android.telecom.telecomManager
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.CountDownLatch
@@ -89,7 +91,7 @@
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
 class NotificationInfoTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().also { it.testCase = this }
 
     private lateinit var underTest: NotificationInfo
     private lateinit var notificationChannel: NotificationChannel
@@ -105,6 +107,7 @@
     private val onUserInteractionCallback = mock<OnUserInteractionCallback>()
     private val mockINotificationManager = mock<INotificationManager>()
     private val channelEditorDialogController = mock<ChannelEditorDialogController>()
+    private val packageDemotionInteractor = mock<PackageDemotionInteractor>()
     private val assistantFeedbackController = mock<AssistantFeedbackController>()
 
     @Before
@@ -863,6 +866,31 @@
         assertThat(underTest.findViewById<View>(R.id.feedback).visibility).isEqualTo(GONE)
     }
 
+    @Test
+    @Throws(RemoteException::class)
+    fun testDismissListenerBound() {
+        val latch = CountDownLatch(1)
+        bindNotification(onCloseClick = { _: View? -> latch.countDown() })
+
+        val dismissView = underTest.findViewById<View>(R.id.inline_dismiss)
+        assertThat(dismissView.isVisible).isTrue()
+        dismissView.performClick()
+
+        // Verify that listener was triggered.
+        assertThat(latch.count).isEqualTo(0)
+    }
+
+    @Test
+    @Throws(RemoteException::class)
+    fun testDismissHiddenWhenUndismissable() {
+
+        entry.sbn.notification.flags =
+            entry.sbn.notification.flags or android.app.Notification.FLAG_NO_DISMISS
+        bindNotification(isDismissable = false)
+        val dismissView = underTest.findViewById<View>(R.id.inline_dismiss)
+        assertThat(dismissView.isVisible).isFalse()
+    }
+
     private fun bindNotification(
         pm: PackageManager = this.mockPackageManager,
         iNotificationManager: INotificationManager = this.mockINotificationManager,
@@ -871,6 +899,7 @@
         onUserInteractionCallback: OnUserInteractionCallback = this.onUserInteractionCallback,
         channelEditorDialogController: ChannelEditorDialogController =
             this.channelEditorDialogController,
+        packageDemotionInteractor: PackageDemotionInteractor = this.packageDemotionInteractor,
         pkg: String = TEST_PACKAGE_NAME,
         notificationChannel: NotificationChannel = this.notificationChannel,
         entry: NotificationEntry = this.entry,
@@ -880,6 +909,7 @@
         uiEventLogger: UiEventLogger = this.uiEventLogger,
         isDeviceProvisioned: Boolean = true,
         isNonblockable: Boolean = false,
+        isDismissable: Boolean = true,
         wasShownHighPriority: Boolean = true,
         assistantFeedbackController: AssistantFeedbackController = this.assistantFeedbackController,
         metricsLogger: MetricsLogger = kosmos.metricsLogger,
@@ -892,6 +922,7 @@
             iconStyleProvider,
             onUserInteractionCallback,
             channelEditorDialogController,
+            packageDemotionInteractor,
             pkg,
             notificationChannel,
             entry,
@@ -901,6 +932,7 @@
             uiEventLogger,
             isDeviceProvisioned,
             isNonblockable,
+            isDismissable,
             wasShownHighPriority,
             assistantFeedbackController,
             metricsLogger,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 9fdfca1..ce120c5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -38,7 +38,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -54,6 +56,7 @@
 @SmallTest
 public class NotificationMenuRowTest extends LeakCheckedTest {
 
+    private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
     private ExpandableNotificationRow mRow;
     private View mView;
     private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@@ -66,6 +69,8 @@
         mPeopleNotificationIdentifier = mock(PeopleNotificationIdentifier.class);
         NotificationEntry entry = new NotificationEntryBuilder().build();
         when(mRow.getEntry()).thenReturn(entry);
+        EntryAdapter entryAdapter = mKosmos.getEntryAdapterFactory().create(entry);
+        when(mRow.getEntryAdapter()).thenReturn(entryAdapter);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 31413b0..063a04a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -36,8 +36,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_OTP
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
-import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
@@ -538,7 +538,7 @@
             false,
             notificationInflater,
             FLAG_CONTENT_VIEW_PUBLIC,
-            REDACTION_TYPE_SENSITIVE_CONTENT,
+            REDACTION_TYPE_OTP,
             newRow,
         )
         // The display name should be included, but not the content or message text
@@ -566,7 +566,7 @@
             false,
             notificationInflater,
             FLAG_CONTENT_VIEW_PUBLIC,
-            REDACTION_TYPE_SENSITIVE_CONTENT,
+            REDACTION_TYPE_OTP,
             newRow,
         )
         var publicView = newRow.publicLayout
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index af67a04..2d4063b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -27,7 +27,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
 import android.testing.TestableResources;
 import android.view.View;
@@ -39,7 +38,6 @@
 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.animation.AnimatorTestRule;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -93,7 +91,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_NOTIFICATION_UNDO_GUTS_ON_CONFIG_CHANGED)
     public void closeControls_withoutSave_performsUndo() {
         ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
         mUnderTest.mSelectedOption = options.getFirst();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 8e7733b..e6b2c25 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -29,6 +29,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -60,11 +61,11 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
 import com.android.systemui.flags.FeatureFlagsClassic;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.media.controls.util.MediaFeatureFlag;
 import com.android.systemui.media.dialog.MediaOutputDialogManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.SmartReplyController;
@@ -363,6 +364,7 @@
                 .setUid(UID)
                 .setInitialPid(2000)
                 .setNotification(summary)
+                .setUser(USER_HANDLE)
                 .setParent(GroupEntry.ROOT_ENTRY)
                 .build();
         GroupEntryBuilder groupEntry = new GroupEntryBuilder()
@@ -743,11 +745,12 @@
                 mock(MetricsLogger.class),
                 mock(PeopleNotificationIdentifier.class),
                 mock(NotificationIconStyleProvider.class),
-                mock(VisualStabilityCoordinator.class)
+                mock(VisualStabilityCoordinator.class),
+                mock(NotificationActionClickManager.class)
         ).create(entry);
 
         row.initialize(
-                entryAdapter,
+                spy(entryAdapter),
                 entry,
                 mock(RemoteInputViewSubcomponent.Factory.class),
                 APP_NAME,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
index 5638e0b..209dfb2d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 
@@ -92,6 +93,8 @@
     @Mock
     private ChannelEditorDialogController mChannelEditorDialogController;
     @Mock
+    private PackageDemotionInteractor mPackageDemotionInteractor;
+    @Mock
     private AssistantFeedbackController mAssistantFeedbackController;
     @Mock
     private TelecomManager mTelecomManager;
@@ -138,6 +141,7 @@
                 mMockIconStyleProvider,
                 mOnUserInteractionCallback,
                 mChannelEditorDialogController,
+                mPackageDemotionInteractor,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
                 mEntry,
@@ -148,6 +152,7 @@
                 true,
                 false,
                 true,
+                true,
                 mAssistantFeedbackController,
                 mMetricsLogger,
                 null);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
index 3d1fdee..961616c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
@@ -22,8 +22,6 @@
 import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository
 import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.testKosmos
-import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -34,16 +32,12 @@
 @SmallTest
 class ActivatableNotificationViewModelTest : SysuiTestCase() {
 
-    private val kosmos = testKosmos()
-
     // fakes
     private val a11yRepo = FakeAccessibilityRepository()
 
     // real impls
     private val a11yInteractor = AccessibilityInteractor(a11yRepo)
-    private val windowRootViewBlurInteractor = kosmos.windowRootViewBlurInteractor
-    private val underTest = ActivatableNotificationViewModel(a11yInteractor,
-        windowRootViewBlurInteractor)
+    private val underTest = ActivatableNotificationViewModel(a11yInteractor)
 
     @Test
     fun isTouchable_whenA11yTouchExplorationDisabled() = runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt
index 531b30b..0fb0548 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shared/TestActiveNotificationModel.kt
@@ -18,27 +18,27 @@
 import com.google.common.truth.Correspondence
 
 val byKey: Correspondence<ActiveNotificationModel, String> =
-    Correspondence.transforming({ it?.key }, "has a key of")
+    Correspondence.transforming({ it.key }, "has a key of")
 val byIsAmbient: Correspondence<ActiveNotificationModel, Boolean> =
-    Correspondence.transforming({ it?.isAmbient }, "has an isAmbient value of")
+    Correspondence.transforming({ it.isAmbient }, "has an isAmbient value of")
 val byIsSuppressedFromStatusBar: Correspondence<ActiveNotificationModel, Boolean> =
     Correspondence.transforming(
-        { it?.isSuppressedFromStatusBar },
+        { it.isSuppressedFromStatusBar },
         "has an isSuppressedFromStatusBar value of",
     )
 val byIsSilent: Correspondence<ActiveNotificationModel, Boolean> =
-    Correspondence.transforming({ it?.isSilent }, "has an isSilent value of")
+    Correspondence.transforming({ it.isSilent }, "has an isSilent value of")
 val byIsRowDismissed: Correspondence<ActiveNotificationModel, Boolean> =
-    Correspondence.transforming({ it?.isRowDismissed }, "has an isRowDismissed value of")
+    Correspondence.transforming({ it.isRowDismissed }, "has an isRowDismissed value of")
 val byIsLastMessageFromReply: Correspondence<ActiveNotificationModel, Boolean> =
     Correspondence.transforming(
-        { it?.isLastMessageFromReply },
+        { it.isLastMessageFromReply },
         "has an isLastMessageFromReply value of",
     )
 val byIsPulsing: Correspondence<ActiveNotificationModel, Boolean> =
-    Correspondence.transforming({ it?.isPulsing }, "has an isPulsing value of")
+    Correspondence.transforming({ it.isPulsing }, "has an isPulsing value of")
 val byIsPromoted: Correspondence<ActiveNotificationModel, Boolean> =
     Correspondence.transforming(
-        { it?.promotedContent != null },
+        { it.promotedContent != null },
         "has (or doesn't have) a promoted content model",
     )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
index 048028c..db0c59f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
@@ -23,12 +23,12 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.power.data.repository.fakePowerRepository
 import com.android.systemui.statusbar.lockscreenShadeTransitionController
 import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -44,7 +44,7 @@
 class NotificationShelfInteractorTest : SysuiTestCase() {
 
     private val kosmos =
-        Kosmos().apply {
+        testKosmos().apply {
             testCase = this@NotificationShelfInteractorTest
             lockscreenShadeTransitionController = mock()
             screenOffAnimationController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 6381b4e..7265262 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testCase
@@ -35,6 +34,7 @@
 import com.android.systemui.shade.domain.interactor.enableSplitShade
 import com.android.systemui.statusbar.lockscreenShadeTransitionController
 import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -50,7 +50,7 @@
 class NotificationShelfViewModelTest : SysuiTestCase() {
 
     private val kosmos =
-        Kosmos().apply {
+        testKosmos().apply {
             testCase = this@NotificationShelfViewModelTest
             lockscreenShadeTransitionController = mock()
             screenOffAnimationController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
index 13da04e..378ffb5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -16,16 +16,22 @@
 
 package com.android.systemui.statusbar.notification.stack
 
+import android.app.Notification
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.res.R
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
 import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository
 import com.android.systemui.statusbar.notification.headsup.AvalancheController
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -33,6 +39,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyString
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
 
@@ -389,6 +396,36 @@
         assertThat(sut.isClosing).isFalse()
     }
     // endregion
+
+    // region isPulsing
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun isPulsing_key() {
+        whenever(headsupRepository.isHeadsUpEntry(anyString())).thenReturn(true);
+        sut.isPulsing = true
+        assertThat(sut.isPulsing("key")).isTrue()
+
+        whenever(headsupRepository.isHeadsUpEntry(anyString())).thenReturn(false);
+        sut.isPulsing = true
+        assertThat(sut.isPulsing("key")).isFalse()
+    }
+
+    @Test
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
+    fun isPulsing_entry() {
+        val notification: Notification =
+            Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+        val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+        sut.isPulsing = true
+        entry.setIsHeadsUpEntry(true)
+        assertThat(sut.isPulsing(entry)).isTrue()
+
+        sut.isPulsing = true
+        entry.setIsHeadsUpEntry(false)
+        assertThat(sut.isPulsing(entry)).isFalse()
+    }
+    // endregion
 }
 
 // region Arrange helper methods.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 08ecbac..41cca19 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.testKosmos
 import com.google.common.truth.Expect
 import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
 import org.junit.Assume
 import org.junit.Before
 import org.junit.Rule
@@ -1572,7 +1573,11 @@
         fullStackHeight: Float = 3000f,
     ) {
         ambientState.headsUpTop = headsUpTop
-        ambientState.headsUpBottom = headsUpBottom
+        if (NotificationsHunSharedAnimationValues.isEnabled) {
+            headsUpAnimator.headsUpAppearHeightBottom = headsUpBottom.roundToInt()
+        } else {
+            ambientState.headsUpBottom = headsUpBottom
+        }
         ambientState.stackTop = stackTop
         ambientState.stackCutoff = stackCutoff
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index f6c031f..8e3bdc4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -219,7 +219,6 @@
             )
     }
 
-    @DisableFlags(Flags.FLAG_PHYSICAL_NOTIFICATION_MOVEMENT)
     @Test
     @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
     fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim_flagOn() {
@@ -246,7 +245,7 @@
     }
 
     @Test
-    @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+    @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME, Flags.FLAG_PHYSICAL_NOTIFICATION_MOVEMENT)
     fun startAnimationForEvents_startsHeadsUpDisappearAnim_flagOff() {
         val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
         val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
@@ -277,6 +276,7 @@
 
     @Test
     @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+    @DisableFlags(Flags.FLAG_PHYSICAL_NOTIFICATION_MOVEMENT)
     fun startAnimationForEvents_startsHeadsUpDisappearAnim_flagOn() {
         val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
         val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
index 14e7cdc..3b836b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
-import android.testing.TestableLooper.RunWithLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
@@ -32,7 +32,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@RunWithLooper
+@EnableSceneContainer
 class NotificationsPlaceholderViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val underTest by lazy { kosmos.notificationsPlaceholderViewModel }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index e2330f4..1ea41de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -61,8 +61,8 @@
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.log.SessionTracker;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
index 46430af..1f37291 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
@@ -790,6 +790,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
     fun animateToGlanceableHub_affectsAlpha() =
         testScope.runTest {
             try {
@@ -809,6 +810,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
     fun animateToGlanceableHub_alphaResetOnCommunalNotShowing() =
         testScope.runTest {
             try {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index c5abd02..19e9838 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -106,6 +106,7 @@
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
         entry.setRow(row);
         when(row.getEntry()).thenReturn(entry);
+        when(row.getEntryLegacy()).thenReturn(entry);
         return entry;
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index eb95ddb..c58b4bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.res.R
@@ -48,6 +47,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -70,7 +70,7 @@
 @TestableLooper.RunWithLooper
 @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
 class OngoingCallControllerTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     private val mainExecutor = kosmos.fakeExecutor
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index a446313..7de56dd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -19,12 +19,11 @@
 import android.platform.test.annotations.DisableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_CHIPS_MODERNIZATION
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -33,7 +32,7 @@
 @RunWith(AndroidJUnit4::class)
 @DisableFlags(StatusBarChipsModernization.FLAG_NAME)
 class OngoingCallRepositoryTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val underTest = kosmos.ongoingCallRepository
 
     @Test
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 bab349a..84f1d5c 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
@@ -23,7 +23,6 @@
 import com.android.systemui.activity.data.repository.activityManagerRepository
 import com.android.systemui.activity.data.repository.fake
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
@@ -36,6 +35,7 @@
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
 import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -51,7 +51,7 @@
 @RunWith(AndroidJUnit4::class)
 @EnableChipsModernization
 class OngoingCallInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos().useUnconfinedTestDispatcher()
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
     private val underTest = kosmos.ongoingCallInteractor
 
     @Before
@@ -82,6 +82,7 @@
                 statusBarChipIconView = testIconView,
                 contentIntent = testIntent,
                 promotedContent = testPromotedContent,
+                isAppVisible = false,
             )
 
             // Verify model is InCall and has the correct icon, intent, and promoted content.
@@ -92,12 +93,12 @@
             assertThat(model.intent).isSameInstanceAs(testIntent)
             assertThat(model.notificationKey).isEqualTo(key)
             assertThat(model.promotedContent).isSameInstanceAs(testPromotedContent)
+            assertThat(model.isAppVisible).isFalse()
         }
 
     @Test
     fun ongoingCallNotification_setsAllFields_withAppVisible() =
         kosmos.runTest {
-            kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = true
             val latest by collectLastValue(underTest.ongoingCallState)
 
             // Set up notification with icon view and intent
@@ -112,17 +113,19 @@
                 statusBarChipIconView = testIconView,
                 contentIntent = testIntent,
                 promotedContent = testPromotedContent,
+                isAppVisible = true,
             )
 
-            // Verify model is InCallWithVisibleApp and has the correct icon, intent, and promoted
-            // content.
-            assertThat(latest).isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
-            val model = latest as OngoingCallModel.InCallWithVisibleApp
+            // Verify model is InCall with visible app and has the correct icon, intent, and
+            // promoted content.
+            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+            val model = latest as OngoingCallModel.InCall
             assertThat(model.startTimeMs).isEqualTo(startTimeMs)
             assertThat(model.notificationIconView).isSameInstanceAs(testIconView)
             assertThat(model.intent).isSameInstanceAs(testIntent)
             assertThat(model.notificationKey).isEqualTo(key)
             assertThat(model.promotedContent).isSameInstanceAs(testPromotedContent)
+            assertThat(model.isAppVisible).isTrue()
         }
 
     @Test
@@ -139,23 +142,23 @@
     @Test
     fun ongoingCallNotification_appVisibleInitially_emitsInCallWithVisibleApp() =
         kosmos.runTest {
-            kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = true
             val latest by collectLastValue(underTest.ongoingCallState)
 
-            addOngoingCallState(uid = UID)
+            addOngoingCallState(uid = UID, isAppVisible = true)
 
-            assertThat(latest).isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
+            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((latest as OngoingCallModel.InCall).isAppVisible).isTrue()
         }
 
     @Test
     fun ongoingCallNotification_appNotVisibleInitially_emitsInCall() =
         kosmos.runTest {
-            kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
             val latest by collectLastValue(underTest.ongoingCallState)
 
-            addOngoingCallState(uid = UID)
+            addOngoingCallState(uid = UID, isAppVisible = false)
 
             assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((latest as OngoingCallModel.InCall).isAppVisible).isFalse()
         }
 
     @Test
@@ -164,17 +167,19 @@
             val latest by collectLastValue(underTest.ongoingCallState)
 
             // Start with notification and app not visible
-            kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
-            addOngoingCallState(uid = UID)
+            addOngoingCallState(uid = UID, isAppVisible = false)
             assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((latest as OngoingCallModel.InCall).isAppVisible).isFalse()
 
             // App becomes visible
             kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
-            assertThat(latest).isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
+            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((latest as OngoingCallModel.InCall).isAppVisible).isTrue()
 
             // App becomes invisible again
             kosmos.activityManagerRepository.fake.setIsAppVisible(UID, false)
             assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((latest as OngoingCallModel.InCall).isAppVisible).isFalse()
         }
 
     @Test
@@ -238,18 +243,17 @@
                         .ongoingProcessRequiresStatusBarVisible
                 )
 
-            kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
-
-            addOngoingCallState(uid = UID)
+            addOngoingCallState(uid = UID, isAppVisible = false)
 
             assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((ongoingCallState as OngoingCallModel.InCall).isAppVisible).isFalse()
             assertThat(requiresStatusBarVisibleInRepository).isTrue()
             assertThat(requiresStatusBarVisibleInWindowController).isTrue()
 
             kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
 
-            assertThat(ongoingCallState)
-                .isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
+            assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((ongoingCallState as OngoingCallModel.InCall).isAppVisible).isTrue()
             assertThat(requiresStatusBarVisibleInRepository).isFalse()
             assertThat(requiresStatusBarVisibleInWindowController).isFalse()
         }
@@ -265,6 +269,7 @@
             addOngoingCallState()
 
             assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
+            assertThat((ongoingCallState as OngoingCallModel.InCall).isAppVisible).isFalse()
             verify(kosmos.swipeStatusBarAwayGestureHandler, never())
                 .addOnGestureDetectedCallback(any(), any())
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosTest.kt
index c89dc57..3368993 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,789 +26,701 @@
 import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
 import com.android.settingslib.mobile.TelephonyIcons
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS
+import com.android.systemui.flags.fake
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kairos.ActivatedKairosFixture
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosTestScope
+import com.android.systemui.kairos.MutableState
+import com.android.systemui.kairos.kairos
+import com.android.systemui.kairos.map
+import com.android.systemui.kairos.runKairosTest
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.log.table.logcatTableLogBuffer
 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.model.ResolvedNetworkType.CarrierMergedNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FIVE_G_OVERRIDE
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.THREE_G
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-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.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
 
+@OptIn(ExperimentalKairosApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MobileIconInteractorKairosTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
+    private val kosmos =
+        testKosmos().apply {
+            useUnconfinedTestDispatcher()
+            featureFlagsClassic.fake.apply { setDefault(FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS) }
+        }
 
-    private lateinit var underTest: MobileIconInteractorKairos
-    private val mobileMappingsProxy = FakeMobileMappingsProxy()
-    private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock())
+    private val Kosmos.tableLogBuffer by Fixture {
+        logcatTableLogBuffer(this, "MobileIconInteractorKairosTest")
+    }
 
-    private val connectionRepository =
-        FakeMobileConnectionRepository(
-            SUB_1_ID,
-            logcatTableLogBuffer(kosmos, "MobileIconInteractorTest"),
+    private var Kosmos.overrides: MobileIconCarrierIdOverrides by Fixture {
+        MobileIconCarrierIdOverridesImpl()
+    }
+
+    private val Kosmos.defaultSubscriptionHasDataEnabled by Fixture { MutableState(kairos, true) }
+
+    private val Kosmos.alwaysShowDataRatIcon by Fixture { MutableState(kairos, false) }
+
+    private val Kosmos.alwaysUseCdmaLevel by Fixture { MutableState(kairos, false) }
+
+    private val Kosmos.isSingleCarrier by Fixture { MutableState(kairos, true) }
+
+    private val Kosmos.mobileIsDefault by Fixture { MutableState(kairos, false) }
+
+    private val Kosmos.defaultMobileIconMapping by Fixture {
+        MutableState(kairos, fakeMobileIconsInteractor.TEST_MAPPING)
+    }
+
+    private val Kosmos.defaultMobileIconGroup by Fixture { MutableState(kairos, TelephonyIcons.G) }
+
+    private val Kosmos.isDefaultConnectionFailed by Fixture { MutableState(kairos, false) }
+
+    private val Kosmos.isForceHidden by Fixture { MutableState(kairos, false) }
+
+    private val Kosmos.underTest by ActivatedKairosFixture {
+        MobileIconInteractorKairosImpl(
+            defaultSubscriptionHasDataEnabled,
+            alwaysShowDataRatIcon,
+            alwaysUseCdmaLevel,
+            isSingleCarrier,
+            mobileIsDefault,
+            defaultMobileIconMapping,
+            defaultMobileIconGroup,
+            isDefaultConnectionFailed,
+            isForceHidden,
+            connectionRepository = connectionRepo,
+            context = context,
+            carrierIdOverrides = overrides,
         )
+    }
 
-    private val testDispatcher = UnconfinedTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
+    private val Kosmos.connectionRepo by Fixture {
+        FakeMobileConnectionRepositoryKairos(SUB_1_ID, kairos, tableLogBuffer).apply {
+            dataEnabled.setValue(true)
+            isInService.setValue(true)
+        }
+    }
 
-    @Before
-    fun setUp() {
-        underTest = createInteractor()
+    private fun runTest(block: suspend KairosTestScope.() -> Unit) =
+        kosmos.run { runKairosTest { block() } }
 
-        mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true
-        connectionRepository.isInService.value = true
+    @Test
+    fun gsm_usesGsmLevel() = runTest {
+        connectionRepo.isGsm.setValue(true)
+        connectionRepo.primaryLevel.setValue(GSM_LEVEL)
+        connectionRepo.cdmaLevel.setValue(CDMA_LEVEL)
+
+        val latest by underTest.signalLevelIcon.collectLastValue()
+
+        assertThat(latest?.level).isEqualTo(GSM_LEVEL)
     }
 
     @Test
-    fun gsm_usesGsmLevel() =
-        testScope.runTest {
-            connectionRepository.isGsm.value = true
-            connectionRepository.primaryLevel.value = GSM_LEVEL
-            connectionRepository.cdmaLevel.value = CDMA_LEVEL
+    fun gsm_alwaysShowCdmaTrue_stillUsesGsmLevel() = runTest {
+        connectionRepo.isGsm.setValue(true)
+        connectionRepo.primaryLevel.setValue(GSM_LEVEL)
+        connectionRepo.cdmaLevel.setValue(CDMA_LEVEL)
+        //            mobileIconsInteractor.alwaysUseCdmaLevel.setValue(true)
+        alwaysUseCdmaLevel.setValue(true)
 
-            var latest: Int? = null
-            val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            assertThat(latest).isEqualTo(GSM_LEVEL)
-
-            job.cancel()
-        }
+        assertThat(latest?.level).isEqualTo(GSM_LEVEL)
+    }
 
     @Test
-    fun gsm_alwaysShowCdmaTrue_stillUsesGsmLevel() =
-        testScope.runTest {
-            connectionRepository.isGsm.value = true
-            connectionRepository.primaryLevel.value = GSM_LEVEL
-            connectionRepository.cdmaLevel.value = CDMA_LEVEL
-            mobileIconsInteractor.alwaysUseCdmaLevel.value = true
+    fun notGsm_level_default_unknown() = runTest {
+        connectionRepo.isGsm.setValue(false)
 
-            var latest: Int? = null
-            val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            assertThat(latest).isEqualTo(GSM_LEVEL)
-
-            job.cancel()
-        }
+        assertThat(latest?.level).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
+    }
 
     @Test
-    fun notGsm_level_default_unknown() =
-        testScope.runTest {
-            connectionRepository.isGsm.value = false
+    fun notGsm_alwaysShowCdmaTrue_usesCdmaLevel() = runTest {
+        connectionRepo.isGsm.setValue(false)
+        connectionRepo.primaryLevel.setValue(GSM_LEVEL)
+        connectionRepo.cdmaLevel.setValue(CDMA_LEVEL)
+        //            mobileIconsInteractor.alwaysUseCdmaLevel.setValue(true)
+        alwaysUseCdmaLevel.setValue(true)
 
-            var latest: Int? = null
-            val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            assertThat(latest).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
-            job.cancel()
-        }
+        assertThat(latest?.level).isEqualTo(CDMA_LEVEL)
+    }
 
     @Test
-    fun notGsm_alwaysShowCdmaTrue_usesCdmaLevel() =
-        testScope.runTest {
-            connectionRepository.isGsm.value = false
-            connectionRepository.primaryLevel.value = GSM_LEVEL
-            connectionRepository.cdmaLevel.value = CDMA_LEVEL
-            mobileIconsInteractor.alwaysUseCdmaLevel.value = true
+    fun notGsm_alwaysShowCdmaFalse_usesPrimaryLevel() = runTest {
+        connectionRepo.isGsm.setValue(false)
+        connectionRepo.primaryLevel.setValue(GSM_LEVEL)
+        connectionRepo.cdmaLevel.setValue(CDMA_LEVEL)
+        //            mobileIconsInteractor.alwaysUseCdmaLevel.setValue(false)
+        alwaysUseCdmaLevel.setValue(false)
 
-            var latest: Int? = null
-            val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            assertThat(latest).isEqualTo(CDMA_LEVEL)
-
-            job.cancel()
-        }
+        assertThat(latest?.level).isEqualTo(GSM_LEVEL)
+    }
 
     @Test
-    fun notGsm_alwaysShowCdmaFalse_usesPrimaryLevel() =
-        testScope.runTest {
-            connectionRepository.isGsm.value = false
-            connectionRepository.primaryLevel.value = GSM_LEVEL
-            connectionRepository.cdmaLevel.value = CDMA_LEVEL
-            mobileIconsInteractor.alwaysUseCdmaLevel.value = false
+    fun numberOfLevels_comesFromRepo_whenApplicable() = runTest {
+        val latest by
+            underTest.signalLevelIcon
+                .map { (it as? SignalIconModel.Cellular)?.numberOfLevels }
+                .collectLastValue()
 
-            var latest: Int? = null
-            val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
+        connectionRepo.numberOfLevels.setValue(5)
+        assertThat(latest).isEqualTo(5)
 
-            assertThat(latest).isEqualTo(GSM_LEVEL)
-
-            job.cancel()
-        }
+        connectionRepo.numberOfLevels.setValue(4)
+        assertThat(latest).isEqualTo(4)
+    }
 
     @Test
-    fun numberOfLevels_comesFromRepo_whenApplicable() =
-        testScope.runTest {
-            var latest: Int? = null
-            val job =
-                underTest.signalLevelIcon
-                    .onEach { latest = (it as? SignalIconModel.Cellular)?.numberOfLevels }
-                    .launchIn(this)
+    fun inflateSignalStrength_arbitrarilyAddsOneToTheReportedLevel() = runTest {
+        connectionRepo.inflateSignalStrength.setValue(false)
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            connectionRepository.numberOfLevels.value = 5
-            assertThat(latest).isEqualTo(5)
+        connectionRepo.primaryLevel.setValue(4)
+        assertThat(latest!!.level).isEqualTo(4)
 
-            connectionRepository.numberOfLevels.value = 4
-            assertThat(latest).isEqualTo(4)
+        connectionRepo.inflateSignalStrength.setValue(true)
+        connectionRepo.primaryLevel.setValue(4)
 
-            job.cancel()
-        }
+        // when INFLATE_SIGNAL_STRENGTH is true, we add 1 to the reported signal level
+        assertThat(latest!!.level).isEqualTo(5)
+    }
 
     @Test
-    fun inflateSignalStrength_arbitrarilyAddsOneToTheReportedLevel() =
-        testScope.runTest {
-            connectionRepository.inflateSignalStrength.value = false
-            val latest by collectLastValue(underTest.signalLevelIcon)
+    fun networkSlice_configOn_hasPrioritizedCaps_showsSlice() = runTest {
+        connectionRepo.allowNetworkSliceIndicator.setValue(true)
+        val latest by underTest.showSliceAttribution.collectLastValue()
 
-            connectionRepository.primaryLevel.value = 4
-            assertThat(latest!!.level).isEqualTo(4)
+        connectionRepo.hasPrioritizedNetworkCapabilities.setValue(true)
 
-            connectionRepository.inflateSignalStrength.value = true
-            connectionRepository.primaryLevel.value = 4
-
-            // when INFLATE_SIGNAL_STRENGTH is true, we add 1 to the reported signal level
-            assertThat(latest!!.level).isEqualTo(5)
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun networkSlice_configOn_hasPrioritizedCaps_showsSlice() =
-        testScope.runTest {
-            connectionRepository.allowNetworkSliceIndicator.value = true
-            val latest by collectLastValue(underTest.showSliceAttribution)
+    fun networkSlice_configOn_noPrioritizedCaps_noSlice() = runTest {
+        connectionRepo.allowNetworkSliceIndicator.setValue(true)
+        val latest by underTest.showSliceAttribution.collectLastValue()
 
-            connectionRepository.hasPrioritizedNetworkCapabilities.value = true
+        connectionRepo.hasPrioritizedNetworkCapabilities.setValue(false)
 
-            assertThat(latest).isTrue()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun networkSlice_configOn_noPrioritizedCaps_noSlice() =
-        testScope.runTest {
-            connectionRepository.allowNetworkSliceIndicator.value = true
-            val latest by collectLastValue(underTest.showSliceAttribution)
+    fun networkSlice_configOff_hasPrioritizedCaps_noSlice() = runTest {
+        connectionRepo.allowNetworkSliceIndicator.setValue(false)
+        val latest by underTest.showSliceAttribution.collectLastValue()
 
-            connectionRepository.hasPrioritizedNetworkCapabilities.value = false
+        connectionRepo.hasPrioritizedNetworkCapabilities.setValue(true)
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun networkSlice_configOff_hasPrioritizedCaps_noSlice() =
-        testScope.runTest {
-            connectionRepository.allowNetworkSliceIndicator.value = false
-            val latest by collectLastValue(underTest.showSliceAttribution)
+    fun networkSlice_configOff_noPrioritizedCaps_noSlice() = runTest {
+        connectionRepo.allowNetworkSliceIndicator.setValue(false)
+        val latest by underTest.showSliceAttribution.collectLastValue()
 
-            connectionRepository.hasPrioritizedNetworkCapabilities.value = true
+        connectionRepo.hasPrioritizedNetworkCapabilities.setValue(false)
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun networkSlice_configOff_noPrioritizedCaps_noSlice() =
-        testScope.runTest {
-            connectionRepository.allowNetworkSliceIndicator.value = false
-            val latest by collectLastValue(underTest.showSliceAttribution)
+    fun iconGroup_three_g() = runTest {
+        connectionRepo.resolvedNetworkType.setValue(
+            DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
+        )
 
-            connectionRepository.hasPrioritizedNetworkCapabilities.value = false
+        val latest by underTest.networkTypeIconGroup.collectLastValue()
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G))
+    }
 
     @Test
-    fun iconGroup_three_g() =
-        testScope.runTest {
-            connectionRepository.resolvedNetworkType.value =
-                DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
+    fun iconGroup_updates_on_change() = runTest {
+        connectionRepo.resolvedNetworkType.setValue(
+            DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
+        )
 
-            var latest: NetworkTypeIconModel? = null
-            val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
+        val latest by underTest.networkTypeIconGroup.collectLastValue()
 
-            assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G))
+        connectionRepo.resolvedNetworkType.setValue(
+            DefaultNetworkType(mobileMappingsProxy.toIconKey(FOUR_G))
+        )
 
-            job.cancel()
-        }
+        assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.FOUR_G))
+    }
 
     @Test
-    fun iconGroup_updates_on_change() =
-        testScope.runTest {
-            connectionRepository.resolvedNetworkType.value =
-                DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
+    fun iconGroup_5g_override_type() = runTest {
+        connectionRepo.resolvedNetworkType.setValue(
+            OverrideNetworkType(mobileMappingsProxy.toIconKeyOverride(FIVE_G_OVERRIDE))
+        )
 
-            var latest: NetworkTypeIconModel? = null
-            val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
+        val latest by underTest.networkTypeIconGroup.collectLastValue()
 
-            connectionRepository.resolvedNetworkType.value =
-                DefaultNetworkType(mobileMappingsProxy.toIconKey(FOUR_G))
-
-            assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.FOUR_G))
-
-            job.cancel()
-        }
+        assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.NR_5G))
+    }
 
     @Test
-    fun iconGroup_5g_override_type() =
-        testScope.runTest {
-            connectionRepository.resolvedNetworkType.value =
-                OverrideNetworkType(mobileMappingsProxy.toIconKeyOverride(FIVE_G_OVERRIDE))
+    fun iconGroup_default_if_no_lookup() = runTest {
+        connectionRepo.resolvedNetworkType.setValue(
+            DefaultNetworkType(mobileMappingsProxy.toIconKey(NETWORK_TYPE_UNKNOWN))
+        )
 
-            var latest: NetworkTypeIconModel? = null
-            val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
+        val latest by underTest.networkTypeIconGroup.collectLastValue()
 
-            assertThat(latest).isEqualTo(NetworkTypeIconModel.DefaultIcon(TelephonyIcons.NR_5G))
-
-            job.cancel()
-        }
+        assertThat(latest)
+            .isEqualTo(NetworkTypeIconModel.DefaultIcon(FakeMobileIconsInteractor.DEFAULT_ICON))
+    }
 
     @Test
-    fun iconGroup_default_if_no_lookup() =
-        testScope.runTest {
-            connectionRepository.resolvedNetworkType.value =
-                DefaultNetworkType(mobileMappingsProxy.toIconKey(NETWORK_TYPE_UNKNOWN))
+    fun iconGroup_carrierMerged_usesOverride() = runTest {
+        connectionRepo.resolvedNetworkType.setValue(CarrierMergedNetworkType)
 
-            var latest: NetworkTypeIconModel? = null
-            val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
+        val latest by underTest.networkTypeIconGroup.collectLastValue()
 
-            assertThat(latest)
-                .isEqualTo(NetworkTypeIconModel.DefaultIcon(FakeMobileIconsInteractor.DEFAULT_ICON))
-
-            job.cancel()
-        }
+        assertThat(latest)
+            .isEqualTo(NetworkTypeIconModel.DefaultIcon(CarrierMergedNetworkType.iconGroupOverride))
+    }
 
     @Test
-    fun iconGroup_carrierMerged_usesOverride() =
-        testScope.runTest {
-            connectionRepository.resolvedNetworkType.value = CarrierMergedNetworkType
+    fun overrideIcon_usesCarrierIdOverride() = runTest {
+        overrides =
+            mock<MobileIconCarrierIdOverrides> {
+                on { carrierIdEntryExists(anyInt()) } doReturn true
+                on { getOverrideFor(anyInt(), anyString(), any()) } doReturn 1234
+            }
 
-            var latest: NetworkTypeIconModel? = null
-            val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
+        connectionRepo.resolvedNetworkType.setValue(
+            DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
+        )
 
-            assertThat(latest)
-                .isEqualTo(
-                    NetworkTypeIconModel.DefaultIcon(CarrierMergedNetworkType.iconGroupOverride)
-                )
+        val latest by underTest.networkTypeIconGroup.collectLastValue()
 
-            job.cancel()
-        }
+        assertThat(latest)
+            .isEqualTo(NetworkTypeIconModel.OverriddenIcon(TelephonyIcons.THREE_G, 1234))
+    }
 
     @Test
-    fun overrideIcon_usesCarrierIdOverride() =
-        testScope.runTest {
-            val overrides =
-                mock<MobileIconCarrierIdOverrides>().also {
-                    whenever(it.carrierIdEntryExists(anyInt())).thenReturn(true)
-                    whenever(it.getOverrideFor(anyInt(), anyString(), any())).thenReturn(1234)
-                }
+    fun alwaysShowDataRatIcon_matchesParent() = runTest {
+        val latest by underTest.alwaysShowDataRatIcon.collectLastValue()
 
-            underTest = createInteractor(overrides)
+        //            mobileIconsInteractor.alwaysShowDataRatIcon.setValue(true)
+        alwaysShowDataRatIcon.setValue(true)
 
-            connectionRepository.resolvedNetworkType.value =
-                DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
+        assertThat(latest).isTrue()
 
-            var latest: NetworkTypeIconModel? = null
-            val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
+        //            mobileIconsInteractor.alwaysShowDataRatIcon.setValue(false)
+        alwaysShowDataRatIcon.setValue(false)
 
-            assertThat(latest)
-                .isEqualTo(NetworkTypeIconModel.OverriddenIcon(TelephonyIcons.THREE_G, 1234))
-
-            job.cancel()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun alwaysShowDataRatIcon_matchesParent() =
-        testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
+    fun dataState_connected() = runTest {
+        val latest by underTest.isDataConnected.collectLastValue()
 
-            mobileIconsInteractor.alwaysShowDataRatIcon.value = true
-            assertThat(latest).isTrue()
+        connectionRepo.dataConnectionState.setValue(DataConnectionState.Connected)
 
-            mobileIconsInteractor.alwaysShowDataRatIcon.value = false
-            assertThat(latest).isFalse()
-
-            job.cancel()
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun dataState_connected() =
-        testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isDataConnected.onEach { latest = it }.launchIn(this)
+    fun dataState_notConnected() = runTest {
+        val latest by underTest.isDataConnected.collectLastValue()
 
-            connectionRepository.dataConnectionState.value = DataConnectionState.Connected
+        connectionRepo.dataConnectionState.setValue(DataConnectionState.Disconnected)
 
-            assertThat(latest).isTrue()
-
-            job.cancel()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun dataState_notConnected() =
-        testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isDataConnected.onEach { latest = it }.launchIn(this)
+    fun isInService_usesRepositoryValue() = runTest {
+        val latest by underTest.isInService.collectLastValue()
 
-            connectionRepository.dataConnectionState.value = DataConnectionState.Disconnected
+        connectionRepo.isInService.setValue(true)
 
-            assertThat(latest).isFalse()
+        assertThat(latest).isTrue()
 
-            job.cancel()
-        }
+        connectionRepo.isInService.setValue(false)
+
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun isInService_usesRepositoryValue() =
-        testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isInService.onEach { latest = it }.launchIn(this)
+    fun roaming_isGsm_usesConnectionModel() = runTest {
+        val latest by underTest.isRoaming.collectLastValue()
 
-            connectionRepository.isInService.value = true
+        connectionRepo.cdmaRoaming.setValue(true)
+        connectionRepo.isGsm.setValue(true)
+        connectionRepo.isRoaming.setValue(false)
 
-            assertThat(latest).isTrue()
+        assertThat(latest).isFalse()
 
-            connectionRepository.isInService.value = false
+        connectionRepo.isRoaming.setValue(true)
 
-            assertThat(latest).isFalse()
-
-            job.cancel()
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun roaming_isGsm_usesConnectionModel() =
-        testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
+    fun roaming_isCdma_usesCdmaRoamingBit() = runTest {
+        val latest by underTest.isRoaming.collectLastValue()
 
-            connectionRepository.cdmaRoaming.value = true
-            connectionRepository.isGsm.value = true
-            connectionRepository.isRoaming.value = false
+        connectionRepo.cdmaRoaming.setValue(false)
+        connectionRepo.isGsm.setValue(false)
+        connectionRepo.isRoaming.setValue(true)
 
-            assertThat(latest).isFalse()
+        assertThat(latest).isFalse()
 
-            connectionRepository.isRoaming.value = true
+        connectionRepo.cdmaRoaming.setValue(true)
+        connectionRepo.isGsm.setValue(false)
+        connectionRepo.isRoaming.setValue(false)
 
-            assertThat(latest).isTrue()
-
-            job.cancel()
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun roaming_isCdma_usesCdmaRoamingBit() =
-        testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
+    fun roaming_falseWhileCarrierNetworkChangeActive() = runTest {
+        val latest by underTest.isRoaming.collectLastValue()
 
-            connectionRepository.cdmaRoaming.value = false
-            connectionRepository.isGsm.value = false
-            connectionRepository.isRoaming.value = true
+        connectionRepo.cdmaRoaming.setValue(true)
+        connectionRepo.isGsm.setValue(false)
+        connectionRepo.isRoaming.setValue(true)
+        connectionRepo.carrierNetworkChangeActive.setValue(true)
 
-            assertThat(latest).isFalse()
+        assertThat(latest).isFalse()
 
-            connectionRepository.cdmaRoaming.value = true
-            connectionRepository.isGsm.value = false
-            connectionRepository.isRoaming.value = false
+        connectionRepo.cdmaRoaming.setValue(true)
+        connectionRepo.isGsm.setValue(true)
 
-            assertThat(latest).isTrue()
-
-            job.cancel()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun roaming_falseWhileCarrierNetworkChangeActive() =
-        testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
+    fun networkName_usesOperatorAlphaShortWhenNonNullAndRepoIsDefault() = runTest {
+        val latest by underTest.networkName.collectLastValue()
 
-            connectionRepository.cdmaRoaming.value = true
-            connectionRepository.isGsm.value = false
-            connectionRepository.isRoaming.value = true
-            connectionRepository.carrierNetworkChangeActive.value = true
+        val testOperatorName = "operatorAlphaShort"
 
-            assertThat(latest).isFalse()
+        // Default network name, operator name is non-null, uses the operator name
+        connectionRepo.networkName.setValue(DEFAULT_NAME_MODEL)
+        connectionRepo.operatorAlphaShort.setValue(testOperatorName)
 
-            connectionRepository.cdmaRoaming.value = true
-            connectionRepository.isGsm.value = true
+        assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived(testOperatorName))
 
-            assertThat(latest).isFalse()
+        // Default network name, operator name is null, uses the default
+        connectionRepo.operatorAlphaShort.setValue(null)
 
-            job.cancel()
-        }
+        assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
+
+        // Derived network name, operator name non-null, uses the derived name
+        connectionRepo.networkName.setValue(DERIVED_NAME_MODEL)
+        connectionRepo.operatorAlphaShort.setValue(testOperatorName)
+
+        assertThat(latest).isEqualTo(DERIVED_NAME_MODEL)
+    }
 
     @Test
-    fun networkName_usesOperatorAlphaShortWhenNonNullAndRepoIsDefault() =
-        testScope.runTest {
-            var latest: NetworkNameModel? = null
-            val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+    fun networkNameForSubId_usesOperatorAlphaShortWhenNonNullAndRepoIsDefault() = runTest {
+        val latest by underTest.carrierName.collectLastValue()
 
-            val testOperatorName = "operatorAlphaShort"
+        val testOperatorName = "operatorAlphaShort"
 
-            // Default network name, operator name is non-null, uses the operator name
-            connectionRepository.networkName.value = DEFAULT_NAME_MODEL
-            connectionRepository.operatorAlphaShort.value = testOperatorName
+        // Default network name, operator name is non-null, uses the operator name
+        connectionRepo.carrierName.setValue(DEFAULT_NAME_MODEL)
+        connectionRepo.operatorAlphaShort.setValue(testOperatorName)
 
-            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived(testOperatorName))
+        assertThat(latest).isEqualTo(testOperatorName)
 
-            // Default network name, operator name is null, uses the default
-            connectionRepository.operatorAlphaShort.value = null
+        // Default network name, operator name is null, uses the default
+        connectionRepo.operatorAlphaShort.setValue(null)
 
-            assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
+        assertThat(latest).isEqualTo(DEFAULT_NAME)
 
-            // Derived network name, operator name non-null, uses the derived name
-            connectionRepository.networkName.value = DERIVED_NAME_MODEL
-            connectionRepository.operatorAlphaShort.value = testOperatorName
+        // Derived network name, operator name non-null, uses the derived name
+        connectionRepo.carrierName.setValue(NetworkNameModel.SubscriptionDerived(DERIVED_NAME))
+        connectionRepo.operatorAlphaShort.setValue(testOperatorName)
 
-            assertThat(latest).isEqualTo(DERIVED_NAME_MODEL)
-
-            job.cancel()
-        }
+        assertThat(latest).isEqualTo(DERIVED_NAME)
+    }
 
     @Test
-    fun networkNameForSubId_usesOperatorAlphaShortWhenNonNullAndRepoIsDefault() =
-        testScope.runTest {
-            var latest: String? = null
-            val job = underTest.carrierName.onEach { latest = it }.launchIn(this)
+    fun isSingleCarrier_matchesParent() = runTest {
+        val latest by underTest.isSingleCarrier.collectLastValue()
 
-            val testOperatorName = "operatorAlphaShort"
+        //            mobileIconsInteractor.isSingleCarrier.setValue(true)
+        isSingleCarrier.setValue(true)
+        assertThat(latest).isTrue()
 
-            // Default network name, operator name is non-null, uses the operator name
-            connectionRepository.carrierName.value = DEFAULT_NAME_MODEL
-            connectionRepository.operatorAlphaShort.value = testOperatorName
-
-            assertThat(latest).isEqualTo(testOperatorName)
-
-            // Default network name, operator name is null, uses the default
-            connectionRepository.operatorAlphaShort.value = null
-
-            assertThat(latest).isEqualTo(DEFAULT_NAME)
-
-            // Derived network name, operator name non-null, uses the derived name
-            connectionRepository.carrierName.value =
-                NetworkNameModel.SubscriptionDerived(DERIVED_NAME)
-            connectionRepository.operatorAlphaShort.value = testOperatorName
-
-            assertThat(latest).isEqualTo(DERIVED_NAME)
-
-            job.cancel()
-        }
+        //            mobileIconsInteractor.isSingleCarrier.setValue(false)
+        isSingleCarrier.setValue(false)
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun isSingleCarrier_matchesParent() =
-        testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+    fun isForceHidden_matchesParent() = runTest {
+        val latest by underTest.isForceHidden.collectLastValue()
 
-            mobileIconsInteractor.isSingleCarrier.value = true
-            assertThat(latest).isTrue()
+        //            mobileIconsInteractor.isForceHidden.setValue(true)
+        isForceHidden.setValue(true)
+        assertThat(latest).isTrue()
 
-            mobileIconsInteractor.isSingleCarrier.value = false
-            assertThat(latest).isFalse()
-
-            job.cancel()
-        }
+        //            mobileIconsInteractor.isForceHidden.setValue(false)
+        isForceHidden.setValue(false)
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun isForceHidden_matchesParent() =
-        testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+    fun isAllowedDuringAirplaneMode_matchesRepo() = runTest {
+        val latest by underTest.isAllowedDuringAirplaneMode.collectLastValue()
 
-            mobileIconsInteractor.isForceHidden.value = true
-            assertThat(latest).isTrue()
+        connectionRepo.isAllowedDuringAirplaneMode.setValue(true)
+        assertThat(latest).isTrue()
 
-            mobileIconsInteractor.isForceHidden.value = false
-            assertThat(latest).isFalse()
-
-            job.cancel()
-        }
+        connectionRepo.isAllowedDuringAirplaneMode.setValue(false)
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun isAllowedDuringAirplaneMode_matchesRepo() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode)
+    fun cellBasedIconId_correctLevel_notCutout() = runTest {
+        connectionRepo.isNonTerrestrial.setValue(false)
+        connectionRepo.isInService.setValue(true)
+        connectionRepo.primaryLevel.setValue(1)
+        connectionRepo.dataEnabled.setValue(true)
+        connectionRepo.isNonTerrestrial.setValue(false)
 
-            connectionRepository.isAllowedDuringAirplaneMode.value = true
-            assertThat(latest).isTrue()
+        val latest by
+            underTest.signalLevelIcon.map { it as? SignalIconModel.Cellular }.collectLastValue()
 
-            connectionRepository.isAllowedDuringAirplaneMode.value = false
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest?.level).isEqualTo(1)
+
+        // TODO: need to provision MobileIconsInteractorKairos#isDefaultConnectionFailed +
+        // defaultSubscriptionHasDataEnabled?
+        assertThat(latest?.showExclamationMark).isEqualTo(false)
+    }
 
     @Test
-    fun cellBasedIconId_correctLevel_notCutout() =
-        testScope.runTest {
-            connectionRepository.isNonTerrestrial.value = false
-            connectionRepository.isInService.value = true
-            connectionRepository.primaryLevel.value = 1
-            connectionRepository.setDataEnabled(false)
-            connectionRepository.isNonTerrestrial.value = false
+    fun icon_usesLevelFromInteractor() = runTest {
+        connectionRepo.isNonTerrestrial.setValue(false)
+        connectionRepo.isInService.setValue(true)
 
-            var latest: SignalIconModel.Cellular? = null
-            val job =
-                underTest.signalLevelIcon
-                    .onEach { latest = it as? SignalIconModel.Cellular }
-                    .launchIn(this)
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            assertThat(latest?.level).isEqualTo(1)
-            assertThat(latest?.showExclamationMark).isFalse()
+        connectionRepo.primaryLevel.setValue(3)
+        assertThat(latest!!.level).isEqualTo(3)
 
-            job.cancel()
-        }
+        connectionRepo.primaryLevel.setValue(1)
+        assertThat(latest!!.level).isEqualTo(1)
+    }
 
     @Test
-    fun icon_usesLevelFromInteractor() =
-        testScope.runTest {
-            connectionRepository.isNonTerrestrial.value = false
-            connectionRepository.isInService.value = true
+    fun cellBasedIcon_usesNumberOfLevelsFromInteractor() = runTest {
+        connectionRepo.isNonTerrestrial.setValue(false)
 
-            var latest: SignalIconModel? = null
-            val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+        val latest by
+            underTest.signalLevelIcon.map { it as? SignalIconModel.Cellular }.collectLastValue()
 
-            connectionRepository.primaryLevel.value = 3
-            assertThat(latest!!.level).isEqualTo(3)
+        connectionRepo.numberOfLevels.setValue(5)
+        assertThat(latest!!.numberOfLevels).isEqualTo(5)
 
-            connectionRepository.primaryLevel.value = 1
-            assertThat(latest!!.level).isEqualTo(1)
-
-            job.cancel()
-        }
+        connectionRepo.numberOfLevels.setValue(2)
+        assertThat(latest!!.numberOfLevels).isEqualTo(2)
+    }
 
     @Test
-    fun cellBasedIcon_usesNumberOfLevelsFromInteractor() =
-        testScope.runTest {
-            connectionRepository.isNonTerrestrial.value = false
+    fun cellBasedIcon_defaultDataDisabled_showExclamationTrue() = runTest {
+        connectionRepo.isNonTerrestrial.setValue(false)
+        connectionRepo.dataEnabled.setValue(false)
+        defaultSubscriptionHasDataEnabled.setValue(false)
 
-            var latest: SignalIconModel.Cellular? = null
-            val job =
-                underTest.signalLevelIcon
-                    .onEach { latest = it as? SignalIconModel.Cellular }
-                    .launchIn(this)
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            connectionRepository.numberOfLevels.value = 5
-            assertThat(latest!!.numberOfLevels).isEqualTo(5)
-
-            connectionRepository.numberOfLevels.value = 2
-            assertThat(latest!!.numberOfLevels).isEqualTo(2)
-
-            job.cancel()
-        }
+        assertThat((latest!! as SignalIconModel.Cellular).showExclamationMark).isTrue()
+    }
 
     @Test
-    fun cellBasedIcon_defaultDataDisabled_showExclamationTrue() =
-        testScope.runTest {
-            connectionRepository.isNonTerrestrial.value = false
-            mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = false
+    fun cellBasedIcon_defaultConnectionFailed_showExclamationTrue() = runTest {
+        connectionRepo.isNonTerrestrial.setValue(false)
+        //            mobileIconsInteractor.isDefaultConnectionFailed.setValue(true)
+        isDefaultConnectionFailed.setValue(true)
 
-            var latest: SignalIconModel.Cellular? = null
-            val job =
-                underTest.signalLevelIcon
-                    .onEach { latest = it as? SignalIconModel.Cellular }
-                    .launchIn(this)
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            assertThat(latest!!.showExclamationMark).isTrue()
-
-            job.cancel()
-        }
+        assertThat((latest!! as SignalIconModel.Cellular).showExclamationMark).isTrue()
+    }
 
     @Test
-    fun cellBasedIcon_defaultConnectionFailed_showExclamationTrue() =
-        testScope.runTest {
-            connectionRepository.isNonTerrestrial.value = false
-            mobileIconsInteractor.isDefaultConnectionFailed.value = true
+    fun cellBasedIcon_enabledAndNotFailed_showExclamationFalse() = runTest {
+        connectionRepo.isNonTerrestrial.setValue(false)
+        connectionRepo.isInService.setValue(true)
+        connectionRepo.dataEnabled.setValue(true)
+        //            mobileIconsInteractor.isDefaultConnectionFailed.setValue(false)
+        isDefaultConnectionFailed.setValue(false)
 
-            var latest: SignalIconModel.Cellular? = null
-            val job =
-                underTest.signalLevelIcon
-                    .onEach { latest = it as? SignalIconModel.Cellular }
-                    .launchIn(this)
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            assertThat(latest!!.showExclamationMark).isTrue()
-
-            job.cancel()
-        }
+        assertThat((latest!! as SignalIconModel.Cellular).showExclamationMark).isFalse()
+    }
 
     @Test
-    fun cellBasedIcon_enabledAndNotFailed_showExclamationFalse() =
-        testScope.runTest {
-            connectionRepository.isNonTerrestrial.value = false
-            connectionRepository.isInService.value = true
-            mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true
-            mobileIconsInteractor.isDefaultConnectionFailed.value = false
+    fun cellBasedIcon_usesEmptyState_whenNotInService() = runTest {
+        val latest by
+            underTest.signalLevelIcon.map { it as SignalIconModel.Cellular }.collectLastValue()
 
-            var latest: SignalIconModel.Cellular? = null
-            val job =
-                underTest.signalLevelIcon
-                    .onEach { latest = it as? SignalIconModel.Cellular }
-                    .launchIn(this)
+        connectionRepo.isNonTerrestrial.setValue(false)
+        connectionRepo.isInService.setValue(false)
 
-            assertThat(latest!!.showExclamationMark).isFalse()
+        assertThat(latest?.level).isEqualTo(0)
+        assertThat(latest?.showExclamationMark).isTrue()
 
-            job.cancel()
-        }
+        // Changing the level doesn't overwrite the disabled state
+        connectionRepo.primaryLevel.setValue(2)
+        assertThat(latest?.level).isEqualTo(0)
+        assertThat(latest?.showExclamationMark).isTrue()
+
+        // Once back in service, the regular icon appears
+        connectionRepo.isInService.setValue(true)
+        assertThat(latest?.level).isEqualTo(2)
+        assertThat(latest?.showExclamationMark).isFalse()
+    }
 
     @Test
-    fun cellBasedIcon_usesEmptyState_whenNotInService() =
-        testScope.runTest {
-            var latest: SignalIconModel.Cellular? = null
-            val job =
-                underTest.signalLevelIcon
-                    .onEach { latest = it as? SignalIconModel.Cellular }
-                    .launchIn(this)
+    fun cellBasedIcon_usesCarrierNetworkState_whenInCarrierNetworkChangeMode() = runTest {
+        val latest by
+            underTest.signalLevelIcon.map { it as SignalIconModel.Cellular }.collectLastValue()
 
-            connectionRepository.isNonTerrestrial.value = false
-            connectionRepository.isInService.value = false
+        connectionRepo.isNonTerrestrial.setValue(false)
+        connectionRepo.isInService.setValue(true)
+        connectionRepo.carrierNetworkChangeActive.setValue(true)
+        connectionRepo.primaryLevel.setValue(1)
+        connectionRepo.cdmaLevel.setValue(1)
 
-            assertThat(latest?.level).isEqualTo(0)
-            assertThat(latest?.showExclamationMark).isTrue()
+        assertThat(latest!!.level).isEqualTo(1)
+        assertThat(latest!!.carrierNetworkChange).isTrue()
 
-            // Changing the level doesn't overwrite the disabled state
-            connectionRepository.primaryLevel.value = 2
-            assertThat(latest?.level).isEqualTo(0)
-            assertThat(latest?.showExclamationMark).isTrue()
+        // SignalIconModel respects the current level
+        connectionRepo.primaryLevel.setValue(2)
 
-            // Once back in service, the regular icon appears
-            connectionRepository.isInService.value = true
-            assertThat(latest?.level).isEqualTo(2)
-            assertThat(latest?.showExclamationMark).isFalse()
-
-            job.cancel()
-        }
+        assertThat(latest!!.level).isEqualTo(2)
+        assertThat(latest!!.carrierNetworkChange).isTrue()
+    }
 
     @Test
-    fun cellBasedIcon_usesCarrierNetworkState_whenInCarrierNetworkChangeMode() =
-        testScope.runTest {
-            var latest: SignalIconModel.Cellular? = null
-            val job =
-                underTest.signalLevelIcon
-                    .onEach { latest = it as? SignalIconModel.Cellular? }
-                    .launchIn(this)
+    fun satBasedIcon_isUsedWhenNonTerrestrial() = runTest {
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            connectionRepository.isNonTerrestrial.value = false
-            connectionRepository.isInService.value = true
-            connectionRepository.carrierNetworkChangeActive.value = true
-            connectionRepository.primaryLevel.value = 1
-            connectionRepository.cdmaLevel.value = 1
+        // Start off using cellular
+        assertThat(latest).isInstanceOf(SignalIconModel.Cellular::class.java)
 
-            assertThat(latest!!.level).isEqualTo(1)
-            assertThat(latest!!.carrierNetworkChange).isTrue()
+        connectionRepo.isNonTerrestrial.setValue(true)
 
-            // SignalIconModel respects the current level
-            connectionRepository.primaryLevel.value = 2
-
-            assertThat(latest!!.level).isEqualTo(2)
-            assertThat(latest!!.carrierNetworkChange).isTrue()
-
-            job.cancel()
-        }
-
-    @Test
-    fun satBasedIcon_isUsedWhenNonTerrestrial() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.signalLevelIcon)
-
-            // Start off using cellular
-            assertThat(latest).isInstanceOf(SignalIconModel.Cellular::class.java)
-
-            connectionRepository.isNonTerrestrial.value = true
-
-            assertThat(latest).isInstanceOf(SignalIconModel.Satellite::class.java)
-        }
+        assertThat(latest).isInstanceOf(SignalIconModel.Satellite::class.java)
+    }
 
     @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
     @Test
     // See b/346904529 for more context
-    fun satBasedIcon_doesNotInflateSignalStrength_flagOff() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.signalLevelIcon)
+    fun satBasedIcon_doesNotInflateSignalStrength_flagOff() = runTest {
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            // GIVEN a satellite connection
-            connectionRepository.isNonTerrestrial.value = true
-            // GIVEN this carrier has set INFLATE_SIGNAL_STRENGTH
-            connectionRepository.inflateSignalStrength.value = true
+        // GIVEN a satellite connection
+        connectionRepo.isNonTerrestrial.setValue(true)
+        // GIVEN this carrier has set INFLATE_SIGNAL_STRENGTH
+        connectionRepo.inflateSignalStrength.setValue(true)
 
-            connectionRepository.primaryLevel.value = 4
-            assertThat(latest!!.level).isEqualTo(4)
+        connectionRepo.primaryLevel.setValue(4)
+        assertThat(latest!!.level).isEqualTo(4)
 
-            connectionRepository.inflateSignalStrength.value = true
-            connectionRepository.primaryLevel.value = 4
+        connectionRepo.inflateSignalStrength.setValue(true)
+        connectionRepo.primaryLevel.setValue(4)
 
-            // Icon level is unaffected
-            assertThat(latest!!.level).isEqualTo(4)
-        }
+        // Icon level is unaffected
+        assertThat(latest!!.level).isEqualTo(4)
+    }
 
     @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
     @Test
     // See b/346904529 for more context
-    fun satBasedIcon_doesNotInflateSignalStrength_flagOn() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.signalLevelIcon)
+    fun satBasedIcon_doesNotInflateSignalStrength_flagOn() = runTest {
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            // GIVEN a satellite connection
-            connectionRepository.isNonTerrestrial.value = true
-            // GIVEN this carrier has set INFLATE_SIGNAL_STRENGTH
-            connectionRepository.inflateSignalStrength.value = true
+        // GIVEN a satellite connection
+        connectionRepo.isNonTerrestrial.setValue(true)
+        // GIVEN this carrier has set INFLATE_SIGNAL_STRENGTH
+        connectionRepo.inflateSignalStrength.setValue(true)
 
-            connectionRepository.satelliteLevel.value = 4
-            assertThat(latest!!.level).isEqualTo(4)
+        connectionRepo.satelliteLevel.setValue(4)
+        assertThat(latest!!.level).isEqualTo(4)
 
-            connectionRepository.inflateSignalStrength.value = true
-            connectionRepository.primaryLevel.value = 4
+        connectionRepo.inflateSignalStrength.setValue(true)
+        connectionRepo.primaryLevel.setValue(4)
 
-            // Icon level is unaffected
-            assertThat(latest!!.level).isEqualTo(4)
-        }
+        // Icon level is unaffected
+        assertThat(latest!!.level).isEqualTo(4)
+    }
 
     @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
     @Test
-    fun satBasedIcon_usesPrimaryLevel_flagOff() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.signalLevelIcon)
+    fun satBasedIcon_usesPrimaryLevel_flagOff() = runTest {
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            // GIVEN a satellite connection
-            connectionRepository.isNonTerrestrial.value = true
+        // GIVEN a satellite connection
+        connectionRepo.isNonTerrestrial.setValue(true)
 
-            // GIVEN primary level is set
-            connectionRepository.primaryLevel.value = 4
-            connectionRepository.satelliteLevel.value = 0
+        // GIVEN primary level is set
+        connectionRepo.primaryLevel.setValue(4)
+        connectionRepo.satelliteLevel.setValue(0)
 
-            // THEN icon uses the primary level because the flag is off
-            assertThat(latest!!.level).isEqualTo(4)
-        }
+        // THEN icon uses the primary level because the flag is off
+        assertThat(latest!!.level).isEqualTo(4)
+    }
 
     @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
     @Test
-    fun satBasedIcon_usesSatelliteLevel_flagOn() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.signalLevelIcon)
+    fun satBasedIcon_usesSatelliteLevel_flagOn() = runTest {
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            // GIVEN a satellite connection
-            connectionRepository.isNonTerrestrial.value = true
+        // GIVEN a satellite connection
+        connectionRepo.isNonTerrestrial.setValue(true)
 
-            // GIVEN satellite level is set
-            connectionRepository.satelliteLevel.value = 4
-            connectionRepository.primaryLevel.value = 0
+        // GIVEN satellite level is set
+        connectionRepo.satelliteLevel.setValue(4)
+        connectionRepo.primaryLevel.setValue(0)
 
-            // THEN icon uses the satellite level because the flag is on
-            assertThat(latest!!.level).isEqualTo(4)
-        }
+        // THEN icon uses the satellite level because the flag is on
+        assertThat(latest!!.level).isEqualTo(4)
+    }
 
     /**
      * Context (b/377518113), this test will not be needed after FLAG_CARRIER_ROAMING_NB_IOT_NTN is
@@ -816,43 +728,23 @@
      */
     @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
     @Test
-    fun satBasedIcon_reportsLevelZeroWhenOutOfService() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.signalLevelIcon)
+    fun satBasedIcon_reportsLevelZeroWhenOutOfService() = runTest {
+        val latest by underTest.signalLevelIcon.collectLastValue()
 
-            // GIVEN a satellite connection
-            connectionRepository.isNonTerrestrial.value = true
-            // GIVEN this carrier has set INFLATE_SIGNAL_STRENGTH
-            connectionRepository.inflateSignalStrength.value = true
+        // GIVEN a satellite connection
+        connectionRepo.isNonTerrestrial.setValue(true)
+        // GIVEN this carrier has set INFLATE_SIGNAL_STRENGTH
+        connectionRepo.inflateSignalStrength.setValue(true)
 
-            connectionRepository.primaryLevel.value = 4
-            assertThat(latest!!.level).isEqualTo(4)
+        connectionRepo.primaryLevel.setValue(4)
+        assertThat(latest!!.level).isEqualTo(4)
 
-            connectionRepository.isInService.value = false
-            connectionRepository.primaryLevel.value = 4
+        connectionRepo.isInService.setValue(false)
+        connectionRepo.primaryLevel.setValue(4)
 
-            // THEN level reports 0, by policy
-            assertThat(latest!!.level).isEqualTo(0)
-        }
-
-    private fun createInteractor(
-        overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
-    ) =
-        MobileIconInteractorKairosImpl(
-            testScope.backgroundScope,
-            mobileIconsInteractor.activeDataConnectionHasDataEnabled,
-            mobileIconsInteractor.alwaysShowDataRatIcon,
-            mobileIconsInteractor.alwaysUseCdmaLevel,
-            mobileIconsInteractor.isSingleCarrier,
-            mobileIconsInteractor.mobileIsDefault,
-            mobileIconsInteractor.defaultMobileIconMapping,
-            mobileIconsInteractor.defaultMobileIconGroup,
-            mobileIconsInteractor.isDefaultConnectionFailed,
-            mobileIconsInteractor.isForceHidden,
-            connectionRepository,
-            context,
-            overrides,
-        )
+        // THEN level reports 0, by policy
+        assertThat(latest!!.level).isEqualTo(0)
+    }
 
     companion object {
         private const val GSM_LEVEL = 1
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosTest.kt
index a9360d1..4f6439d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,186 +28,164 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fake
 import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosTestScope
+import com.android.systemui.kairos.runKairosTest
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runCurrent
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.statusbar.core.NewStatusBarIcons
 import com.android.systemui.statusbar.core.StatusBarRootModernization
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryLogbufferName
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
 import com.android.systemui.statusbar.pipeline.shared.data.repository.fake
-import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
 import com.android.systemui.testKosmos
-import com.android.systemui.util.CarrierConfigTracker
+import com.android.systemui.util.carrierConfigTracker
 import com.google.common.truth.Truth.assertThat
 import java.util.UUID
 import kotlinx.coroutines.test.advanceTimeBy
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
+@OptIn(ExperimentalKairosApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MobileIconsInteractorKairosTest : SysuiTestCase() {
-    private val kosmos by lazy {
+
+    private val kosmos =
         testKosmos().apply {
-            mobileConnectionsRepositoryLogbufferName = "MobileIconsInteractorTest"
-            mobileConnectionsRepository.fake.run {
-                setMobileConnectionRepositoryMap(
-                    mapOf(
-                        SUB_1_ID to FakeMobileConnectionRepository(SUB_1_ID, mock()),
-                        SUB_2_ID to FakeMobileConnectionRepository(SUB_2_ID, mock()),
-                        SUB_3_ID to FakeMobileConnectionRepository(SUB_3_ID, mock()),
-                        SUB_4_ID to FakeMobileConnectionRepository(SUB_4_ID, mock()),
-                    )
-                )
-                setActiveMobileDataSubscriptionId(SUB_1_ID)
-            }
+            useUnconfinedTestDispatcher()
+            mobileConnectionsRepositoryKairos =
+                fakeMobileConnectionsRepositoryKairos.apply {
+                    setActiveMobileDataSubscriptionId(SUB_1_ID)
+                    subscriptions.setValue(listOf(SUB_1, SUB_2, SUB_3_OPP, SUB_4_OPP))
+                }
             featureFlagsClassic.fake.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
         }
-    }
 
-    // shortcut rename
-    private val Kosmos.connectionsRepository by Fixture { mobileConnectionsRepository.fake }
+    private val Kosmos.underTest
+        get() = mobileIconsInteractorKairos
 
-    private val Kosmos.carrierConfigTracker by Fixture { mock<CarrierConfigTracker>() }
-
-    private val Kosmos.underTest by Fixture {
-        MobileIconsInteractorKairosImpl(
-            mobileConnectionsRepository,
-            carrierConfigTracker,
-            tableLogger = mock(),
-            connectivityRepository,
-            FakeUserSetupRepository(),
-            testScope.backgroundScope,
-            context,
-            featureFlagsClassic,
-        )
-    }
+    private fun runTest(block: suspend KairosTestScope.() -> Unit) =
+        kosmos.run { runKairosTest { block() } }
 
     @Test
-    fun filteredSubscriptions_default() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+    fun filteredSubscriptions_default() = runTest {
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(emptyList())
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            assertThat(latest).isEqualTo(listOf<SubscriptionModel>())
-        }
+        assertThat(latest).isEqualTo(emptyList<SubscriptionModel>())
+    }
 
     // Based on the logic from the old pipeline, we'll never filter subs when there are more than 2
     @Test
-    fun filteredSubscriptions_moreThanTwo_doesNotFilter() =
-        kosmos.runTest {
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
-            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
+    fun filteredSubscriptions_moreThanTwo_doesNotFilter() = runTest {
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(
+            listOf(SUB_1, SUB_3_OPP, SUB_4_OPP)
+        )
+        mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(SUB_4_ID)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            assertThat(latest).isEqualTo(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
-        }
+        assertThat(latest).isEqualTo(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
+    }
 
     @Test
-    fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
-        kosmos.runTest {
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+    fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() = runTest {
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            assertThat(latest).isEqualTo(listOf(SUB_1, SUB_2))
-        }
+        assertThat(latest).isEqualTo(listOf(SUB_1, SUB_2))
+    }
 
     @Test
-    fun filteredSubscriptions_opportunistic_differentGroups_doesNotFilter() =
-        kosmos.runTest {
-            connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
-            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+    fun filteredSubscriptions_opportunistic_differentGroups_doesNotFilter() = runTest {
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_3_OPP, SUB_4_OPP))
+        mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(SUB_3_ID)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            assertThat(latest).isEqualTo(listOf(SUB_3_OPP, SUB_4_OPP))
-        }
+        assertThat(latest).isEqualTo(listOf(SUB_3_OPP, SUB_4_OPP))
+    }
 
     @Test
-    fun filteredSubscriptions_opportunistic_nonGrouped_doesNotFilter() =
-        kosmos.runTest {
-            val (sub1, sub2) =
-                createSubscriptionPair(
-                    subscriptionIds = Pair(SUB_1_ID, SUB_2_ID),
-                    opportunistic = Pair(true, true),
-                    grouped = false,
-                )
-            connectionsRepository.setSubscriptions(listOf(sub1, sub2))
-            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
+    fun filteredSubscriptions_opportunistic_nonGrouped_doesNotFilter() = runTest {
+        val (sub1, sub2) =
+            createSubscriptionPair(
+                subscriptionIds = Pair(SUB_1_ID, SUB_2_ID),
+                opportunistic = Pair(true, true),
+                grouped = false,
+            )
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub1, sub2))
+        mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(SUB_1_ID)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            assertThat(latest).isEqualTo(listOf(sub1, sub2))
-        }
+        assertThat(latest).isEqualTo(listOf(sub1, sub2))
+    }
 
     @Test
-    fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_3() =
-        kosmos.runTest {
-            val (sub3, sub4) =
-                createSubscriptionPair(
-                    subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
-                    opportunistic = Pair(true, true),
-                    grouped = true,
-                )
-            connectionsRepository.setSubscriptions(listOf(sub3, sub4))
-            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
-            whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
-                .thenReturn(false)
+    fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_3() = runTest {
+        val (sub3, sub4) =
+            createSubscriptionPair(
+                subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+                opportunistic = Pair(true, true),
+                grouped = true,
+            )
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub3, sub4))
+        mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(SUB_3_ID)
+        whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
+            .thenReturn(false)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            // Filtered subscriptions should show the active one when the config is false
-            assertThat(latest).isEqualTo(listOf(sub3))
-        }
+        // Filtered subscriptions should show the active one when the config is false
+        assertThat(latest).isEqualTo(listOf(sub3))
+    }
 
     @Test
-    fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_4() =
-        kosmos.runTest {
-            val (sub3, sub4) =
-                createSubscriptionPair(
-                    subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
-                    opportunistic = Pair(true, true),
-                    grouped = true,
-                )
-            connectionsRepository.setSubscriptions(listOf(sub3, sub4))
-            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
-            whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
-                .thenReturn(false)
+    fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_4() = runTest {
+        val (sub3, sub4) =
+            createSubscriptionPair(
+                subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+                opportunistic = Pair(true, true),
+                grouped = true,
+            )
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub3, sub4))
+        mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(SUB_4_ID)
+        whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
+            .thenReturn(false)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            // Filtered subscriptions should show the active one when the config is false
-            assertThat(latest).isEqualTo(listOf(sub4))
-        }
+        // Filtered subscriptions should show the active one when the config is false
+        assertThat(latest).isEqualTo(listOf(sub4))
+    }
 
     @Test
     fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_active_1() =
-        kosmos.runTest {
+        runTest {
             val (sub1, sub3) =
                 createSubscriptionPair(
                     subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
                     opportunistic = Pair(false, true),
                     grouped = true,
                 )
-            connectionsRepository.setSubscriptions(listOf(sub1, sub3))
-            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
+            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub1, sub3))
+            mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(SUB_1_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(true)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+            val latest by underTest.filteredSubscriptions.collectLastValue()
 
             // Filtered subscriptions should show the primary (non-opportunistic) if the config is
             // true
@@ -216,19 +194,19 @@
 
     @Test
     fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_nonActive_1() =
-        kosmos.runTest {
+        runTest {
             val (sub1, sub3) =
                 createSubscriptionPair(
                     subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
                     opportunistic = Pair(false, true),
                     grouped = true,
                 )
-            connectionsRepository.setSubscriptions(listOf(sub1, sub3))
-            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub1, sub3))
+            mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(SUB_3_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(true)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+            val latest by underTest.filteredSubscriptions.collectLastValue()
 
             // Filtered subscriptions should show the primary (non-opportunistic) if the config is
             // true
@@ -236,135 +214,130 @@
         }
 
     @Test
-    fun filteredSubscriptions_vcnSubId_agreesWithActiveSubId_usesActiveAkaVcnSub() =
-        kosmos.runTest {
-            val (sub1, sub3) =
-                createSubscriptionPair(
-                    subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
-                    opportunistic = Pair(true, true),
-                    grouped = true,
-                )
-            connectionsRepository.setSubscriptions(listOf(sub1, sub3))
-            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
-            kosmos.connectivityRepository.fake.vcnSubId.value = SUB_3_ID
-            whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
-                .thenReturn(false)
+    fun filteredSubscriptions_vcnSubId_agreesWithActiveSubId_usesActiveAkaVcnSub() = runTest {
+        val (sub1, sub3) =
+            createSubscriptionPair(
+                subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
+                opportunistic = Pair(true, true),
+                grouped = true,
+            )
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub1, sub3))
+        mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(SUB_3_ID)
+        kosmos.connectivityRepository.fake.vcnSubId.value = SUB_3_ID
+        whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
+            .thenReturn(false)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            assertThat(latest).isEqualTo(listOf(sub3))
-        }
+        assertThat(latest).isEqualTo(listOf(sub3))
+    }
 
     @Test
-    fun filteredSubscriptions_vcnSubId_disagreesWithActiveSubId_usesVcnSub() =
-        kosmos.runTest {
-            val (sub1, sub3) =
-                createSubscriptionPair(
-                    subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
-                    opportunistic = Pair(true, true),
-                    grouped = true,
-                )
-            connectionsRepository.setSubscriptions(listOf(sub1, sub3))
-            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
-            kosmos.connectivityRepository.fake.vcnSubId.value = SUB_1_ID
-            whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
-                .thenReturn(false)
+    fun filteredSubscriptions_vcnSubId_disagreesWithActiveSubId_usesVcnSub() = runTest {
+        val (sub1, sub3) =
+            createSubscriptionPair(
+                subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
+                opportunistic = Pair(true, true),
+                grouped = true,
+            )
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub1, sub3))
+        mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(SUB_3_ID)
+        kosmos.connectivityRepository.fake.vcnSubId.value = SUB_1_ID
+        whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
+            .thenReturn(false)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            assertThat(latest).isEqualTo(listOf(sub1))
-        }
+        assertThat(latest).isEqualTo(listOf(sub1))
+    }
 
     @Test
-    fun filteredSubscriptions_doesNotFilterProvisioningWhenFlagIsFalse() =
-        kosmos.runTest {
-            // GIVEN the flag is false
-            featureFlagsClassic.fake.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, false)
+    fun filteredSubscriptions_doesNotFilterProvisioningWhenFlagIsFalse() = runTest {
+        // GIVEN the flag is false
+        featureFlagsClassic.fake.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, false)
 
-            // GIVEN 1 sub that is in PROFILE_CLASS_PROVISIONING
-            val sub1 =
-                SubscriptionModel(
-                    subscriptionId = SUB_1_ID,
-                    isOpportunistic = false,
-                    carrierName = "Carrier 1",
-                    profileClass = PROFILE_CLASS_PROVISIONING,
-                )
+        // GIVEN 1 sub that is in PROFILE_CLASS_PROVISIONING
+        val sub1 =
+            SubscriptionModel(
+                subscriptionId = SUB_1_ID,
+                isOpportunistic = false,
+                carrierName = "Carrier 1",
+                profileClass = PROFILE_CLASS_PROVISIONING,
+            )
 
-            connectionsRepository.setSubscriptions(listOf(sub1))
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub1))
 
-            // WHEN filtering is applied
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        // WHEN filtering is applied
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            // THEN the provisioning sub is still present (unfiltered)
-            assertThat(latest).isEqualTo(listOf(sub1))
-        }
+        // THEN the provisioning sub is still present (unfiltered)
+        assertThat(latest).isEqualTo(listOf(sub1))
+    }
 
     @Test
-    fun filteredSubscriptions_filtersOutProvisioningSubs() =
-        kosmos.runTest {
-            val sub1 =
-                SubscriptionModel(
-                    subscriptionId = SUB_1_ID,
-                    isOpportunistic = false,
-                    carrierName = "Carrier 1",
-                    profileClass = PROFILE_CLASS_UNSET,
-                )
-            val sub2 =
-                SubscriptionModel(
-                    subscriptionId = SUB_2_ID,
-                    isOpportunistic = false,
-                    carrierName = "Carrier 2",
-                    profileClass = PROFILE_CLASS_PROVISIONING,
-                )
+    fun filteredSubscriptions_filtersOutProvisioningSubs() = runTest {
+        val sub1 =
+            SubscriptionModel(
+                subscriptionId = SUB_1_ID,
+                isOpportunistic = false,
+                carrierName = "Carrier 1",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+        val sub2 =
+            SubscriptionModel(
+                subscriptionId = SUB_2_ID,
+                isOpportunistic = false,
+                carrierName = "Carrier 2",
+                profileClass = PROFILE_CLASS_PROVISIONING,
+            )
 
-            connectionsRepository.setSubscriptions(listOf(sub1, sub2))
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub1, sub2))
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            assertThat(latest).isEqualTo(listOf(sub1))
-        }
+        assertThat(latest).isEqualTo(listOf(sub1))
+    }
 
     /** Note: I'm not sure if this will ever be the case, but we can test it at least */
     @Test
-    fun filteredSubscriptions_filtersOutProvisioningSubsBeforeOpportunistic() =
-        kosmos.runTest {
-            // This is a contrived test case, where the active subId is the one that would
-            // also be filtered by opportunistic filtering.
+    fun filteredSubscriptions_filtersOutProvisioningSubsBeforeOpportunistic() = runTest {
+        // This is a contrived test case, where the active subId is the one that would
+        // also be filtered by opportunistic filtering.
 
-            // GIVEN grouped, opportunistic subscriptions
-            val groupUuid = ParcelUuid(UUID.randomUUID())
-            val sub1 =
-                SubscriptionModel(
-                    subscriptionId = 1,
-                    isOpportunistic = true,
-                    groupUuid = groupUuid,
-                    carrierName = "Carrier 1",
-                    profileClass = PROFILE_CLASS_PROVISIONING,
-                )
+        // GIVEN grouped, opportunistic subscriptions
+        val groupUuid = ParcelUuid(UUID.randomUUID())
+        val sub1 =
+            SubscriptionModel(
+                subscriptionId = 1,
+                isOpportunistic = true,
+                groupUuid = groupUuid,
+                carrierName = "Carrier 1",
+                profileClass = PROFILE_CLASS_PROVISIONING,
+            )
 
-            val sub2 =
-                SubscriptionModel(
-                    subscriptionId = 2,
-                    isOpportunistic = true,
-                    groupUuid = groupUuid,
-                    carrierName = "Carrier 2",
-                    profileClass = PROFILE_CLASS_UNSET,
-                )
+        val sub2 =
+            SubscriptionModel(
+                subscriptionId = 2,
+                isOpportunistic = true,
+                groupUuid = groupUuid,
+                carrierName = "Carrier 2",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
 
-            // GIVEN active subId is 1
-            connectionsRepository.setSubscriptions(listOf(sub1, sub2))
-            connectionsRepository.setActiveMobileDataSubscriptionId(1)
+        // GIVEN active subId is 1
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub1, sub2))
+        mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(1)
 
-            // THEN filtering of provisioning subs takes place first, and we result in sub2
+        // THEN filtering of provisioning subs takes place first, and we result in sub2
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        val latest by underTest.filteredSubscriptions.collectLastValue()
 
-            assertThat(latest).isEqualTo(listOf(sub2))
-        }
+        assertThat(latest).isEqualTo(listOf(sub2))
+    }
 
     @Test
     fun filteredSubscriptions_groupedPairAndNonProvisioned_groupedFilteringStillHappens() =
-        kosmos.runTest {
+        runTest {
             // Grouped filtering only happens when the list of subs is length 2. In this case
             // we'll show that filtering of provisioning subs happens before, and thus grouped
             // filtering happens even though the unfiltered list is length 3
@@ -384,87 +357,88 @@
                     profileClass = PROFILE_CLASS_PROVISIONING,
                 )
 
-            connectionsRepository.setSubscriptions(listOf(sub1, sub2, sub3))
-            connectionsRepository.setActiveMobileDataSubscriptionId(1)
+            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(sub1, sub2, sub3))
+            mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(1)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+            val latest by underTest.filteredSubscriptions.collectLastValue()
 
             assertThat(latest).isEqualTo(listOf(sub1))
         }
 
     @Test
-    fun filteredSubscriptions_subNotExclusivelyNonTerrestrial_hasSub() =
-        kosmos.runTest {
-            val notExclusivelyNonTerrestrialSub =
-                SubscriptionModel(
-                    isExclusivelyNonTerrestrial = false,
-                    subscriptionId = 5,
-                    carrierName = "Carrier 5",
-                    profileClass = PROFILE_CLASS_UNSET,
-                )
-
-            connectionsRepository.setSubscriptions(listOf(notExclusivelyNonTerrestrialSub))
-
-            val latest by collectLastValue(underTest.filteredSubscriptions)
-
-            assertThat(latest).isEqualTo(listOf(notExclusivelyNonTerrestrialSub))
-        }
-
-    @Test
-    fun filteredSubscriptions_subExclusivelyNonTerrestrial_doesNotHaveSub() =
-        kosmos.runTest {
-            val exclusivelyNonTerrestrialSub =
-                SubscriptionModel(
-                    isExclusivelyNonTerrestrial = true,
-                    subscriptionId = 5,
-                    carrierName = "Carrier 5",
-                    profileClass = PROFILE_CLASS_UNSET,
-                )
-
-            connectionsRepository.setSubscriptions(listOf(exclusivelyNonTerrestrialSub))
-
-            val latest by collectLastValue(underTest.filteredSubscriptions)
-
-            assertThat(latest).isEmpty()
-        }
-
-    @Test
-    fun filteredSubscription_mixOfExclusivelyNonTerrestrialAndOther_hasOtherSubsOnly() =
-        kosmos.runTest {
-            val exclusivelyNonTerrestrialSub =
-                SubscriptionModel(
-                    isExclusivelyNonTerrestrial = true,
-                    subscriptionId = 5,
-                    carrierName = "Carrier 5",
-                    profileClass = PROFILE_CLASS_UNSET,
-                )
-            val otherSub1 =
-                SubscriptionModel(
-                    isExclusivelyNonTerrestrial = false,
-                    subscriptionId = 1,
-                    carrierName = "Carrier 1",
-                    profileClass = PROFILE_CLASS_UNSET,
-                )
-            val otherSub2 =
-                SubscriptionModel(
-                    isExclusivelyNonTerrestrial = false,
-                    subscriptionId = 2,
-                    carrierName = "Carrier 2",
-                    profileClass = PROFILE_CLASS_UNSET,
-                )
-
-            connectionsRepository.setSubscriptions(
-                listOf(otherSub1, exclusivelyNonTerrestrialSub, otherSub2)
+    fun filteredSubscriptions_subNotExclusivelyNonTerrestrial_hasSub() = runTest {
+        val notExclusivelyNonTerrestrialSub =
+            SubscriptionModel(
+                isExclusivelyNonTerrestrial = false,
+                subscriptionId = 5,
+                carrierName = "Carrier 5",
+                profileClass = PROFILE_CLASS_UNSET,
             )
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(
+            listOf(notExclusivelyNonTerrestrialSub)
+        )
 
-            assertThat(latest).isEqualTo(listOf(otherSub1, otherSub2))
-        }
+        val latest by underTest.filteredSubscriptions.collectLastValue()
+
+        assertThat(latest).isEqualTo(listOf(notExclusivelyNonTerrestrialSub))
+    }
+
+    @Test
+    fun filteredSubscriptions_subExclusivelyNonTerrestrial_doesNotHaveSub() = runTest {
+        val exclusivelyNonTerrestrialSub =
+            SubscriptionModel(
+                isExclusivelyNonTerrestrial = true,
+                subscriptionId = 5,
+                carrierName = "Carrier 5",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(
+            listOf(exclusivelyNonTerrestrialSub)
+        )
+
+        val latest by underTest.filteredSubscriptions.collectLastValue()
+
+        assertThat(latest).isEmpty()
+    }
+
+    @Test
+    fun filteredSubscription_mixOfExclusivelyNonTerrestrialAndOther_hasOtherSubsOnly() = runTest {
+        val exclusivelyNonTerrestrialSub =
+            SubscriptionModel(
+                isExclusivelyNonTerrestrial = true,
+                subscriptionId = 5,
+                carrierName = "Carrier 5",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+        val otherSub1 =
+            SubscriptionModel(
+                isExclusivelyNonTerrestrial = false,
+                subscriptionId = 1,
+                carrierName = "Carrier 1",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+        val otherSub2 =
+            SubscriptionModel(
+                isExclusivelyNonTerrestrial = false,
+                subscriptionId = 2,
+                carrierName = "Carrier 2",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(
+            listOf(otherSub1, exclusivelyNonTerrestrialSub, otherSub2)
+        )
+
+        val latest by underTest.filteredSubscriptions.collectLastValue()
+
+        assertThat(latest).isEqualTo(listOf(otherSub1, otherSub2))
+    }
 
     @Test
     fun filteredSubscriptions_exclusivelyNonTerrestrialSub_andOpportunistic_bothFiltersHappen() =
-        kosmos.runTest {
+        runTest {
             // Exclusively non-terrestrial sub
             val exclusivelyNonTerrestrialSub =
                 SubscriptionModel(
@@ -483,10 +457,12 @@
                 )
 
             // WHEN both an exclusively non-terrestrial sub and opportunistic sub pair is included
-            connectionsRepository.setSubscriptions(listOf(sub3, sub4, exclusivelyNonTerrestrialSub))
-            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+            mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(
+                listOf(sub3, sub4, exclusivelyNonTerrestrialSub)
+            )
+            mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(SUB_3_ID)
 
-            val latest by collectLastValue(underTest.filteredSubscriptions)
+            val latest by underTest.filteredSubscriptions.collectLastValue()
 
             // THEN both the only-non-terrestrial sub and the non-active sub are filtered out,
             // leaving only sub3.
@@ -494,484 +470,427 @@
         }
 
     @Test
-    fun activeDataConnection_turnedOn() =
-        kosmos.runTest {
-            (fakeMobileConnectionsRepository.getRepoForSubId(SUB_1_ID)
-                    as FakeMobileConnectionRepository)
-                .dataEnabled
-                .value = true
+    fun activeDataConnection_turnedOn() = runTest {
+        val connection1 =
+            mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId.sample()[SUB_1_ID]!!
 
-            val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
+        connection1.dataEnabled.setValue(true)
 
-            assertThat(latest).isTrue()
-        }
+        val latest by underTest.activeDataConnectionHasDataEnabled.collectLastValue()
+
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun activeDataConnection_turnedOff() =
-        kosmos.runTest {
-            (fakeMobileConnectionsRepository.getRepoForSubId(SUB_1_ID)
-                    as FakeMobileConnectionRepository)
-                .dataEnabled
-                .value = true
+    fun activeDataConnection_turnedOff() = runTest {
+        val connection1 =
+            mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId.sample()[SUB_1_ID]!!
 
-            val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
+        connection1.dataEnabled.setValue(true)
+        val latest by underTest.activeDataConnectionHasDataEnabled.collectLastValue()
 
-            (fakeMobileConnectionsRepository.getRepoForSubId(SUB_1_ID)
-                    as FakeMobileConnectionRepository)
-                .dataEnabled
-                .value = false
+        connection1.dataEnabled.setValue(false)
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun activeDataConnection_invalidSubId() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
+    fun activeDataConnection_invalidSubId() = runTest {
+        val latest by underTest.activeDataConnectionHasDataEnabled.collectLastValue()
 
-            connectionsRepository.setActiveMobileDataSubscriptionId(INVALID_SUBSCRIPTION_ID)
+        mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(
+            INVALID_SUBSCRIPTION_ID
+        )
 
-            // An invalid active subId should tell us that data is off
-            assertThat(latest).isFalse()
-        }
+        // An invalid active subId should tell us that data is off
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun failedConnection_default_validated_notFailed() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+    fun failedConnection_default_validated_notFailed() = runTest {
+        val latest by underTest.isDefaultConnectionFailed.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = true
-            connectionsRepository.defaultConnectionIsValidated.value = true
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(true)
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun failedConnection_notDefault_notValidated_notFailed() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+    fun failedConnection_notDefault_notValidated_notFailed() = runTest {
+        val latest by underTest.isDefaultConnectionFailed.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = false
-            connectionsRepository.defaultConnectionIsValidated.value = false
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(false)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(false)
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun failedConnection_default_notValidated_failed() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+    fun failedConnection_default_notValidated_failed() = runTest {
+        val latest by underTest.isDefaultConnectionFailed.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = true
-            connectionsRepository.defaultConnectionIsValidated.value = false
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(false)
 
-            assertThat(latest).isTrue()
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun failedConnection_carrierMergedDefault_notValidated_failed() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+    fun failedConnection_carrierMergedDefault_notValidated_failed() = runTest {
+        val latest by underTest.isDefaultConnectionFailed.collectLastValue()
 
-            connectionsRepository.hasCarrierMergedConnection.value = true
-            connectionsRepository.defaultConnectionIsValidated.value = false
+        mobileConnectionsRepositoryKairos.fake.hasCarrierMergedConnection.setValue(true)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(false)
 
-            assertThat(latest).isTrue()
-        }
+        assertThat(latest).isTrue()
+    }
 
     /** Regression test for b/275076959. */
     @Test
-    fun failedConnection_dataSwitchInSameGroup_notFailed() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+    fun failedConnection_dataSwitchInSameGroup_notFailed() = runTest {
+        val latest by underTest.isDefaultConnectionFailed.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = true
-            connectionsRepository.defaultConnectionIsValidated.value = true
-            runCurrent()
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(true)
+        runCurrent()
 
-            // WHEN there's a data change in the same subscription group
-            connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
-            connectionsRepository.defaultConnectionIsValidated.value = false
-            runCurrent()
+        // WHEN there's a data change in the same subscription group
+        mobileConnectionsRepositoryKairos.fake.activeSubChangedInGroupEvent.emit(Unit)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(false)
+        runCurrent()
 
-            // THEN the default connection is *not* marked as failed because of forced validation
-            assertThat(latest).isFalse()
-        }
+        // THEN the default connection is *not* marked as failed because of forced validation
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun failedConnection_dataSwitchNotInSameGroup_isFailed() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+    fun failedConnection_dataSwitchNotInSameGroup_isFailed() = runTest {
+        val latest by underTest.isDefaultConnectionFailed.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = true
-            connectionsRepository.defaultConnectionIsValidated.value = true
-            runCurrent()
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(true)
+        runCurrent()
 
-            // WHEN the connection is invalidated without a activeSubChangedInGroupEvent
-            connectionsRepository.defaultConnectionIsValidated.value = false
+        // WHEN the connection is invalidated without a activeSubChangedInGroupEvent
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(false)
 
-            // THEN the connection is immediately marked as failed
-            assertThat(latest).isTrue()
-        }
+        // THEN the connection is immediately marked as failed
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun alwaysShowDataRatIcon_configHasTrue() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.alwaysShowDataRatIcon)
+    fun alwaysShowDataRatIcon_configHasTrue() = runTest {
+        val latest by underTest.alwaysShowDataRatIcon.collectLastValue()
 
-            val config = MobileMappings.Config()
-            config.alwaysShowDataRatIcon = true
-            connectionsRepository.defaultDataSubRatConfig.value = config
+        val config = MobileMappings.Config()
+        config.alwaysShowDataRatIcon = true
+        mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue(config)
 
-            assertThat(latest).isTrue()
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun alwaysShowDataRatIcon_configHasFalse() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.alwaysShowDataRatIcon)
+    fun alwaysShowDataRatIcon_configHasFalse() = runTest {
+        val latest by underTest.alwaysShowDataRatIcon.collectLastValue()
 
-            val config = MobileMappings.Config()
-            config.alwaysShowDataRatIcon = false
-            connectionsRepository.defaultDataSubRatConfig.value = config
+        val config = MobileMappings.Config()
+        config.alwaysShowDataRatIcon = false
+        mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue(config)
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun alwaysUseCdmaLevel_configHasTrue() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.alwaysUseCdmaLevel)
+    fun alwaysUseCdmaLevel_configHasTrue() = runTest {
+        val latest by underTest.alwaysUseCdmaLevel.collectLastValue()
 
-            val config = MobileMappings.Config()
-            config.alwaysShowCdmaRssi = true
-            connectionsRepository.defaultDataSubRatConfig.value = config
+        val config = MobileMappings.Config()
+        config.alwaysShowCdmaRssi = true
+        mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue(config)
 
-            assertThat(latest).isTrue()
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun alwaysUseCdmaLevel_configHasFalse() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.alwaysUseCdmaLevel)
+    fun alwaysUseCdmaLevel_configHasFalse() = runTest {
+        val latest by underTest.alwaysUseCdmaLevel.collectLastValue()
 
-            val config = MobileMappings.Config()
-            config.alwaysShowCdmaRssi = false
-            connectionsRepository.defaultDataSubRatConfig.value = config
+        val config = MobileMappings.Config()
+        config.alwaysShowCdmaRssi = false
+        mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue(config)
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun isSingleCarrier_zeroSubscriptions_false() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isSingleCarrier)
+    fun isSingleCarrier_zeroSubscriptions_false() = runTest {
+        val latest by underTest.isSingleCarrier.collectLastValue()
 
-            connectionsRepository.setSubscriptions(emptyList())
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(emptyList())
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun isSingleCarrier_oneSubscription_true() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isSingleCarrier)
+    fun isSingleCarrier_oneSubscription_true() = runTest {
+        val latest by underTest.isSingleCarrier.collectLastValue()
 
-            connectionsRepository.setSubscriptions(listOf(SUB_1))
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1))
 
-            assertThat(latest).isTrue()
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun isSingleCarrier_twoSubscriptions_false() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isSingleCarrier)
+    fun isSingleCarrier_twoSubscriptions_false() = runTest {
+        val latest by underTest.isSingleCarrier.collectLastValue()
 
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun isSingleCarrier_updates() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isSingleCarrier)
+    fun isSingleCarrier_updates() = runTest {
+        val latest by underTest.isSingleCarrier.collectLastValue()
 
-            connectionsRepository.setSubscriptions(listOf(SUB_1))
-            assertThat(latest).isTrue()
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1))
+        assertThat(latest).isTrue()
 
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
-            assertThat(latest).isFalse()
-        }
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun mobileIsDefault_mobileFalseAndCarrierMergedFalse_false() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.mobileIsDefault)
+    fun mobileIsDefault_mobileFalseAndCarrierMergedFalse_false() = runTest {
+        val latest by underTest.mobileIsDefault.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = false
-            connectionsRepository.hasCarrierMergedConnection.value = false
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(false)
+        mobileConnectionsRepositoryKairos.fake.hasCarrierMergedConnection.setValue(false)
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun mobileIsDefault_mobileTrueAndCarrierMergedFalse_true() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.mobileIsDefault)
+    fun mobileIsDefault_mobileTrueAndCarrierMergedFalse_true() = runTest {
+        val latest by underTest.mobileIsDefault.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = true
-            connectionsRepository.hasCarrierMergedConnection.value = false
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+        mobileConnectionsRepositoryKairos.fake.hasCarrierMergedConnection.setValue(false)
 
-            assertThat(latest).isTrue()
-        }
+        assertThat(latest).isTrue()
+    }
 
     /** Regression test for b/272586234. */
     @Test
-    fun mobileIsDefault_mobileFalseAndCarrierMergedTrue_true() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.mobileIsDefault)
+    fun mobileIsDefault_mobileFalseAndCarrierMergedTrue_true() = runTest {
+        val latest by underTest.mobileIsDefault.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = false
-            connectionsRepository.hasCarrierMergedConnection.value = true
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(false)
+        mobileConnectionsRepositoryKairos.fake.hasCarrierMergedConnection.setValue(true)
 
-            assertThat(latest).isTrue()
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun mobileIsDefault_updatesWhenRepoUpdates() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.mobileIsDefault)
+    fun mobileIsDefault_updatesWhenRepoUpdates() = runTest {
+        val latest by underTest.mobileIsDefault.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = true
-            assertThat(latest).isTrue()
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+        assertThat(latest).isTrue()
 
-            connectionsRepository.mobileIsDefault.value = false
-            assertThat(latest).isFalse()
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(false)
+        assertThat(latest).isFalse()
 
-            connectionsRepository.hasCarrierMergedConnection.value = true
-            assertThat(latest).isTrue()
-        }
+        mobileConnectionsRepositoryKairos.fake.hasCarrierMergedConnection.setValue(true)
+        assertThat(latest).isTrue()
+    }
 
     // The data switch tests are mostly testing the [forcingCellularValidation] flow, but that flow
     // is private and can only be tested by looking at [isDefaultConnectionFailed].
 
     @Test
-    fun dataSwitch_inSameGroup_validatedMatchesPreviousValue_expiresAfter2s() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+    fun dataSwitch_inSameGroup_validatedMatchesPreviousValue_expiresAfter2s() = runTest {
+        val latest by underTest.isDefaultConnectionFailed.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = true
-            connectionsRepository.defaultConnectionIsValidated.value = true
-            runCurrent()
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(true)
+        runCurrent()
 
-            // Trigger a data change in the same subscription group that's not yet validated
-            connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
-            connectionsRepository.defaultConnectionIsValidated.value = false
-            runCurrent()
+        // Trigger a data change in the same subscription group that's not yet validated
+        mobileConnectionsRepositoryKairos.fake.activeSubChangedInGroupEvent.emit(Unit)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(false)
+        runCurrent()
 
-            // After 1s, the force validation bit is still present, so the connection is not marked
-            // as failed
-            testScope.advanceTimeBy(1000)
-            assertThat(latest).isFalse()
+        // After 1s, the force validation bit is still present, so the connection is not marked
+        // as failed
+        testScope.advanceTimeBy(1000)
+        assertThat(latest).isFalse()
 
-            // After 2s, the force validation expires so the connection updates to failed
-            testScope.advanceTimeBy(1001)
-            assertThat(latest).isTrue()
-        }
+        // After 2s, the force validation expires so the connection updates to failed
+        testScope.advanceTimeBy(1001)
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun dataSwitch_inSameGroup_notValidated_immediatelyMarkedAsFailed() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+    fun dataSwitch_inSameGroup_notValidated_immediatelyMarkedAsFailed() = runTest {
+        val latest by underTest.isDefaultConnectionFailed.collectLastValue()
 
-            connectionsRepository.mobileIsDefault.value = true
-            connectionsRepository.defaultConnectionIsValidated.value = false
-            runCurrent()
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(false)
+        runCurrent()
 
-            connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+        mobileConnectionsRepositoryKairos.fake.activeSubChangedInGroupEvent.emit(Unit)
 
-            assertThat(latest).isTrue()
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun dataSwitch_loseValidation_thenSwitchHappens_clearsForcedBit() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+    fun dataSwitch_loseValidation_thenSwitchHappens_clearsForcedBit() = runTest {
+        val latest by underTest.isDefaultConnectionFailed.collectLastValue()
 
-            // GIVEN the network starts validated
-            connectionsRepository.mobileIsDefault.value = true
-            connectionsRepository.defaultConnectionIsValidated.value = true
-            runCurrent()
+        // GIVEN the network starts validated
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(true)
+        runCurrent()
 
-            // WHEN a data change happens in the same group
-            connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+        // WHEN a data change happens in the same group
+        mobileConnectionsRepositoryKairos.fake.activeSubChangedInGroupEvent.emit(Unit)
 
-            // WHEN the validation bit is lost
-            connectionsRepository.defaultConnectionIsValidated.value = false
-            runCurrent()
+        // WHEN the validation bit is lost
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(false)
+        runCurrent()
 
-            // WHEN another data change happens in the same group
-            connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+        // WHEN another data change happens in the same group
+        mobileConnectionsRepositoryKairos.fake.activeSubChangedInGroupEvent.emit(Unit)
 
-            // THEN the forced validation bit is still used...
-            assertThat(latest).isFalse()
+        // THEN the forced validation bit is still used...
+        assertThat(latest).isFalse()
 
-            testScope.advanceTimeBy(1000)
-            assertThat(latest).isFalse()
+        testScope.advanceTimeBy(1000)
+        assertThat(latest).isFalse()
 
-            // ... but expires after 2s
-            testScope.advanceTimeBy(1001)
-            assertThat(latest).isTrue()
-        }
+        // ... but expires after 2s
+        testScope.advanceTimeBy(1001)
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun dataSwitch_whileAlreadyForcingValidation_resetsClock() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
-            connectionsRepository.mobileIsDefault.value = true
-            connectionsRepository.defaultConnectionIsValidated.value = true
-            runCurrent()
+    fun dataSwitch_whileAlreadyForcingValidation_resetsClock() = runTest {
+        val latest by underTest.isDefaultConnectionFailed.collectLastValue()
+        mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(true)
+        runCurrent()
 
-            connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
+        mobileConnectionsRepositoryKairos.fake.activeSubChangedInGroupEvent.emit(Unit)
 
-            testScope.advanceTimeBy(1000)
+        testScope.advanceTimeBy(1000)
 
-            // WHEN another change in same group event happens
-            connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
-            connectionsRepository.defaultConnectionIsValidated.value = false
-            runCurrent()
+        // WHEN another change in same group event happens
+        mobileConnectionsRepositoryKairos.fake.activeSubChangedInGroupEvent.emit(Unit)
+        mobileConnectionsRepositoryKairos.fake.defaultConnectionIsValidated.setValue(false)
+        runCurrent()
 
-            // THEN the forced validation remains for exactly 2 more seconds from now
+        // THEN the forced validation remains for exactly 2 more seconds from now
 
-            // 1.500s from second event
-            testScope.advanceTimeBy(1500)
-            assertThat(latest).isFalse()
+        // 1.500s from second event
+        testScope.advanceTimeBy(1500)
+        assertThat(latest).isFalse()
 
-            // 2.001s from the second event
-            testScope.advanceTimeBy(501)
-            assertThat(latest).isTrue()
-        }
+        // 2.001s from the second event
+        testScope.advanceTimeBy(501)
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun isForceHidden_repoHasMobileHidden_true() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isForceHidden)
+    fun isForceHidden_repoHasMobileHidden_true() = runTest {
+        val latest by underTest.isForceHidden.collectLastValue()
 
-            kosmos.connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
+        kosmos.connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
 
-            assertThat(latest).isTrue()
-        }
+        assertThat(latest).isTrue()
+    }
 
     @Test
-    fun isForceHidden_repoDoesNotHaveMobileHidden_false() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isForceHidden)
+    fun isForceHidden_repoDoesNotHaveMobileHidden_false() = runTest {
+        val latest by underTest.isForceHidden.collectLastValue()
 
-            kosmos.connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+        kosmos.connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
-    fun iconInteractor_cachedPerSubId() =
-        kosmos.runTest {
-            val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
-            val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
+    fun deviceBasedEmergencyMode_emergencyCallsOnly_followsDeviceServiceStateFromRepo() = runTest {
+        val latest by underTest.isDeviceInEmergencyCallsOnlyMode.collectLastValue()
 
-            assertThat(interactor1).isNotNull()
-            assertThat(interactor1).isSameInstanceAs(interactor2)
-        }
+        mobileConnectionsRepositoryKairos.fake.isDeviceEmergencyCallCapable.setValue(true)
 
-    @Test
-    fun deviceBasedEmergencyMode_emergencyCallsOnly_followsDeviceServiceStateFromRepo() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode)
+        assertThat(latest).isTrue()
 
-            connectionsRepository.isDeviceEmergencyCallCapable.value = true
+        mobileConnectionsRepositoryKairos.fake.isDeviceEmergencyCallCapable.setValue(false)
 
-            assertThat(latest).isTrue()
-
-            connectionsRepository.isDeviceEmergencyCallCapable.value = false
-
-            assertThat(latest).isFalse()
-        }
-
-    @Test
-    fun defaultDataSubId_tracksRepo() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.defaultDataSubId)
-
-            connectionsRepository.defaultDataSubId.value = 1
-
-            assertThat(latest).isEqualTo(1)
-
-            connectionsRepository.defaultDataSubId.value = 2
-
-            assertThat(latest).isEqualTo(2)
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
     @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
-    fun isStackable_tracksNumberOfSubscriptions() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isStackable)
+    fun isStackable_tracksNumberOfSubscriptions() = runTest {
+        val latest by underTest.isStackable.collectLastValue()
 
-            connectionsRepository.setSubscriptions(listOf(SUB_1))
-            assertThat(latest).isFalse()
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1))
+        assertThat(latest).isFalse()
 
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
-            assertThat(latest).isTrue()
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
+        assertThat(latest).isTrue()
 
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2, SUB_3_OPP))
-            assertThat(latest).isFalse()
-        }
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(
+            listOf(SUB_1, SUB_2, SUB_3_OPP)
+        )
+        assertThat(latest).isFalse()
+    }
 
     @Test
     @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
-    fun isStackable_checksForTerrestrialConnections() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isStackable)
+    fun isStackable_checksForTerrestrialConnections() = runTest {
+        val latest by underTest.isStackable.collectLastValue()
 
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
-            setNumberOfLevelsForSubId(SUB_1_ID, 5)
-            setNumberOfLevelsForSubId(SUB_2_ID, 5)
-            assertThat(latest).isTrue()
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
+        setNumberOfLevelsForSubId(SUB_1_ID, 5)
+        setNumberOfLevelsForSubId(SUB_2_ID, 5)
+        assertThat(latest).isTrue()
 
-            (fakeMobileConnectionsRepository.getRepoForSubId(SUB_1_ID)
-                    as FakeMobileConnectionRepository)
-                .isNonTerrestrial
-                .value = true
+        mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+            .sample()[SUB_1_ID]!!
+            .isNonTerrestrial
+            .setValue(true)
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
     @Test
     @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
-    fun isStackable_checksForNumberOfBars() =
-        kosmos.runTest {
-            val latest by collectLastValue(underTest.isStackable)
+    fun isStackable_checksForNumberOfBars() = runTest {
+        val latest by underTest.isStackable.collectLastValue()
 
-            // Number of levels is the same for both
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
-            setNumberOfLevelsForSubId(SUB_1_ID, 5)
-            setNumberOfLevelsForSubId(SUB_2_ID, 5)
+        // Number of levels is the same for both
+        mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
+        setNumberOfLevelsForSubId(SUB_1_ID, 5)
+        setNumberOfLevelsForSubId(SUB_2_ID, 5)
 
-            assertThat(latest).isTrue()
+        assertThat(latest).isTrue()
 
-            // Change the number of levels to be different than SUB_2
-            setNumberOfLevelsForSubId(SUB_1_ID, 6)
+        // Change the number of levels to be different than SUB_2
+        setNumberOfLevelsForSubId(SUB_1_ID, 6)
 
-            assertThat(latest).isFalse()
-        }
+        assertThat(latest).isFalse()
+    }
 
-    private fun setNumberOfLevelsForSubId(subId: Int, numberOfLevels: Int) {
-        with(kosmos) {
-            (fakeMobileConnectionsRepository.getRepoForSubId(subId)
-                    as FakeMobileConnectionRepository)
-                .numberOfLevels
-                .value = numberOfLevels
-        }
+    private suspend fun KairosTestScope.setNumberOfLevelsForSubId(subId: Int, numberOfLevels: Int) {
+        mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+            .sample()[subId]!!
+            .numberOfLevels
+            .setValue(numberOfLevels)
     }
 
     /**
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt
new file mode 100644
index 0000000..57e63a5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt
@@ -0,0 +1,198 @@
+/*
+ * 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.pipeline.mobile.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.log.table.logcatTableLogBuffer
+import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
+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.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+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.shared.ConnectivityConstants
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.util.CarrierConfigTracker
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LocationBasedMobileIconViewModelKairosTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
+    private lateinit var commonImpl: MobileIconViewModelCommonKairos
+    private lateinit var homeIcon: HomeMobileIconViewModelKairos
+    private lateinit var qsIcon: QsMobileIconViewModelKairos
+    private lateinit var keyguardIcon: KeyguardMobileIconViewModelKairos
+    private lateinit var iconsInteractor: MobileIconsInteractor
+    private lateinit var interactor: MobileIconInteractor
+    private val connectionsRepository = kosmos.fakeMobileConnectionsRepository
+    private lateinit var repository: FakeMobileConnectionRepository
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
+
+    private val connectivityRepository = FakeConnectivityRepository()
+    private val flags =
+        FakeFeatureFlagsClassic().also {
+            it.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
+        }
+
+    @Mock private lateinit var constants: ConnectivityConstants
+    private val tableLogBuffer =
+        logcatTableLogBuffer(kosmos, "LocationBasedMobileIconViewModelTest")
+    @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
+
+    private val testDispatcher = UnconfinedTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                FakeAirplaneModeRepository(),
+                FakeConnectivityRepository(),
+                connectionsRepository,
+            )
+        repository =
+            FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
+                isInService.value = true
+                cdmaLevel.value = 1
+                primaryLevel.value = 1
+                isEmergencyOnly.value = false
+                numberOfLevels.value = 4
+                resolvedNetworkType.value = ResolvedNetworkType.DefaultNetworkType(lookupKey = "3G")
+                dataConnectionState.value = DataConnectionState.Connected
+            }
+
+        connectionsRepository.activeMobileDataRepository.value = repository
+
+        connectivityRepository.apply { setMobileConnected() }
+
+        iconsInteractor =
+            MobileIconsInteractorImpl(
+                connectionsRepository,
+                carrierConfigTracker,
+                tableLogBuffer,
+                connectivityRepository,
+                FakeUserSetupRepository(),
+                testScope.backgroundScope,
+                context,
+                flags,
+            )
+
+        interactor =
+            MobileIconInteractorImpl(
+                testScope.backgroundScope,
+                iconsInteractor.activeDataConnectionHasDataEnabled,
+                iconsInteractor.alwaysShowDataRatIcon,
+                iconsInteractor.alwaysUseCdmaLevel,
+                iconsInteractor.isSingleCarrier,
+                iconsInteractor.mobileIsDefault,
+                iconsInteractor.defaultMobileIconMapping,
+                iconsInteractor.defaultMobileIconGroup,
+                iconsInteractor.isDefaultConnectionFailed,
+                iconsInteractor.isForceHidden,
+                repository,
+                context,
+                MobileIconCarrierIdOverridesFake(),
+            )
+
+        commonImpl =
+            MobileIconViewModelKairos(
+                SUB_1_ID,
+                interactor,
+                airplaneModeInteractor,
+                constants,
+                testScope.backgroundScope,
+            )
+
+        homeIcon = HomeMobileIconViewModelKairos(commonImpl, mock())
+        qsIcon = QsMobileIconViewModelKairos(commonImpl)
+        keyguardIcon = KeyguardMobileIconViewModelKairos(commonImpl)
+    }
+
+    @Test
+    fun locationBasedViewModelsReceiveSameIconIdWhenCommonImplUpdates() =
+        testScope.runTest {
+            var latestHome: SignalIconModel? = null
+            val homeJob = homeIcon.icon.onEach { latestHome = it }.launchIn(this)
+
+            var latestQs: SignalIconModel? = null
+            val qsJob = qsIcon.icon.onEach { latestQs = it }.launchIn(this)
+
+            var latestKeyguard: SignalIconModel? = null
+            val keyguardJob = keyguardIcon.icon.onEach { latestKeyguard = it }.launchIn(this)
+
+            var expected = defaultSignal(level = 1)
+
+            assertThat(latestHome).isEqualTo(expected)
+            assertThat(latestQs).isEqualTo(expected)
+            assertThat(latestKeyguard).isEqualTo(expected)
+
+            repository.setAllLevels(2)
+            expected = defaultSignal(level = 2)
+
+            assertThat(latestHome).isEqualTo(expected)
+            assertThat(latestQs).isEqualTo(expected)
+            assertThat(latestKeyguard).isEqualTo(expected)
+
+            homeJob.cancel()
+            qsJob.cancel()
+            keyguardJob.cancel()
+        }
+
+    companion object {
+        private const val SUB_1_ID = 1
+        private const val NUM_LEVELS = 4
+
+        /** Convenience constructor for these tests */
+        fun defaultSignal(level: Int = 1): SignalIconModel {
+            return SignalIconModel.Cellular(
+                level,
+                NUM_LEVELS,
+                showExclamationMark = false,
+                carrierNetworkChange = false,
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt
new file mode 100644
index 0000000..6b114a8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt
@@ -0,0 +1,1077 @@
+/*
+ * 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.pipeline.mobile.ui.viewmodel
+
+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.settingslib.mobile.MobileMappings
+import com.android.settingslib.mobile.TelephonyIcons.G
+import com.android.settingslib.mobile.TelephonyIcons.THREE_G
+import com.android.settingslib.mobile.TelephonyIcons.UNKNOWN
+import com.android.systemui.Flags.FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.log.table.logcatTableLogBuffer
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
+import com.android.systemui.statusbar.core.NewStatusBarIcons
+import com.android.systemui.statusbar.core.StatusBarRootModernization
+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
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.util.CarrierConfigTracker
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.yield
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MobileIconViewModelKairosTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
+    private var connectivityRepository = FakeConnectivityRepository()
+
+    private lateinit var underTest: MobileIconViewModelKairos
+    private lateinit var interactor: MobileIconInteractorImpl
+    private lateinit var iconsInteractor: MobileIconsInteractorImpl
+    private lateinit var repository: FakeMobileConnectionRepository
+    private lateinit var connectionsRepository: FakeMobileConnectionsRepository
+    private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
+    @Mock private lateinit var constants: ConnectivityConstants
+    private val tableLogBuffer = logcatTableLogBuffer(kosmos, "MobileIconViewModelTest")
+    @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
+
+    private val flags =
+        FakeFeatureFlagsClassic().also {
+            it.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
+        }
+    private val testDispatcher = UnconfinedTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(constants.hasDataCapabilities).thenReturn(true)
+
+        connectionsRepository =
+            FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer)
+
+        repository =
+            FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
+                setNetworkTypeKey(connectionsRepository.GSM_KEY)
+                isInService.value = true
+                dataConnectionState.value = DataConnectionState.Connected
+                dataEnabled.value = true
+            }
+        connectionsRepository.activeMobileDataRepository.value = repository
+        connectionsRepository.mobileIsDefault.value = true
+
+        airplaneModeRepository = FakeAirplaneModeRepository()
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                airplaneModeRepository,
+                connectivityRepository,
+                kosmos.fakeMobileConnectionsRepository,
+            )
+
+        iconsInteractor =
+            MobileIconsInteractorImpl(
+                connectionsRepository,
+                carrierConfigTracker,
+                tableLogBuffer,
+                connectivityRepository,
+                FakeUserSetupRepository(),
+                testScope.backgroundScope,
+                context,
+                flags,
+            )
+
+        interactor =
+            MobileIconInteractorImpl(
+                testScope.backgroundScope,
+                iconsInteractor.activeDataConnectionHasDataEnabled,
+                iconsInteractor.alwaysShowDataRatIcon,
+                iconsInteractor.alwaysUseCdmaLevel,
+                iconsInteractor.isSingleCarrier,
+                iconsInteractor.mobileIsDefault,
+                iconsInteractor.defaultMobileIconMapping,
+                iconsInteractor.defaultMobileIconGroup,
+                iconsInteractor.isDefaultConnectionFailed,
+                iconsInteractor.isForceHidden,
+                repository,
+                context,
+                MobileIconCarrierIdOverridesFake(),
+            )
+        createAndSetViewModel()
+    }
+
+    @Test
+    fun isVisible_notDataCapable_alwaysFalse() =
+        testScope.runTest {
+            // Create a new view model here so the constants are properly read
+            whenever(constants.hasDataCapabilities).thenReturn(false)
+            createAndSetViewModel()
+
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_notAirplane_notForceHidden_true() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_airplaneAndNotAllowed_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(true)
+            repository.isAllowedDuringAirplaneMode.value = false
+            connectivityRepository.setForceHiddenIcons(setOf())
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    /** Regression test for b/291993542. */
+    @Test
+    fun isVisible_airplaneButAllowed_true() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(true)
+            repository.isAllowedDuringAirplaneMode.value = true
+            connectivityRepository.setForceHiddenIcons(setOf())
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_forceHidden_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+            connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_respondsToUpdates() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+            connectivityRepository.setForceHiddenIcons(setOf())
+
+            assertThat(latest).isTrue()
+
+            airplaneModeRepository.setIsAirplaneMode(true)
+            assertThat(latest).isFalse()
+
+            repository.isAllowedDuringAirplaneMode.value = true
+            assertThat(latest).isTrue()
+
+            connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_satellite_respectsAirplaneMode() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isVisible)
+
+            repository.isNonTerrestrial.value = true
+            airplaneModeInteractor.setIsAirplaneMode(false)
+
+            assertThat(latest).isTrue()
+
+            airplaneModeInteractor.setIsAirplaneMode(true)
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun contentDescription_notInService_usesNoPhone() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+
+            repository.isInService.value = false
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+        }
+
+    @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 {
+            val latest by collectLastValue(underTest.contentDescription)
+
+            repository.setAllLevels(2)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
+
+            repository.setAllLevels(0)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+        }
+
+    @Test
+    fun contentDescription_nonInflated_invalidLevelUsesNoSignalText() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+
+            repository.inflateSignalStrength.value = false
+            repository.setAllLevels(-1)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+
+            repository.setAllLevels(100)
+
+            assertThat(latest as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+        }
+
+    @Test
+    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 as MobileContentDescription.Cellular)
+                .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+
+            repository.setAllLevels(100)
+
+            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
+    fun contentDescription_nonInflated_testABunchOfLevelsForNull() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+
+            repository.inflateSignalStrength.value = false
+            repository.numberOfLevels.value = 5
+
+            // -1 and 5 are out of the bounds for non-inflated content descriptions
+            for (i in -1..5) {
+                repository.setAllLevels(i)
+                when (i) {
+                    -1,
+                    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)
+                            .isNotNull()
+                }
+            }
+        }
+
+    @Test
+    fun contentDescription_inflated_testABunchOfLevelsForNull() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.contentDescription)
+            repository.inflateSignalStrength.value = true
+            repository.numberOfLevels.value = 6
+            // -1 and 6 are out of the bounds for inflated content descriptions
+            // Note that the interactor adds 1 to the reported level, hence the -2 to 5 range
+            for (i in -2..5) {
+                repository.setAllLevels(i)
+                when (i) {
+                    -2,
+                    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)
+                            .isNotNull()
+                }
+            }
+        }
+
+    @Test
+    fun networkType_dataEnabled_groupIsRepresented() =
+        testScope.runTest {
+            val expected =
+                Icon.Resource(
+                    THREE_G.dataType,
+                    ContentDescription.Resource(THREE_G.dataContentDescription),
+                )
+            connectionsRepository.mobileIsDefault.value = true
+            repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(expected)
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_null_whenDisabled() =
+        testScope.runTest {
+            repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+            repository.setDataEnabled(false)
+            connectionsRepository.mobileIsDefault.value = true
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_null_whenCarrierNetworkChangeActive() =
+        testScope.runTest {
+            repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+            repository.carrierNetworkChangeActive.value = true
+            connectionsRepository.mobileIsDefault.value = true
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkTypeIcon_notNull_whenEnabled() =
+        testScope.runTest {
+            val expected =
+                Icon.Resource(
+                    THREE_G.dataType,
+                    ContentDescription.Resource(THREE_G.dataContentDescription),
+                )
+            repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+            repository.setDataEnabled(true)
+            repository.dataConnectionState.value = DataConnectionState.Connected
+            connectionsRepository.mobileIsDefault.value = true
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(expected)
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_nullWhenDataDisconnects() =
+        testScope.runTest {
+            val initial =
+                Icon.Resource(
+                    THREE_G.dataType,
+                    ContentDescription.Resource(THREE_G.dataContentDescription),
+                )
+
+            repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(initial)
+
+            repository.dataConnectionState.value = DataConnectionState.Disconnected
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_null_changeToDisabled() =
+        testScope.runTest {
+            val expected =
+                Icon.Resource(
+                    THREE_G.dataType,
+                    ContentDescription.Resource(THREE_G.dataContentDescription),
+                )
+            repository.dataEnabled.value = true
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(expected)
+
+            repository.dataEnabled.value = false
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_alwaysShow_shownEvenWhenDisabled() =
+        testScope.runTest {
+            repository.dataEnabled.value = false
+
+            connectionsRepository.defaultDataSubRatConfig.value =
+                MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            val expected =
+                Icon.Resource(
+                    THREE_G.dataType,
+                    ContentDescription.Resource(THREE_G.dataContentDescription),
+                )
+            assertThat(latest).isEqualTo(expected)
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_alwaysShow_shownEvenWhenDisconnected() =
+        testScope.runTest {
+            repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+            repository.dataConnectionState.value = DataConnectionState.Disconnected
+
+            connectionsRepository.defaultDataSubRatConfig.value =
+                MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            val expected =
+                Icon.Resource(
+                    THREE_G.dataType,
+                    ContentDescription.Resource(THREE_G.dataContentDescription),
+                )
+            assertThat(latest).isEqualTo(expected)
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_alwaysShow_shownEvenWhenFailedConnection() =
+        testScope.runTest {
+            repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+            connectionsRepository.mobileIsDefault.value = true
+            connectionsRepository.defaultDataSubRatConfig.value =
+                MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            val expected =
+                Icon.Resource(
+                    THREE_G.dataType,
+                    ContentDescription.Resource(THREE_G.dataContentDescription),
+                )
+            assertThat(latest).isEqualTo(expected)
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_alwaysShow_usesDefaultIconWhenInvalid() =
+        testScope.runTest {
+            // The UNKNOWN icon group doesn't have a valid data type icon ID, and the logic from the
+            // old pipeline was to use the default icon group if the map doesn't exist
+            repository.setNetworkTypeKey(UNKNOWN.name)
+            connectionsRepository.defaultDataSubRatConfig.value =
+                MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            val expected =
+                Icon.Resource(
+                    connectionsRepository.defaultMobileIconGroup.value.dataType,
+                    ContentDescription.Resource(G.dataContentDescription),
+                )
+
+            assertThat(latest).isEqualTo(expected)
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_alwaysShow_shownWhenNotDefault() =
+        testScope.runTest {
+            repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+            connectionsRepository.mobileIsDefault.value = false
+            connectionsRepository.defaultDataSubRatConfig.value =
+                MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            val expected =
+                Icon.Resource(
+                    THREE_G.dataType,
+                    ContentDescription.Resource(THREE_G.dataContentDescription),
+                )
+            assertThat(latest).isEqualTo(expected)
+
+            job.cancel()
+        }
+
+    @Test
+    fun networkType_notShownWhenNotDefault() =
+        testScope.runTest {
+            repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+            repository.dataConnectionState.value = DataConnectionState.Connected
+            connectionsRepository.mobileIsDefault.value = false
+
+            var latest: Icon? = null
+            val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun roaming() =
+        testScope.runTest {
+            repository.setAllRoaming(true)
+
+            var latest: Boolean? = null
+            val job = underTest.roaming.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isTrue()
+
+            repository.setAllRoaming(false)
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun dataActivity_nullWhenConfigIsOff() =
+        testScope.runTest {
+            // Create a new view model here so the constants are properly read
+            whenever(constants.shouldShowActivityConfig).thenReturn(false)
+            createAndSetViewModel()
+
+            var inVisible: Boolean? = null
+            val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
+
+            var outVisible: Boolean? = null
+            val outJob = underTest.activityInVisible.onEach { outVisible = it }.launchIn(this)
+
+            var containerVisible: Boolean? = null
+            val containerJob =
+                underTest.activityInVisible.onEach { containerVisible = it }.launchIn(this)
+
+            repository.dataActivityDirection.value =
+                DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+
+            assertThat(inVisible).isFalse()
+            assertThat(outVisible).isFalse()
+            assertThat(containerVisible).isFalse()
+
+            inJob.cancel()
+            outJob.cancel()
+            containerJob.cancel()
+        }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
+    fun dataActivity_configOn_testIndicators_staticFlagOff() =
+        testScope.runTest {
+            // Create a new view model here so the constants are properly read
+            whenever(constants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+
+            var inVisible: Boolean? = null
+            val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
+
+            var outVisible: Boolean? = null
+            val outJob = underTest.activityOutVisible.onEach { outVisible = it }.launchIn(this)
+
+            var containerVisible: Boolean? = null
+            val containerJob =
+                underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this)
+
+            repository.dataActivityDirection.value =
+                DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+
+            yield()
+
+            assertThat(inVisible).isTrue()
+            assertThat(outVisible).isFalse()
+            assertThat(containerVisible).isTrue()
+
+            repository.dataActivityDirection.value =
+                DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+
+            assertThat(inVisible).isFalse()
+            assertThat(outVisible).isTrue()
+            assertThat(containerVisible).isTrue()
+
+            repository.dataActivityDirection.value =
+                DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+
+            assertThat(inVisible).isFalse()
+            assertThat(outVisible).isFalse()
+            assertThat(containerVisible).isFalse()
+
+            inJob.cancel()
+            outJob.cancel()
+            containerJob.cancel()
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
+    fun dataActivity_configOn_testIndicators_staticFlagOn() =
+        testScope.runTest {
+            // Create a new view model here so the constants are properly read
+            whenever(constants.shouldShowActivityConfig).thenReturn(true)
+            createAndSetViewModel()
+
+            var inVisible: Boolean? = null
+            val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
+
+            var outVisible: Boolean? = null
+            val outJob = underTest.activityOutVisible.onEach { outVisible = it }.launchIn(this)
+
+            var containerVisible: Boolean? = null
+            val containerJob =
+                underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this)
+
+            repository.dataActivityDirection.value =
+                DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+
+            yield()
+
+            assertThat(inVisible).isTrue()
+            assertThat(outVisible).isFalse()
+            assertThat(containerVisible).isTrue()
+
+            repository.dataActivityDirection.value =
+                DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+
+            assertThat(inVisible).isFalse()
+            assertThat(outVisible).isTrue()
+            assertThat(containerVisible).isTrue()
+
+            repository.dataActivityDirection.value =
+                DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+
+            assertThat(inVisible).isFalse()
+            assertThat(outVisible).isFalse()
+            assertThat(containerVisible).isTrue()
+
+            inJob.cancel()
+            outJob.cancel()
+            containerJob.cancel()
+        }
+
+    @Test
+    fun netTypeBackground_nullWhenNoPrioritizedCapabilities() =
+        testScope.runTest {
+            createAndSetViewModel()
+
+            val latest by collectLastValue(underTest.networkTypeBackground)
+
+            repository.hasPrioritizedNetworkCapabilities.value = false
+
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+    fun netTypeBackground_sliceUiEnabled_notNullWhenPrioritizedCapabilities_newIcons() =
+        testScope.runTest {
+            createAndSetViewModel()
+
+            val latest by collectLastValue(underTest.networkTypeBackground)
+
+            repository.hasPrioritizedNetworkCapabilities.value = true
+
+            assertThat(latest)
+                .isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background_updated, null))
+        }
+
+    @Test
+    @DisableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+    fun netTypeBackground_sliceUiDisabled_notNullWhenPrioritizedCapabilities_oldIcons() =
+        testScope.runTest {
+            createAndSetViewModel()
+
+            val latest by collectLastValue(underTest.networkTypeBackground)
+
+            repository.hasPrioritizedNetworkCapabilities.value = true
+
+            assertThat(latest)
+                .isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background, null))
+        }
+
+    @Test
+    fun nonTerrestrial_defaultProperties() =
+        testScope.runTest {
+            repository.isNonTerrestrial.value = true
+
+            val roaming by collectLastValue(underTest.roaming)
+            val networkTypeIcon by collectLastValue(underTest.networkTypeIcon)
+            val networkTypeBackground by collectLastValue(underTest.networkTypeBackground)
+            val activityInVisible by collectLastValue(underTest.activityInVisible)
+            val activityOutVisible by collectLastValue(underTest.activityOutVisible)
+            val activityContainerVisible by collectLastValue(underTest.activityContainerVisible)
+
+            assertThat(roaming).isFalse()
+            assertThat(networkTypeIcon).isNull()
+            assertThat(networkTypeBackground).isNull()
+            assertThat(activityInVisible).isFalse()
+            assertThat(activityOutVisible).isFalse()
+            assertThat(activityContainerVisible).isFalse()
+        }
+
+    @Test
+    fun nonTerrestrial_ignoresDefaultProperties() =
+        testScope.runTest {
+            repository.isNonTerrestrial.value = true
+
+            val roaming by collectLastValue(underTest.roaming)
+            val networkTypeIcon by collectLastValue(underTest.networkTypeIcon)
+            val networkTypeBackground by collectLastValue(underTest.networkTypeBackground)
+            val activityInVisible by collectLastValue(underTest.activityInVisible)
+            val activityOutVisible by collectLastValue(underTest.activityOutVisible)
+            val activityContainerVisible by collectLastValue(underTest.activityContainerVisible)
+
+            repository.setAllRoaming(true)
+            repository.setNetworkTypeKey(connectionsRepository.LTE_KEY)
+            // sets the background on cellular
+            repository.hasPrioritizedNetworkCapabilities.value = true
+            repository.dataActivityDirection.value =
+                DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+
+            assertThat(roaming).isFalse()
+            assertThat(networkTypeIcon).isNull()
+            assertThat(networkTypeBackground).isNull()
+            assertThat(activityInVisible).isFalse()
+            assertThat(activityOutVisible).isFalse()
+            assertThat(activityContainerVisible).isFalse()
+        }
+
+    @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    @Test
+    fun nonTerrestrial_usesSatelliteIcon_flagOff() =
+        testScope.runTest {
+            repository.isNonTerrestrial.value = true
+            repository.setAllLevels(0)
+            repository.satelliteLevel.value = 0
+
+            val latest by
+                collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
+
+            // Level 0 -> no connection
+            assertThat(latest).isNotNull()
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
+
+            // 1-2 -> 1 bar
+            repository.setAllLevels(1)
+            repository.satelliteLevel.value = 1
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+            repository.setAllLevels(2)
+            repository.satelliteLevel.value = 2
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+            // 3-4 -> 2 bars
+            repository.setAllLevels(3)
+            repository.satelliteLevel.value = 3
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+
+            repository.setAllLevels(4)
+            repository.satelliteLevel.value = 4
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+        }
+
+    @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    @Test
+    fun nonTerrestrial_usesSatelliteIcon_flagOn() =
+        testScope.runTest {
+            repository.isNonTerrestrial.value = true
+            repository.satelliteLevel.value = 0
+
+            val latest by
+                collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
+
+            // Level 0 -> no connection
+            assertThat(latest).isNotNull()
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
+
+            // 1-2 -> 1 bar
+            repository.satelliteLevel.value = 1
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+            repository.satelliteLevel.value = 2
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+            // 3-4 -> 2 bars
+            repository.satelliteLevel.value = 3
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+
+            repository.satelliteLevel.value = 4
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+        }
+
+    @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    @Test
+    fun satelliteIcon_ignoresInflateSignalStrength_flagOff() =
+        testScope.runTest {
+            // Note that this is the exact same test as above, but with inflateSignalStrength set to
+            // true we note that the level is unaffected by inflation
+            repository.inflateSignalStrength.value = true
+            repository.isNonTerrestrial.value = true
+            repository.setAllLevels(0)
+            repository.satelliteLevel.value = 0
+
+            val latest by
+                collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
+
+            // Level 0 -> no connection
+            assertThat(latest).isNotNull()
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
+
+            // 1-2 -> 1 bar
+            repository.setAllLevels(1)
+            repository.satelliteLevel.value = 1
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+            repository.setAllLevels(2)
+            repository.satelliteLevel.value = 2
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+            // 3-4 -> 2 bars
+            repository.setAllLevels(3)
+            repository.satelliteLevel.value = 3
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+
+            repository.setAllLevels(4)
+            repository.satelliteLevel.value = 4
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+        }
+
+    @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    @Test
+    fun satelliteIcon_ignoresInflateSignalStrength_flagOn() =
+        testScope.runTest {
+            // Note that this is the exact same test as above, but with inflateSignalStrength set to
+            // true we note that the level is unaffected by inflation
+            repository.inflateSignalStrength.value = true
+            repository.isNonTerrestrial.value = true
+            repository.satelliteLevel.value = 0
+
+            val latest by
+                collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
+
+            // Level 0 -> no connection
+            assertThat(latest).isNotNull()
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
+
+            // 1-2 -> 1 bar
+            repository.satelliteLevel.value = 1
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+            repository.satelliteLevel.value = 2
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+            // 3-4 -> 2 bars
+            repository.satelliteLevel.value = 3
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+
+            repository.satelliteLevel.value = 4
+            assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+        }
+
+    private fun createAndSetViewModel() {
+        underTest =
+            MobileIconViewModelKairos(
+                SUB_1_ID,
+                interactor,
+                airplaneModeInteractor,
+                constants,
+                testScope.backgroundScope,
+            )
+    }
+
+    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/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt
new file mode 100644
index 0000000..e921430
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt
@@ -0,0 +1,385 @@
+/*
+ * 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.pipeline.mobile.ui.viewmodel
+
+import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.mobile.TelephonyIcons
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.statusbar.phone.StatusBarLocation
+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.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
+import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
+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.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MobileIconsViewModelKairosTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
+    private lateinit var underTest: MobileIconsViewModelKairos
+    private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
+    private val flags = FakeFeatureFlagsClassic()
+
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
+    @Mock private lateinit var constants: ConnectivityConstants
+    @Mock private lateinit var logger: MobileViewLogger
+    @Mock private lateinit var verboseLogger: VerboseMobileViewLogger
+
+    private val testDispatcher = UnconfinedTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                FakeAirplaneModeRepository(),
+                FakeConnectivityRepository(),
+                kosmos.fakeMobileConnectionsRepository,
+            )
+
+        underTest =
+            MobileIconsViewModelKairos(
+                logger,
+                verboseLogger,
+                interactor,
+                airplaneModeInteractor,
+                constants,
+                testScope.backgroundScope,
+            )
+
+        interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+    }
+
+    @Test
+    fun subscriptionIdsFlow_matchesInteractor() =
+        testScope.runTest {
+            var latest: List<Int>? = null
+            val job = underTest.subscriptionIdsFlow.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value =
+                listOf(
+                    SubscriptionModel(
+                        subscriptionId = 1,
+                        isOpportunistic = false,
+                        carrierName = "Carrier 1",
+                        profileClass = PROFILE_CLASS_UNSET,
+                    )
+                )
+            assertThat(latest).isEqualTo(listOf(1))
+
+            interactor.filteredSubscriptions.value =
+                listOf(
+                    SubscriptionModel(
+                        subscriptionId = 2,
+                        isOpportunistic = false,
+                        carrierName = "Carrier 2",
+                        profileClass = PROFILE_CLASS_UNSET,
+                    ),
+                    SubscriptionModel(
+                        subscriptionId = 5,
+                        isOpportunistic = true,
+                        carrierName = "Carrier 5",
+                        profileClass = PROFILE_CLASS_UNSET,
+                    ),
+                    SubscriptionModel(
+                        subscriptionId = 7,
+                        isOpportunistic = true,
+                        carrierName = "Carrier 7",
+                        profileClass = PROFILE_CLASS_UNSET,
+                    ),
+                )
+            assertThat(latest).isEqualTo(listOf(2, 5, 7))
+
+            interactor.filteredSubscriptions.value = emptyList()
+            assertThat(latest).isEmpty()
+
+            job.cancel()
+        }
+
+    @Test
+    fun caching_mobileIconViewModelIsReusedForSameSubId() =
+        testScope.runTest {
+            val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
+            val model2 = underTest.viewModelForSub(1, StatusBarLocation.QS)
+
+            assertThat(model1.commonImpl).isSameInstanceAs(model2.commonImpl)
+        }
+
+    @Test
+    fun caching_invalidViewModelsAreRemovedFromCacheWhenSubDisappears() =
+        testScope.runTest {
+            // Retrieve models to trigger caching
+            val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
+            val model2 = underTest.viewModelForSub(2, StatusBarLocation.QS)
+
+            // Both impls are cached
+            assertThat(underTest.reuseCache.keys).containsExactly(1, 2)
+
+            // SUB_1 is removed from the list...
+            interactor.filteredSubscriptions.value = listOf(SUB_2)
+
+            // ... and dropped from the cache
+            assertThat(underTest.reuseCache.keys).containsExactly(2)
+        }
+
+    @Test
+    fun caching_invalidatedViewModelsAreCanceled() =
+        testScope.runTest {
+            // Retrieve models to trigger caching
+            val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
+            val model2 = underTest.viewModelForSub(2, StatusBarLocation.QS)
+
+            var scope1 = underTest.reuseCache[1]?.second
+            var scope2 = underTest.reuseCache[2]?.second
+
+            // Scopes are not canceled
+            assertTrue(scope1!!.isActive)
+            assertTrue(scope2!!.isActive)
+
+            // SUB_1 is removed from the list...
+            interactor.filteredSubscriptions.value = listOf(SUB_2)
+
+            // scope1 is canceled
+            assertFalse(scope1!!.isActive)
+            assertTrue(scope2!!.isActive)
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_noSubs_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = emptyList()
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_oneSub_notShowingRat_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1)
+            // The unknown icon group doesn't show a RAT
+            interactor.getInteractorForSubId(1)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_oneSub_showingRat_true() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1)
+            // The 3G icon group will show a RAT
+            interactor.getInteractorForSubId(1)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_updatesAsSubUpdates() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1)
+            val sub1Interactor = interactor.getInteractorForSubId(1)!!
+
+            sub1Interactor.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+            assertThat(latest).isTrue()
+
+            sub1Interactor.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+            assertThat(latest).isFalse()
+
+            sub1Interactor.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.LTE)
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubNotShowingRat_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+            interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubShowingRat_true() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+            interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+
+            assertThat(latest).isTrue()
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_subListUpdates_valAlsoUpdates() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+            interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+
+            assertThat(latest).isTrue()
+
+            // WHEN the sub list gets new subscriptions where the last subscription is not showing
+            // the network type icon
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3)
+            interactor.getInteractorForSubId(3)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+
+            // THEN the flow updates
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_subListReorders_valAlsoUpdates() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            // Immediately switch the order so that we've created both interactors
+            interactor.filteredSubscriptions.value = listOf(SUB_2, SUB_1)
+            val sub1Interactor = interactor.getInteractorForSubId(1)!!
+            val sub2Interactor = interactor.getInteractorForSubId(2)!!
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            sub1Interactor.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+            sub2Interactor.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+            assertThat(latest).isTrue()
+
+            // WHEN sub1 becomes last and sub1 has no network type icon
+            interactor.filteredSubscriptions.value = listOf(SUB_2, SUB_1)
+
+            // THEN the flow updates
+            assertThat(latest).isFalse()
+
+            // WHEN sub2 becomes last and sub2 has a network type icon
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+
+            // THEN the flow updates
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    companion object {
+        private val SUB_1 =
+            SubscriptionModel(
+                subscriptionId = 1,
+                isOpportunistic = false,
+                carrierName = "Carrier 1",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+        private val SUB_2 =
+            SubscriptionModel(
+                subscriptionId = 2,
+                isOpportunistic = false,
+                carrierName = "Carrier 2",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+        private val SUB_3 =
+            SubscriptionModel(
+                subscriptionId = 3,
+                isOpportunistic = false,
+                carrierName = "Carrier 3",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt
new file mode 100644
index 0000000..ce35d9d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.statusbar.core.NewStatusBarIcons
+import com.android.systemui.statusbar.core.StatusBarRootModernization
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StackedMobileIconViewModelKairosTest : SysuiTestCase() {
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val testScope = kosmos.testScope
+
+    private val Kosmos.underTest: StackedMobileIconViewModelKairos by Fixture {
+        stackedMobileIconViewModelKairos
+    }
+
+    @Before
+    fun setUp() {
+        kosmos.underTest.activateIn(testScope)
+    }
+
+    @Test
+    @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+    fun dualSim_filtersOutNonDualConnections() =
+        kosmos.runTest {
+            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf()
+            assertThat(underTest.dualSim).isNull()
+
+            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1)
+            assertThat(underTest.dualSim).isNull()
+
+            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3)
+            assertThat(underTest.dualSim).isNull()
+
+            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            assertThat(underTest.dualSim).isNotNull()
+        }
+
+    @Test
+    @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+    fun dualSim_filtersOutNonCellularIcons() =
+        kosmos.runTest {
+            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1)
+            assertThat(underTest.dualSim).isNull()
+
+            fakeMobileIconsInteractor
+                .getInteractorForSubId(SUB_1.subscriptionId)!!
+                .signalLevelIcon
+                .value =
+                SignalIconModel.Satellite(
+                    level = 0,
+                    icon = Icon.Resource(res = 0, contentDescription = null),
+                )
+            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            assertThat(underTest.dualSim).isNull()
+        }
+
+    @Test
+    @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+    fun dualSim_tracksActiveSubId() =
+        kosmos.runTest {
+            // Active sub id is null, order is unchanged
+            fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            setIconLevel(SUB_1.subscriptionId, 1)
+            setIconLevel(SUB_2.subscriptionId, 2)
+
+            assertThat(underTest.dualSim!!.primary.level).isEqualTo(1)
+            assertThat(underTest.dualSim!!.secondary.level).isEqualTo(2)
+
+            // Active sub is 2, order is swapped
+            fakeMobileIconsInteractor.activeMobileDataSubscriptionId.value = SUB_2.subscriptionId
+
+            assertThat(underTest.dualSim!!.primary.level).isEqualTo(2)
+            assertThat(underTest.dualSim!!.secondary.level).isEqualTo(1)
+        }
+
+    private fun setIconLevel(subId: Int, level: Int) {
+        with(kosmos.fakeMobileIconsInteractor.getInteractorForSubId(subId)!!) {
+            signalLevelIcon.value =
+                (signalLevelIcon.value as SignalIconModel.Cellular).copy(level = level)
+        }
+    }
+
+    companion object {
+        private val SUB_1 =
+            SubscriptionModel(
+                subscriptionId = 1,
+                isOpportunistic = false,
+                carrierName = "Carrier 1",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+        private val SUB_2 =
+            SubscriptionModel(
+                subscriptionId = 2,
+                isOpportunistic = false,
+                carrierName = "Carrier 2",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+        private val SUB_3 =
+            SubscriptionModel(
+                subscriptionId = 3,
+                isOpportunistic = false,
+                carrierName = "Carrier 3",
+                profileClass = PROFILE_CLASS_UNSET,
+            )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index 91b3896..39cf02d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -54,9 +54,7 @@
         MutableStateFlow(OngoingActivityChipModel.Inactive())
 
     override val ongoingActivityChips =
-        MutableStateFlow(
-            ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
-        )
+        ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
 
     override val ongoingActivityChipsLegacy =
         MutableStateFlow(MultipleOngoingActivityChipsModelLegacy())
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 12cf3b6..20cf3ae 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
@@ -49,6 +49,7 @@
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.log.assertLogsWtf
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -107,7 +108,8 @@
 @RunWith(AndroidJUnit4::class)
 class HomeStatusBarViewModelImplTest : SysuiTestCase() {
     private val kosmos = testKosmos().useUnconfinedTestDispatcher()
-    private val Kosmos.underTest by Kosmos.Fixture { kosmos.homeStatusBarViewModel }
+    private val Kosmos.underTest by
+        Kosmos.Fixture { kosmos.homeStatusBarViewModel.also { it.activateIn(kosmos.testScope) } }
 
     @Before
     fun setUp() {
@@ -891,32 +893,26 @@
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarHidden_noSecureCamera_noHun_notAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             // home status bar not allowed
             kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
             kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, taskInfo = null)
 
-            assertThat(latest!!.areChipsAllowed).isFalse()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
         }
 
     @Test
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_noHun_isAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             transitionKeyguardToGone()
 
-            assertThat(latest!!.areChipsAllowed).isTrue()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
         }
 
     @Test
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarNotHidden_secureCamera_noHun_notAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             fakeKeyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.OCCLUDED,
@@ -924,7 +920,7 @@
             )
             kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
 
-            assertThat(latest!!.areChipsAllowed).isFalse()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
         }
 
     @Test
@@ -932,8 +928,6 @@
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOff_notAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             transitionKeyguardToGone()
 
             headsUpNotificationRepository.setNotifications(
@@ -943,7 +937,7 @@
                 )
             )
 
-            assertThat(latest!!.areChipsAllowed).isFalse()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isFalse()
         }
 
     @Test
@@ -951,8 +945,6 @@
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOff_isAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             transitionKeyguardToGone()
 
             headsUpNotificationRepository.setNotifications(
@@ -962,16 +954,14 @@
                 )
             )
 
-            assertThat(latest!!.areChipsAllowed).isTrue()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
         }
 
     @Test
     @EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
     @EnableChipsModernization
-    fun ongoingActivityChips_tatusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
+    fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             transitionKeyguardToGone()
 
             headsUpNotificationRepository.setNotifications(
@@ -981,7 +971,7 @@
                 )
             )
 
-            assertThat(latest!!.areChipsAllowed).isTrue()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
         }
 
     @Test
@@ -989,8 +979,6 @@
     @EnableChipsModernization
     fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOn_isAllowed() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
-
             transitionKeyguardToGone()
 
             headsUpNotificationRepository.setNotifications(
@@ -1000,7 +988,7 @@
                 )
             )
 
-            assertThat(latest!!.areChipsAllowed).isTrue()
+            assertThat(underTest.ongoingActivityChips.areChipsAllowed).isTrue()
         }
 
     @Test
@@ -1008,17 +996,16 @@
     @EnableChipsModernization
     fun ongoingActivityChips_followsChipsViewModel() =
         kosmos.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChips)
             transitionKeyguardToGone()
 
             screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
 
-            assertIsScreenRecordChip(latest!!.chips.active[0])
+            assertIsScreenRecordChip(underTest.ongoingActivityChips.chips.active[0])
 
             addOngoingCallState(key = "call")
 
-            assertIsScreenRecordChip(latest!!.chips.active[0])
-            assertIsCallChip(latest!!.chips.active[1], "call", context)
+            assertIsScreenRecordChip(underTest.ongoingActivityChips.chips.active[0])
+            assertIsCallChip(underTest.ongoingActivityChips.chips.active[1], "call", context)
         }
 
     @Test
@@ -1513,7 +1500,39 @@
 
     @Test
     @DisableSceneContainer
-    fun shadeShown_sceneFlagOff_noStatusBarViewsShown() =
+    fun shadeSlightlyShown_sceneFlagOff_statusBarViewsShown() =
+        kosmos.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
+            transitionKeyguardToGone()
+
+            kosmos.shadeTestUtil.setShadeExpansion(0.1f)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun shadeHalfShown_sceneFlagOff_noStatusBarViewsShown() =
+        kosmos.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
+            transitionKeyguardToGone()
+
+            kosmos.shadeTestUtil.setShadeExpansion(0.5f)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun shadeFullyShown_sceneFlagOff_noStatusBarViewsShown() =
         kosmos.runTest {
             val clockVisible by collectLastValue(underTest.isClockVisible)
             val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
@@ -1527,6 +1546,83 @@
             assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
         }
 
+    /** Regression test for b/394257529#comment24. */
+    @Test
+    @DisableSceneContainer
+    fun qqsToQsTransition_sceneFlagOff_statusBarViewsNeverShown() =
+        kosmos.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
+            transitionKeyguardToGone()
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 1f, qsExpansion = 0f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0.9f, qsExpansion = 0.1f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0.6f, qsExpansion = 0.4f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0.5f, qsExpansion = 0.5f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0.2f, qsExpansion = 0.8f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0f, qsExpansion = 1f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+        }
+
+    /** Regression test for b/394257529#comment24. */
+    @Test
+    @DisableSceneContainer
+    fun qsToQqsTransition_sceneFlagOff_statusBarViewsNeverShown() =
+        kosmos.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis)
+            transitionKeyguardToGone()
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0f, qsExpansion = 1f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0.3f, qsExpansion = 0.7f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0.5f, qsExpansion = 0.5f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0.7f, qsExpansion = 0.3f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+
+            kosmos.shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 1f, qsExpansion = 0f)
+            assertThat(clockVisible!!.visibility).isEqualTo(View.INVISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE)
+        }
+
     @Test
     @EnableSceneContainer
     fun shadeShown_sceneFlagOn_noStatusBarViewsShown() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 9a0b812..b8be343 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -282,6 +282,33 @@
 
         assertThat(mThemeOverlayController.mThemeStyle).isEqualTo(Style.TONAL_SPOT);
     }
+    @Test
+    @HardwareColors(color = "BLK", options = {
+            "BLK|MONOCHROMATIC|#FF0000",
+            "*|VIBRANT|home_wallpaper"
+    })
+    @EnableFlags(com.android.systemui.Flags.FLAG_HARDWARE_COLOR_STYLES)
+    public void start_checkHardwareColor_storeInSecureSetting() {
+        // getWallpaperColors should not be called
+        ArgumentCaptor<Runnable> registrationRunnable = ArgumentCaptor.forClass(Runnable.class);
+        verify(mMainExecutor).execute(registrationRunnable.capture());
+        registrationRunnable.getValue().run();
+        verify(mWallpaperManager, never()).getWallpaperColors(anyInt());
+
+        assertThat(mThemeOverlayController.mThemeStyle).isEqualTo(Style.MONOCHROMATIC);
+        assertThat(mThemeOverlayController.mCurrentColors.get(0).getMainColors().get(
+                0).toArgb()).isEqualTo(Color.RED);
+
+        ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+        verify(mSecureSettings).putStringForUser(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
+                anyInt());
+
+        assertThat(updatedSetting.getValue().contains(
+                "android.theme.customization.theme_style\":\"MONOCHROMATIC")).isTrue();
+        assertThat(updatedSetting.getValue().contains(
+                "android.theme.customization.system_palette\":\"ffff0000")).isTrue();
+    }
 
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
new file mode 100644
index 0000000..6d3813c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects
+
+import android.view.View
+import android.view.WindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
+import com.android.systemui.keyevent.data.repository.keyEventRepository
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyevent.domain.interactor.keyEventInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.android.systemui.topwindoweffects.data.repository.fakeSqueezeEffectRepository
+import com.android.systemui.topwindoweffects.domain.interactor.SqueezeEffectInteractor
+import com.android.systemui.topwindoweffects.ui.compose.EffectsWindowRoot
+import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TopLevelWindowEffectsTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+    @Mock
+    private lateinit var windowManager: WindowManager
+
+    @Mock
+    private lateinit var viewCapture: Lazy<ViewCapture>
+
+    @Mock
+    private lateinit var viewModelFactory: SqueezeEffectViewModel.Factory
+
+    private val Kosmos.underTest by Kosmos.Fixture {
+        TopLevelWindowEffects(
+            context = mContext,
+            applicationScope = testScope.backgroundScope,
+            windowManager = ViewCaptureAwareWindowManager(
+                windowManager = windowManager,
+                lazyViewCapture = viewCapture,
+                isViewCaptureEnabled = false
+            ),
+            keyEventInteractor = keyEventInteractor,
+            viewModelFactory = viewModelFactory,
+            squeezeEffectInteractor = SqueezeEffectInteractor(
+                squeezeEffectRepository = fakeSqueezeEffectRepository
+            )
+        )
+    }
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        doNothing().whenever(windowManager).addView(any<View>(), any<WindowManager.LayoutParams>())
+        doNothing().whenever(windowManager).removeView(any<View>())
+        doNothing().whenever(windowManager).removeView(any<EffectsWindowRoot>())
+    }
+
+    @Test
+    fun noWindowWhenSqueezeEffectDisabled() =
+        kosmos.runTest {
+            fakeSqueezeEffectRepository.isSqueezeEffectEnabled.value = false
+
+            underTest.start()
+
+            verify(windowManager, never()).addView(any<View>(), any<WindowManager.LayoutParams>())
+        }
+
+    @Test
+    fun addViewToWindowWhenSqueezeEffectEnabled() =
+        kosmos.runTest {
+            fakeSqueezeEffectRepository.isSqueezeEffectEnabled.value = true
+            fakeKeyEventRepository.setPowerButtonDown(true)
+
+            underTest.start()
+
+            verify(windowManager, times(1)).addView(any<View>(),
+                any<WindowManager.LayoutParams>())
+        }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryTest.kt
new file mode 100644
index 0000000..9b01fd3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects.data.repository
+
+import android.os.Handler
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.provider.Settings.Global.POWER_BUTTON_LONG_PRESS
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.shared.Flags
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.FakeGlobalSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+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)
+class SqueezeEffectRepositoryTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val globalSettings = FakeGlobalSettings(StandardTestDispatcher())
+
+    @Mock
+    private lateinit var bgHandler: Handler
+
+    private val Kosmos.underTest by Kosmos.Fixture {
+        SqueezeEffectRepositoryImpl(
+            bgHandler = bgHandler,
+            bgCoroutineContext = testScope.testScheduler,
+            globalSettings = globalSettings
+        )
+    }
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @DisableFlags(Flags.FLAG_ENABLE_LPP_SQUEEZE_EFFECT)
+    @Test
+    fun testSqueezeEffectDisabled_WhenFlagDisabled() =
+        kosmos.runTest {
+            val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled)
+
+            assertThat(isSqueezeEffectEnabled).isFalse()
+        }
+
+    @EnableFlags(Flags.FLAG_ENABLE_LPP_SQUEEZE_EFFECT)
+    @Test
+    fun testSqueezeEffectDisabled_WhenFlagEnabled_GlobalSettingsDisabled() =
+        kosmos.runTest {
+            globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 0)
+
+            val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled)
+
+            assertThat(isSqueezeEffectEnabled).isFalse()
+        }
+
+    @EnableFlags(Flags.FLAG_ENABLE_LPP_SQUEEZE_EFFECT)
+    @Test
+    fun testSqueezeEffectEnabled_WhenFlagEnabled_GlobalSettingEnabled() =
+        kosmos.runTest {
+            globalSettings.putInt(POWER_BUTTON_LONG_PRESS, 5)
+
+            val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled)
+
+            assertThat(isSqueezeEffectEnabled).isTrue()
+        }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/domain/interactor/SqueezeEffectInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/domain/interactor/SqueezeEffectInteractorTest.kt
new file mode 100644
index 0000000..a94d49c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/domain/interactor/SqueezeEffectInteractorTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.android.systemui.topwindoweffects.data.repository.fakeSqueezeEffectRepository
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SqueezeEffectInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+
+    private val Kosmos.underTest by Kosmos.Fixture {
+        SqueezeEffectInteractor(
+            squeezeEffectRepository = fakeSqueezeEffectRepository
+        )
+    }
+
+    @Test
+    fun testIsSqueezeEffectDisabled_whenDisabledInRepository() =
+        kosmos.runTest {
+            fakeSqueezeEffectRepository.isSqueezeEffectEnabled.value = false
+
+            val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled)
+
+            assertThat(isSqueezeEffectEnabled).isFalse()
+        }
+
+    @Test
+    fun testIsSqueezeEffectEnabled_whenEnabledInRepository() =
+        kosmos.runTest {
+            fakeSqueezeEffectRepository.isSqueezeEffectEnabled.value = true
+
+            val isSqueezeEffectEnabled by collectLastValue(underTest.isSqueezeEffectEnabled)
+
+            assertThat(isSqueezeEffectEnabled).isTrue()
+        }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 92bec70..18a124c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -38,7 +38,6 @@
 import com.android.systemui.display.data.repository.fakeDeviceStateRepository
 import com.android.systemui.foldedDeviceStateList
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setScreenPowerState
@@ -47,6 +46,7 @@
 import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
 import com.android.systemui.shared.system.SysUiStatsLog
 import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.testKosmos
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.COOL_DOWN_DURATION
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_HALF_OPEN
@@ -89,7 +89,7 @@
     private lateinit var displaySwitchLatencyTracker: DisplaySwitchLatencyTracker
     @Captor private lateinit var loggerArgumentCaptor: ArgumentCaptor<DisplaySwitchLatencyEvent>
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val mockContext = mock<Context>()
     private val resources = mock<Resources>()
     private val foldStateRepository = kosmos.fakeDeviceStateRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
index dfc4d0f..98d99f7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.animation.AnimatorTestRule
 import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
 import com.android.systemui.display.data.repository.fakeDeviceStateRepository
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -36,6 +35,7 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.power.shared.model.ScreenPowerState
 import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.testKosmos
 import com.android.systemui.util.animation.data.repository.fakeAnimationStatusRepository
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceTimeBy
@@ -61,7 +61,7 @@
 class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
     @get:Rule val animatorTestRule = AnimatorTestRule(this)
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testScope: TestScope = kosmos.testScope
     private val fakeDeviceStateRepository = kosmos.fakeDeviceStateRepository
     private val powerInteractor = kosmos.powerInteractor
@@ -93,7 +93,7 @@
                 fakeAnimationStatusRepository,
                 mockControllerFactory,
                 mockFoldLockSettingAvailabilityProvider,
-                mockJankMonitor
+                mockJankMonitor,
             )
         foldLightRevealAnimation.init()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index bafa8cf..da5622a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -17,8 +17,10 @@
 
 package com.android.systemui.user.data.repository
 
+import android.app.admin.DevicePolicyManager
 import android.app.admin.devicePolicyManager
 import android.content.Intent
+import android.content.applicationContext
 import android.content.pm.UserInfo
 import android.internal.statusbar.fakeStatusBarService
 import android.os.UserHandle
@@ -77,10 +79,7 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         tracker = FakeUserTracker()
-        context.orCreateTestableResources.addOverride(
-            R.bool.config_userSwitchingMustGoThroughLoginScreen,
-            false,
-        )
+        setUserSwitchingMustGoThroughLoginScreen(false)
     }
 
     @Test
@@ -308,6 +307,117 @@
             job.cancel()
         }
 
+    @Test
+    fun isSecondaryUserLogoutEnabled_secondaryLogoutDisabled_alwaysFalse() =
+        testScope.runTest {
+            underTest = create(testScope.backgroundScope)
+            mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
+            setSecondaryUserLogoutEnabled(false)
+            setUpUsers(count = 2, selectedIndex = 0)
+            tracker.onProfileChanged()
+
+            val secondaryUserLogoutEnabled by
+                collectLastValue(underTest.isSecondaryUserLogoutEnabled)
+
+            assertThat(secondaryUserLogoutEnabled).isFalse()
+
+            setUpUsers(count = 2, selectedIndex = 1)
+            tracker.onProfileChanged()
+            assertThat(secondaryUserLogoutEnabled).isFalse()
+        }
+
+    @Test
+    fun isSecondaryUserLogoutEnabled_secondaryLogoutEnabled_NullLogoutUser_alwaysFalse() =
+        testScope.runTest {
+            underTest = create(testScope.backgroundScope)
+            mockLogoutUser(LogoutUserResult.NONE)
+            setSecondaryUserLogoutEnabled(true)
+            setUpUsers(count = 2, selectedIndex = 0)
+            tracker.onProfileChanged()
+
+            val secondaryUserLogoutEnabled by
+                collectLastValue(underTest.isSecondaryUserLogoutEnabled)
+
+            assertThat(secondaryUserLogoutEnabled).isFalse()
+
+            setUpUsers(count = 2, selectedIndex = 1)
+            tracker.onProfileChanged()
+            assertThat(secondaryUserLogoutEnabled).isFalse()
+        }
+
+    @Test
+    fun isSecondaryUserLogoutEnabled_secondaryLogoutEnabled_NonSystemLogoutUser_trueWhenNonSystem() =
+        testScope.runTest {
+            underTest = create(testScope.backgroundScope)
+            mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
+            setSecondaryUserLogoutEnabled(true)
+            setUpUsers(count = 2, selectedIndex = 0)
+            tracker.onProfileChanged()
+
+            val secondaryUserLogoutEnabled by
+                collectLastValue(underTest.isSecondaryUserLogoutEnabled)
+
+            assertThat(secondaryUserLogoutEnabled).isFalse()
+
+            setUpUsers(count = 2, selectedIndex = 1)
+            tracker.onProfileChanged()
+            assertThat(secondaryUserLogoutEnabled).isTrue()
+        }
+
+    @Test
+    fun isLogoutToSystemUserEnabled_logoutThroughLoginScreenDisabled_alwaysFalse() =
+        testScope.runTest {
+            underTest = create(testScope.backgroundScope)
+            mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
+            setUserSwitchingMustGoThroughLoginScreen(false)
+            setUpUsers(count = 2, selectedIndex = 0)
+            tracker.onProfileChanged()
+
+            val logoutToSystemUserEnabled by collectLastValue(underTest.isLogoutToSystemUserEnabled)
+
+            assertThat(logoutToSystemUserEnabled).isFalse()
+
+            setUpUsers(count = 2, selectedIndex = 1)
+            tracker.onProfileChanged()
+            assertThat(logoutToSystemUserEnabled).isFalse()
+        }
+
+    @Test
+    fun isLogoutToSystemUserEnabled_logoutThroughLoginScreenEnabled_NullLogoutUser_alwaysFalse() =
+        testScope.runTest {
+            underTest = create(testScope.backgroundScope)
+            mockLogoutUser(LogoutUserResult.NONE)
+            setUserSwitchingMustGoThroughLoginScreen(true)
+            setUpUsers(count = 2, selectedIndex = 0)
+            tracker.onProfileChanged()
+
+            val logoutToSystemUserEnabled by collectLastValue(underTest.isLogoutToSystemUserEnabled)
+
+            assertThat(logoutToSystemUserEnabled).isFalse()
+
+            setUpUsers(count = 2, selectedIndex = 1)
+            tracker.onProfileChanged()
+            assertThat(logoutToSystemUserEnabled).isFalse()
+        }
+
+    @Test
+    fun isLogoutToSystemUserEnabled_logoutThroughLoginScreenEnabled_NonSystemLogoutUser_trueWhenNonSystem() =
+        testScope.runTest {
+            underTest = create(testScope.backgroundScope)
+            mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
+            setUserSwitchingMustGoThroughLoginScreen(true)
+            setUpUsers(count = 2, selectedIndex = 0)
+            tracker.onProfileChanged()
+
+            val logoutToSystemUserEnabled by collectLastValue(underTest.isLogoutToSystemUserEnabled)
+
+            assertThat(logoutToSystemUserEnabled).isFalse()
+
+            setUpUsers(count = 2, selectedIndex = 1)
+            tracker.onProfileChanged()
+            assertThat(logoutToSystemUserEnabled).isTrue()
+        }
+
     private fun createUserInfo(id: Int, isGuest: Boolean): UserInfo {
         val flags = 0
         return UserInfo(
@@ -354,6 +464,38 @@
         assertThat(model.isUserSwitcherEnabled).isEqualTo(expectedUserSwitcherEnabled)
     }
 
+    private fun setSecondaryUserLogoutEnabled(enabled: Boolean) {
+        whenever(devicePolicyManager.isLogoutEnabled).thenReturn(enabled)
+        broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+            kosmos.applicationContext,
+            Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+        )
+    }
+
+    private fun setUserSwitchingMustGoThroughLoginScreen(enabled: Boolean) {
+        context.orCreateTestableResources.addOverride(
+            R.bool.config_userSwitchingMustGoThroughLoginScreen,
+            enabled,
+        )
+    }
+
+    private fun mockLogoutUser(result: LogoutUserResult) {
+        when (result) {
+            LogoutUserResult.NONE -> {
+                whenever(devicePolicyManager.logoutUser).thenReturn(null)
+            }
+            LogoutUserResult.NON_SYSTEM_CURRENT -> {
+                whenever(devicePolicyManager.logoutUser).thenAnswer {
+                    if (tracker.userHandle != UserHandle.SYSTEM) {
+                        tracker.userHandle
+                    } else {
+                        null
+                    }
+                }
+            }
+        }
+    }
+
     private fun create(scope: CoroutineScope): UserRepositoryImpl {
         return UserRepositoryImpl(
             appContext = context,
@@ -373,4 +515,9 @@
     companion object {
         @JvmStatic private val IMMEDIATE = Dispatchers.Main.immediate
     }
+
+    private enum class LogoutUserResult {
+        NONE,
+        NON_SYSTEM_CURRENT,
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
index b4ba41a..a180d51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/UtilsTest.kt
@@ -27,7 +27,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.deviceStateManager
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import org.junit.Before
@@ -39,7 +39,7 @@
 @RunWith(AndroidJUnit4::class)
 class UtilsTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val deviceStateManager = kosmos.deviceStateManager
     private lateinit var testableResources: TestableResources
 
@@ -53,7 +53,7 @@
     fun isFoldableReturnsFalse_overlayConfigurationValues() {
         testableResources.addOverride(
             com.android.internal.R.array.config_foldedDeviceStates,
-            intArrayOf() // empty array <=> device is not foldable
+            intArrayOf(), // empty array <=> device is not foldable
         )
         whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE))
         assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
@@ -64,7 +64,7 @@
     fun isFoldableReturnsFalse_deviceStateManager() {
         testableResources.addOverride(
             com.android.internal.R.array.config_foldedDeviceStates,
-            intArrayOf() // empty array <=> device is not foldable
+            intArrayOf(), // empty array <=> device is not foldable
         )
         whenever(deviceStateManager.supportedDeviceStates).thenReturn(listOf(DEFAULT_DEVICE_STATE))
         assertFalse(Utils.isDeviceFoldable(testableResources.resources, deviceStateManager))
@@ -75,7 +75,7 @@
     fun isFoldableReturnsTrue_overlayConfigurationValues() {
         testableResources.addOverride(
             com.android.internal.R.array.config_foldedDeviceStates,
-            intArrayOf(FOLDED_DEVICE_STATE.identifier)
+            intArrayOf(FOLDED_DEVICE_STATE.identifier),
         )
         whenever(deviceStateManager.supportedDeviceStates)
             .thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE))
@@ -87,7 +87,7 @@
     fun isFoldableReturnsTrue_deviceStateManager() {
         testableResources.addOverride(
             com.android.internal.R.array.config_foldedDeviceStates,
-            intArrayOf(FOLDED_DEVICE_STATE.identifier)
+            intArrayOf(FOLDED_DEVICE_STATE.identifier),
         )
         whenever(deviceStateManager.supportedDeviceStates)
             .thenReturn(listOf(FOLDED_DEVICE_STATE, UNFOLDED_DEVICE_STATE))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/ClientTrackingWakeLockTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/ClientTrackingWakeLockTest.kt
index fdfcdc4..9a58365 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/ClientTrackingWakeLockTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/ClientTrackingWakeLockTest.kt
@@ -16,14 +16,13 @@
 
 package com.android.systemui.util.wakelock
 
-import android.os.Build
 import android.os.PowerManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.assertLogsWtf
 import org.junit.After
 import org.junit.Assert
-import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -82,12 +81,11 @@
 
     @Test
     fun wakeLock_releasedTooManyTimes_stillReleased_noThrow() {
-        Assume.assumeFalse(Build.IS_ENG)
         mWakeLock.acquire(WHY)
         mWakeLock.acquire(WHY_2)
         mWakeLock.release(WHY)
         mWakeLock.release(WHY_2)
-        mWakeLock.release(WHY)
+        assertLogsWtf { mWakeLock.release(WHY) }
         Assert.assertFalse(mInner.isHeld)
     }
 
@@ -104,9 +102,8 @@
 
     @Test
     fun prodBuild_wakeLock_releaseWithoutAcquire_noThrow() {
-        Assume.assumeFalse(Build.IS_ENG)
         // shouldn't throw an exception on production builds
-        mWakeLock.release(WHY)
+        assertLogsWtf { mWakeLock.release(WHY) }
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index 90aecfb..3951670 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -19,16 +19,15 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.os.Build;
 import android.os.PowerManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.log.LogAssertKt;
 
 import org.junit.After;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -93,8 +92,7 @@
 
     @Test
     public void prodBuild_wakeLock_releaseWithoutAcquire_noThrow() {
-        Assume.assumeFalse(Build.IS_ENG);
         // shouldn't throw an exception on production builds
-        mWakeLock.release(WHY);
+        LogAssertKt.assertRunnableLogsWtf(() -> mWakeLock.release(WHY));
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
index f232d52..93e5721 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
@@ -20,8 +20,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.volume.panel.domain.availableCriteria
 import com.android.systemui.volume.panel.domain.defaultCriteria
 import com.android.systemui.volume.panel.domain.model.ComponentModel
@@ -39,7 +39,7 @@
 @RunWith(AndroidJUnit4::class)
 class ComponentsInteractorImplTest : SysuiTestCase() {
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     private lateinit var underTest: ComponentsInteractor
 
@@ -60,12 +60,7 @@
     fun componentsAvailability_checked() {
         with(kosmos) {
             testScope.runTest {
-                enabledComponents =
-                    setOf(
-                        BOTTOM_BAR,
-                        COMPONENT_1,
-                        COMPONENT_2,
-                    )
+                enabledComponents = setOf(BOTTOM_BAR, COMPONENT_1, COMPONENT_2)
                 criteriaByKey =
                     mapOf(
                         BOTTOM_BAR to Provider { availableCriteria },
@@ -90,12 +85,7 @@
     fun noCriteria_fallbackToDefaultCriteria() {
         with(kosmos) {
             testScope.runTest {
-                enabledComponents =
-                    setOf(
-                        BOTTOM_BAR,
-                        COMPONENT_1,
-                        COMPONENT_2,
-                    )
+                enabledComponents = setOf(BOTTOM_BAR, COMPONENT_1, COMPONENT_2)
                 criteriaByKey =
                     mapOf(
                         BOTTOM_BAR to Provider { availableCriteria },
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
index cc6a7b9..a0f1a5c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.WallpaperInfo
 import android.app.WallpaperManager
+import android.app.WallpaperManager.FLAG_LOCK
 import android.content.ComponentName
 import android.content.Intent
 import android.content.pm.UserInfo
@@ -29,6 +30,7 @@
 import com.android.internal.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R as SysUIR
@@ -44,6 +46,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.kotlin.any
 import org.mockito.kotlin.doAnswer
 import org.mockito.kotlin.mock
@@ -64,6 +67,7 @@
     private val testScope = kosmos.testScope
     private val userRepository = kosmos.fakeUserRepository
     private val broadcastDispatcher = kosmos.broadcastDispatcher
+    private val configRepository = kosmos.fakeConfigurationRepository
 
     // Initialized in each test since certain flows rely on mocked data that isn't
     // modifiable after start, like wallpaperManager.isWallpaperSupported
@@ -251,10 +255,18 @@
             secureSettings.putInt(Settings.Secure.DOZE_ALWAYS_ON_WALLPAPER_ENABLED, 1)
             context.orCreateTestableResources.addOverride(
                 R.bool.config_dozeSupportsAodWallpaper,
+                false,
+            )
+            configRepository.onAnyConfigurationChange()
+            val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
+            assertThat(latest).isFalse()
+
+            // Validate that a configuration change recalculates the flow
+            context.orCreateTestableResources.addOverride(
+                R.bool.config_dozeSupportsAodWallpaper,
                 true,
             )
-
-            val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
+            configRepository.onAnyConfigurationChange()
             assertThat(latest).isTrue()
         }
 
@@ -275,17 +287,17 @@
 
     @Test
     @EnableFlags(SharedFlags.FLAG_EXTENDED_WALLPAPER_EFFECTS)
-    fun shouldSendNotificationLayout_setExtendedEffectsWallpaper_launchSendLayoutJob() =
+    fun shouldSendNotificationLayout_setExtendedEffectsWallpaper() =
         testScope.runTest {
             underTest = kosmos.wallpaperRepository
             val latest by collectLastValue(underTest.shouldSendFocalArea)
-            val extedendEffectsWallpaper =
+            val extendedEffectsWallpaper =
                 mock<WallpaperInfo>().apply {
                     whenever(this.component).thenReturn(ComponentName(context, focalAreaTarget))
                 }
 
             whenever(kosmos.wallpaperManager.getWallpaperInfoForUser(any()))
-                .thenReturn(extedendEffectsWallpaper)
+                .thenReturn(extendedEffectsWallpaper)
             broadcastDispatcher.sendIntentToMatchingReceiversOnly(
                 context,
                 Intent(Intent.ACTION_WALLPAPER_CHANGED),
@@ -295,7 +307,7 @@
 
     @Test
     @EnableFlags(SharedFlags.FLAG_EXTENDED_WALLPAPER_EFFECTS)
-    fun shouldSendNotificationLayout_setNotExtendedEffectsWallpaper_cancelSendLayoutJob() =
+    fun shouldSendNotificationLayout_setNotExtendedEffectsWallpaper() =
         testScope.runTest {
             underTest = kosmos.wallpaperRepository
             val latest by collectLastValue(underTest.shouldSendFocalArea)
@@ -322,6 +334,51 @@
             assertThat(latest).isFalse()
         }
 
+    @Test
+    @EnableFlags(SharedFlags.FLAG_EXTENDED_WALLPAPER_EFFECTS)
+    fun shouldSendNotificationLayout_setExtendedEffectsWallpaperOnlyForHomescreen() =
+        testScope.runTest {
+            underTest = kosmos.wallpaperRepository
+            val latest by collectLastValue(underTest.shouldSendFocalArea)
+            val extendedEffectsWallpaper =
+                mock<WallpaperInfo>().apply {
+                    whenever(this.component).thenReturn(ComponentName("", focalAreaTarget))
+                }
+
+            whenever(kosmos.wallpaperManager.lockScreenWallpaperExists()).thenReturn(true)
+            whenever(kosmos.wallpaperManager.getWallpaperInfoForUser(any()))
+                .thenReturn(extendedEffectsWallpaper)
+            whenever(kosmos.wallpaperManager.getWallpaperInfo(eq(FLAG_LOCK), any()))
+                .thenReturn(UNSUPPORTED_WP)
+            broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(Intent.ACTION_WALLPAPER_CHANGED),
+            )
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    @EnableFlags(SharedFlags.FLAG_EXTENDED_WALLPAPER_EFFECTS)
+    fun shouldSendNotificationLayout_setExtendedEffectsWallpaperOnlyForLockscreen() =
+        testScope.runTest {
+            underTest = kosmos.wallpaperRepository
+            val latest by collectLastValue(underTest.shouldSendFocalArea)
+            val extendedEffectsWallpaper =
+                mock<WallpaperInfo>().apply {
+                    whenever(this.component).thenReturn(ComponentName("", focalAreaTarget))
+                }
+            whenever(kosmos.wallpaperManager.lockScreenWallpaperExists()).thenReturn(true)
+            whenever(kosmos.wallpaperManager.getWallpaperInfoForUser(any()))
+                .thenReturn(UNSUPPORTED_WP)
+            whenever(kosmos.wallpaperManager.getWallpaperInfo(eq(FLAG_LOCK), any()))
+                .thenReturn(extendedEffectsWallpaper)
+            broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(Intent.ACTION_WALLPAPER_CHANGED),
+            )
+            assertThat(latest).isTrue()
+        }
+
     private companion object {
         val USER_WITH_UNSUPPORTED_WP = UserInfo(/* id= */ 3, /* name= */ "user3", /* flags= */ 0)
         val UNSUPPORTED_WP =
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt
index b52db83..7657a21 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt
@@ -13,7 +13,6 @@
  */
 package com.android.systemui.plugins.clocks
 
-import android.graphics.RectF
 import com.android.systemui.plugins.annotations.ProtectedInterface
 import com.android.systemui.plugins.annotations.SimpleProperty
 import java.io.PrintWriter
@@ -50,5 +49,5 @@
 }
 
 interface ClockEventListener {
-    fun onBoundsChanged(bounds: RectF)
+    fun onBoundsChanged(bounds: VRectF)
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
index 235475f..0fc3470 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
@@ -44,5 +44,5 @@
     fun onZenDataChanged(data: ZenData)
 
     /** Update reactive axes for this clock */
-    fun onFontAxesChanged(axes: List<ClockFontAxisSetting>)
+    fun onFontAxesChanged(axes: ClockAxisStyle)
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt
index 20ee6c1..a658c15 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt
@@ -45,6 +45,7 @@
      * render within the centered targetRect to avoid obstructing other elements. The specified
      * targetRegion is relative to the parent view.
      */
+    @Deprecated("No longer necessary, pending removal")
     fun onTargetRegionChanged(targetRegion: Rect?)
 
     /** Called to notify the clock about its display. */
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
index 02a3902..5b67edd 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.plugins.clocks
 
-import android.graphics.Rect
 import android.view.View
 import android.view.View.MeasureSpec
 import com.android.systemui.log.core.LogLevel
@@ -56,12 +55,9 @@
     }
 
     fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
-        d({ "onLayout($bool1, ${Rect(int1, int2, long1.toInt(), long2.toInt())})" }) {
+        d({ "onLayout($bool1, ${VRect.fromLong(long1)})" }) {
             bool1 = changed
-            int1 = left
-            int2 = top
-            long1 = right.toLong()
-            long2 = bottom.toLong()
+            long1 = VRect(left, top, right, bottom).toLong()
         }
     }
 
@@ -108,8 +104,11 @@
         }
     }
 
-    fun animateDoze() {
-        d("animateDoze()")
+    fun animateDoze(isDozing: Boolean, isAnimated: Boolean) {
+        d({ "animateDoze(isDozing=$bool1, isAnimated=$bool2)" }) {
+            bool1 = isDozing
+            bool2 = isAnimated
+        }
     }
 
     fun animateCharge() {
@@ -117,10 +116,7 @@
     }
 
     fun animateFidget(x: Float, y: Float) {
-        d({ "animateFidget($str1, $str2)" }) {
-            str1 = x.toString()
-            str2 = y.toString()
-        }
+        d({ "animateFidget(${VPointF.fromLong(long1)})" }) { long1 = VPointF(x, y).toLong() }
     }
 
     companion object {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
index 0cbc30d..2147ca1 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt
@@ -35,10 +35,54 @@
     /** Font axes that can be modified on this clock */
     val axes: List<ClockFontAxis> = listOf(),
 
-    /** List of font presets for this clock. Can be assigned directly. */
-    val axisPresets: List<List<ClockFontAxisSetting>> = listOf(),
+    /** Presets for this clock. Null indicates the preset list should be disabled. */
+    val presetConfig: AxisPresetConfig? = null,
 )
 
+data class AxisPresetConfig(
+    /** Groups of Presets. Each group can be used together in a single control. */
+    val groups: List<Group>,
+
+    /** Preset item currently being used, null when the current style is not a preset */
+    val current: IndexedStyle? = null,
+) {
+    /** The selected clock axis style, and its indices */
+    data class IndexedStyle(
+        /** Index of the group that this clock axis style appears in */
+        val groupIndex: Int,
+
+        /** Index of the preset within the group */
+        val presetIndex: Int,
+
+        /** Reference to the style in question */
+        val style: ClockAxisStyle,
+    )
+
+    /** A group of preset styles */
+    data class Group(
+        /* List of preset styles in this group */
+        val presets: List<ClockAxisStyle>,
+
+        /* Icon to use when this preset-group is active */
+        val icon: Drawable,
+    )
+
+    fun findStyle(style: ClockAxisStyle): IndexedStyle? {
+        groups.forEachIndexed { groupIndex, group ->
+            group.presets.forEachIndexed { presetIndex, preset ->
+                if (preset == style) {
+                    return@findStyle IndexedStyle(
+                        groupIndex = groupIndex,
+                        presetIndex = presetIndex,
+                        style = preset,
+                    )
+                }
+            }
+        }
+        return null
+    }
+}
+
 /** Represents an Axis that can be modified */
 data class ClockFontAxis(
     /** Axis key, not user renderable */
@@ -62,19 +106,12 @@
     /** Description of the axis */
     val description: String,
 ) {
-    fun toSetting() = ClockFontAxisSetting(key, currentValue)
-
     companion object {
-        fun List<ClockFontAxis>.merge(
-            axisSettings: List<ClockFontAxisSetting>
-        ): List<ClockFontAxis> {
-            val result = mutableListOf<ClockFontAxis>()
-            for (axis in this) {
-                val setting = axisSettings.firstOrNull { axis.key == it.key }
-                val output = setting?.let { axis.copy(currentValue = it.value) } ?: axis
-                result.add(output)
-            }
-            return result
+        fun List<ClockFontAxis>.merge(axisStyle: ClockAxisStyle): List<ClockFontAxis> {
+            return this.map { axis ->
+                    axisStyle.get(axis.key)?.let { axis.copy(currentValue = it) } ?: axis
+                }
+                .toList()
         }
     }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
index e7b3662..cccc558 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt
@@ -22,7 +22,7 @@
 data class ClockSettings(
     val clockId: ClockId? = null,
     val seedColor: Int? = null,
-    val axes: List<ClockFontAxisSetting> = listOf(),
+    val axes: ClockAxisStyle = ClockAxisStyle(),
 ) {
     // Exclude metadata from equality checks
     var metadata: JSONObject = JSONObject()
@@ -38,15 +38,15 @@
                 put(KEY_CLOCK_ID, setting.clockId)
                 put(KEY_SEED_COLOR, setting.seedColor)
                 put(KEY_METADATA, setting.metadata)
-                put(KEY_AXIS_LIST, ClockFontAxisSetting.toJson(setting.axes))
+                put(KEY_AXIS_LIST, ClockAxisStyle.toJson(setting.axes))
             }
         }
 
         fun fromJson(json: JSONObject): ClockSettings {
             val clockId = if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null
             val seedColor = if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null
-            val axisList = json.optJSONArray(KEY_AXIS_LIST)?.let(ClockFontAxisSetting::fromJson)
-            return ClockSettings(clockId, seedColor, axisList ?: listOf()).apply {
+            val axisList = json.optJSONArray(KEY_AXIS_LIST)?.let(ClockAxisStyle::fromJson)
+            return ClockSettings(clockId, seedColor, axisList ?: ClockAxisStyle()).apply {
                 metadata = json.optJSONObject(KEY_METADATA) ?: JSONObject()
             }
         }
@@ -54,64 +54,102 @@
 }
 
 @Keep
-/** Axis setting value for a clock */
-data class ClockFontAxisSetting(
-    /** Axis key; matches ClockFontAxis.key */
-    val key: String,
+class ClockAxisStyle {
+    private val settings: MutableMap<String, Float>
 
-    /** Value to set this axis to */
-    val value: Float,
-) {
+    // Iterable would be implemented on ClockAxisStyle directly,
+    // but that doesn't appear to work with plugins/dynamic libs.
+    val items: Iterable<Map.Entry<String, Float>>
+        get() = settings.asIterable()
+
+    val isEmpty: Boolean
+        get() = settings.isEmpty()
+
+    constructor(initialize: ClockAxisStyle.() -> Unit = {}) {
+        settings = mutableMapOf()
+        this.initialize()
+    }
+
+    constructor(style: ClockAxisStyle) {
+        settings = style.settings.toMutableMap()
+    }
+
+    constructor(items: Map<String, Float>) {
+        settings = items.toMutableMap()
+    }
+
+    constructor(key: String, value: Float) {
+        settings = mutableMapOf(key to value)
+    }
+
+    constructor(items: List<ClockFontAxis>) {
+        settings = items.associate { it.key to it.currentValue }.toMutableMap()
+    }
+
+    fun copy(initialize: ClockAxisStyle.() -> Unit): ClockAxisStyle {
+        return ClockAxisStyle(this).apply { initialize() }
+    }
+
+    operator fun get(key: String): Float? = settings[key]
+
+    operator fun set(key: String, value: Float) = put(key, value)
+
+    fun put(key: String, value: Float) {
+        settings.put(key, value)
+    }
+
+    fun toFVar(): String {
+        val sb = StringBuilder()
+        for (axis in settings) {
+            if (sb.length > 0) sb.append(", ")
+            sb.append("'${axis.key}' ${axis.value.toInt()}")
+        }
+        return sb.toString()
+    }
+
+    fun copyWith(replacements: ClockAxisStyle): ClockAxisStyle {
+        val result = ClockAxisStyle(this)
+        for ((key, value) in replacements.settings) {
+            result[key] = value
+        }
+        return result
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is ClockAxisStyle) return false
+        return settings == other.settings
+    }
+
     companion object {
         private val KEY_AXIS_KEY = "key"
         private val KEY_AXIS_VALUE = "value"
 
-        fun toJson(setting: ClockFontAxisSetting): JSONObject {
-            return JSONObject().apply {
-                put(KEY_AXIS_KEY, setting.key)
-                put(KEY_AXIS_VALUE, setting.value)
-            }
-        }
-
-        fun toJson(settings: List<ClockFontAxisSetting>): JSONArray {
-            return JSONArray().apply {
-                for (axis in settings) {
-                    put(toJson(axis))
-                }
-            }
-        }
-
-        fun fromJson(jsonObj: JSONObject): ClockFontAxisSetting {
-            return ClockFontAxisSetting(
-                key = jsonObj.getString(KEY_AXIS_KEY),
-                value = jsonObj.getDouble(KEY_AXIS_VALUE).toFloat(),
-            )
-        }
-
-        fun fromJson(jsonArray: JSONArray): List<ClockFontAxisSetting> {
-            val result = mutableListOf<ClockFontAxisSetting>()
+        fun fromJson(jsonArray: JSONArray): ClockAxisStyle {
+            val result = ClockAxisStyle()
             for (i in 0..jsonArray.length() - 1) {
                 val obj = jsonArray.getJSONObject(i)
                 if (obj == null) continue
-                result.add(fromJson(obj))
+
+                result.put(
+                    key = obj.getString(KEY_AXIS_KEY),
+                    value = obj.getDouble(KEY_AXIS_VALUE).toFloat(),
+                )
             }
             return result
         }
 
-        fun List<ClockFontAxisSetting>.toFVar(): String {
-            val sb = StringBuilder()
-            for (axis in this) {
-                if (sb.length > 0) sb.append(", ")
-                sb.append("'${axis.key}' ${axis.value.toInt()}")
+        fun toJson(style: ClockAxisStyle): JSONArray {
+            return JSONArray().apply {
+                for ((key, value) in style.settings) {
+                    put(
+                        JSONObject().apply {
+                            put(KEY_AXIS_KEY, key)
+                            put(KEY_AXIS_VALUE, value)
+                        }
+                    )
+                }
             }
-            return sb.toString()
-        }
-
-        fun List<ClockFontAxisSetting>.replace(
-            replacements: List<ClockFontAxisSetting>
-        ): List<ClockFontAxisSetting> {
-            var remaining = this.filterNot { lhs -> replacements.any { rhs -> lhs.key == rhs.key } }
-            return remaining + replacements
         }
     }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/VPoint.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VPoint.kt
similarity index 92%
rename from packages/SystemUI/customization/src/com/android/systemui/shared/clocks/VPoint.kt
rename to packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VPoint.kt
index 3dae530..de62f9c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/VPoint.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VPoint.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.shared.clocks
+package com.android.systemui.plugins.clocks
 
 import android.graphics.Point
 import android.graphics.PointF
@@ -30,22 +30,20 @@
 
 private fun unpackX(data: ULong): Int = ((data and X_MASK) shr 32).toInt()
 
-private fun unpackY(data: ULong): Int = (data and Y_MASK).toInt()
+private fun unpackY(data: ULong): Int = ((data and Y_MASK) shr 0).toInt()
 
 private fun pack(x: Int, y: Int): ULong {
-    return ((x.toULong() shl 32) and X_MASK) or (y.toULong() and Y_MASK)
+    return ((x.toULong() shl 32) and X_MASK) or ((y.toULong() shl 0) and Y_MASK)
 }
 
 @JvmInline
-value class VPointF(private val data: ULong) {
+value class VPointF(val data: ULong) {
     val x: Float
         get() = Float.fromBits(unpackX(data))
 
     val y: Float
         get() = Float.fromBits(unpackY(data))
 
-    constructor() : this(0f, 0f)
-
     constructor(pt: PointF) : this(pt.x, pt.y)
 
     constructor(x: Int, y: Int) : this(x.toFloat(), y.toFloat())
@@ -58,6 +56,8 @@
 
     fun toPointF() = PointF(x, y)
 
+    fun toLong(): Long = data.toLong()
+
     fun lengthSq(): Float = x * x + y * y
 
     fun length(): Float = sqrt(lengthSq())
@@ -112,6 +112,8 @@
     companion object {
         val ZERO = VPointF(0, 0)
 
+        fun fromLong(data: Long) = VPointF(data.toULong())
+
         fun max(lhs: VPointF, rhs: VPointF) = VPointF(max(lhs.x, rhs.x), max(lhs.y, rhs.y))
 
         fun min(lhs: VPointF, rhs: VPointF) = VPointF(min(lhs.x, rhs.x), min(lhs.y, rhs.y))
@@ -139,19 +141,19 @@
 }
 
 @JvmInline
-value class VPoint(private val data: ULong) {
+value class VPoint(val data: ULong) {
     val x: Int
         get() = unpackX(data)
 
     val y: Int
         get() = unpackY(data)
 
-    constructor() : this(0, 0)
-
     constructor(x: Int, y: Int) : this(pack(x, y))
 
     fun toPoint() = Point(x, y)
 
+    fun toLong(): Long = data.toLong()
+
     fun abs() = VPoint(abs(x), abs(y))
 
     operator fun component1(): Int = x
@@ -195,6 +197,8 @@
     companion object {
         val ZERO = VPoint(0, 0)
 
+        fun fromLong(data: Long) = VPoint(data.toULong())
+
         fun max(lhs: VPoint, rhs: VPoint) = VPoint(max(lhs.x, rhs.x), max(lhs.y, rhs.y))
 
         fun min(lhs: VPoint, rhs: VPoint) = VPoint(min(lhs.x, rhs.x), min(lhs.y, rhs.y))
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VRect.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VRect.kt
new file mode 100644
index 0000000..1bd29aa
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/VRect.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.plugins.clocks
+
+import android.graphics.Rect
+import android.graphics.RectF
+import android.util.Half
+
+private val LEFT_MASK: ULong = 0xFFFF000000000000U
+private val TOP_MASK: ULong = 0x0000FFFF00000000U
+private val RIGHT_MASK: ULong = 0x00000000FFFF0000U
+private val BOTTOM_MASK: ULong = 0x000000000000FFFFU
+
+private fun unpackLeft(data: ULong): Short = ((data and LEFT_MASK) shr 48).toShort()
+
+private fun unpackTop(data: ULong): Short = ((data and TOP_MASK) shr 32).toShort()
+
+private fun unpackRight(data: ULong): Short = ((data and RIGHT_MASK) shr 16).toShort()
+
+private fun unpackBottom(data: ULong): Short = ((data and BOTTOM_MASK) shr 0).toShort()
+
+private fun pack(left: Short, top: Short, right: Short, bottom: Short): ULong {
+    return ((left.toULong() shl 48) and LEFT_MASK) or
+        ((top.toULong() shl 32) and TOP_MASK) or
+        ((right.toULong() shl 16) and RIGHT_MASK) or
+        ((bottom.toULong() shl 0) and BOTTOM_MASK)
+}
+
+@JvmInline
+value class VRectF(val data: ULong) {
+    val left: Float
+        get() = fromBits(unpackLeft(data))
+
+    val top: Float
+        get() = fromBits(unpackTop(data))
+
+    val right: Float
+        get() = fromBits(unpackRight(data))
+
+    val bottom: Float
+        get() = fromBits(unpackBottom(data))
+
+    val width: Float
+        get() = right - left
+
+    val height: Float
+        get() = bottom - top
+
+    constructor(rect: RectF) : this(rect.left, rect.top, rect.right, rect.bottom)
+
+    constructor(
+        rect: Rect
+    ) : this(
+        left = rect.left.toFloat(),
+        top = rect.top.toFloat(),
+        right = rect.right.toFloat(),
+        bottom = rect.bottom.toFloat(),
+    )
+
+    constructor(
+        left: Float,
+        top: Float,
+        right: Float,
+        bottom: Float,
+    ) : this(pack(toBits(left), toBits(top), toBits(right), toBits(bottom)))
+
+    val center: VPointF
+        get() = VPointF(left, top) + size / 2f
+
+    val size: VPointF
+        get() = VPointF(width, height)
+
+    fun toRectF(): RectF = RectF(left, top, right, bottom)
+
+    fun toLong(): Long = data.toLong()
+
+    override fun toString() = "($left, $top) -> ($right, $bottom)"
+
+    companion object {
+        private fun toBits(value: Float): Short = Half.halfToShortBits(Half.toHalf(value))
+
+        private fun fromBits(value: Short): Float = Half.toFloat(Half.intBitsToHalf(value.toInt()))
+
+        fun fromLong(data: Long) = VRectF(data.toULong())
+
+        fun fromCenter(center: VPointF, size: VPointF): VRectF {
+            return VRectF(
+                center.x - size.x / 2,
+                center.y - size.y / 2,
+                center.x + size.x / 2,
+                center.y + size.y / 2,
+            )
+        }
+
+        fun fromTopLeft(pos: VPointF, size: VPointF): VRectF {
+            return VRectF(pos.x, pos.y, pos.x + size.x, pos.y + size.y)
+        }
+
+        val ZERO = VRectF(0f, 0f, 0f, 0f)
+    }
+}
+
+@JvmInline
+value class VRect(val data: ULong) {
+    val left: Int
+        get() = unpackLeft(data).toInt()
+
+    val top: Int
+        get() = unpackTop(data).toInt()
+
+    val right: Int
+        get() = unpackRight(data).toInt()
+
+    val bottom: Int
+        get() = unpackBottom(data).toInt()
+
+    val width: Int
+        get() = right - left
+
+    val height: Int
+        get() = bottom - top
+
+    constructor(
+        rect: Rect
+    ) : this(
+        left = rect.left.toShort(),
+        top = rect.top.toShort(),
+        right = rect.right.toShort(),
+        bottom = rect.bottom.toShort(),
+    )
+
+    constructor(
+        left: Int,
+        top: Int,
+        right: Int,
+        bottom: Int,
+    ) : this(
+        left = left.toShort(),
+        top = top.toShort(),
+        right = right.toShort(),
+        bottom = bottom.toShort(),
+    )
+
+    constructor(
+        left: Short,
+        top: Short,
+        right: Short,
+        bottom: Short,
+    ) : this(pack(left, top, right, bottom))
+
+    val center: VPoint
+        get() = VPoint(left, top) + size / 2
+
+    val size: VPoint
+        get() = VPoint(width, height)
+
+    fun toRect(): Rect = Rect(left, top, right, bottom)
+
+    fun toLong(): Long = data.toLong()
+
+    override fun toString() = "($left, $top) -> ($right, $bottom)"
+
+    companion object {
+        val ZERO = VRect(0, 0, 0, 0)
+
+        fun fromLong(data: Long) = VRect(data.toULong())
+
+        fun fromCenter(center: VPoint, size: VPoint): VRect {
+            return VRect(
+                (center.x - size.x / 2).toShort(),
+                (center.y - size.y / 2).toShort(),
+                (center.x + size.x / 2).toShort(),
+                (center.y + size.y / 2).toShort(),
+            )
+        }
+
+        fun fromTopLeft(pos: VPoint, size: VPoint): VRect {
+            return VRect(
+                pos.x.toShort(),
+                pos.y.toShort(),
+                (pos.x + size.x).toShort(),
+                (pos.y + size.y).toShort(),
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/TileDetailsViewModel.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/TileDetailsViewModel.kt
index be0362f..ac7a8574 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/TileDetailsViewModel.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/TileDetailsViewModel.kt
@@ -16,15 +16,13 @@
 
 package com.android.systemui.plugins.qs
 
-/**
- * The base view model class for rendering the Tile's TileDetailsView.
- */
-abstract class TileDetailsViewModel {
+/** The view model interface for rendering the Tile's TileDetailsView. */
+interface TileDetailsViewModel {
     // The callback when the settings button is clicked. Currently this is the same as the on tile
     // long press callback
-    abstract fun clickOnSettingsButton()
+    fun clickOnSettingsButton()
 
-    abstract fun getTitle(): String
+    val title: String
 
-    abstract fun getSubTitle(): String
+    val subTitle: String
 }
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 1a49bbb..0524b34 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -21,11 +21,11 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"‏أدخل رقم التعريف الشخصي (PIN)"</string>
-    <string name="keyguard_enter_pin" msgid="8114529922480276834">"أدخِل رقم التعريف الشخصي"</string>
+    <string name="keyguard_enter_pin" msgid="8114529922480276834">"يُرجى إدخال رقم التعريف الشخصي"</string>
     <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"أدخل النقش"</string>
     <string name="keyguard_enter_pattern" msgid="7616595160901084119">"ارسم النقش"</string>
     <string name="keyguard_enter_your_password" msgid="7225626204122735501">"أدخل كلمة المرور"</string>
-    <string name="keyguard_enter_password" msgid="6483623792371009758">"أدخِل كلمة المرور"</string>
+    <string name="keyguard_enter_password" msgid="6483623792371009758">"يُرجى إدخال كلمة المرور"</string>
     <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"بطاقة غير صالحة."</string>
     <string name="keyguard_charged" msgid="5478247181205188995">"اكتمل الشحن"</string>
     <string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن لاسلكيًا"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index 9eb89b6..cd63405 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -23,7 +23,7 @@
     <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"अपना पिन डालें"</string>
     <string name="keyguard_enter_pin" msgid="8114529922480276834">"पिन डालें"</string>
     <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"अपना पैटर्न डालें"</string>
-    <string name="keyguard_enter_pattern" msgid="7616595160901084119">"पैटर्न ड्रॉ करें"</string>
+    <string name="keyguard_enter_pattern" msgid="7616595160901084119">"पैटर्न बनाएं"</string>
     <string name="keyguard_enter_your_password" msgid="7225626204122735501">"अपना पासवर्ड डालें"</string>
     <string name="keyguard_enter_password" msgid="6483623792371009758">"पासवर्ड डालें"</string>
     <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"गलत कार्ड."</string>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 609a517..0c844ab 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -23,7 +23,7 @@
     <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"Inserisci il PIN"</string>
     <string name="keyguard_enter_pin" msgid="8114529922480276834">"Inserisci il PIN"</string>
     <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Inserisci la sequenza"</string>
-    <string name="keyguard_enter_pattern" msgid="7616595160901084119">"Inserisci la sequenza"</string>
+    <string name="keyguard_enter_pattern" msgid="7616595160901084119">"Traccia la sequenza"</string>
     <string name="keyguard_enter_your_password" msgid="7225626204122735501">"Inserisci la password"</string>
     <string name="keyguard_enter_password" msgid="6483623792371009758">"Inserisci la password"</string>
     <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Scheda non valida."</string>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index f014c29..67ee3ed 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -23,7 +23,7 @@
     <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"מה קוד האימות שלך?"</string>
     <string name="keyguard_enter_pin" msgid="8114529922480276834">"צריך להזין קוד אימות"</string>
     <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"יש להזין קו ביטול נעילה"</string>
-    <string name="keyguard_enter_pattern" msgid="7616595160901084119">"צריך לצייר קו ביטול נעילה"</string>
+    <string name="keyguard_enter_pattern" msgid="7616595160901084119">"צריך לצייר את קו פתיחת הנעילה"</string>
     <string name="keyguard_enter_your_password" msgid="7225626204122735501">"יש להזין סיסמה"</string>
     <string name="keyguard_enter_password" msgid="6483623792371009758">"צריך להזין סיסמה"</string>
     <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"כרטיס לא חוקי."</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index 6b08a3a4..dbfc494 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -62,7 +62,7 @@
     <string name="kg_bio_try_again_or_pin" msgid="4752168242723808390">"请重试,或输入 PIN 码"</string>
     <string name="kg_bio_try_again_or_password" msgid="1473132729225398039">"请重试,或输入密码"</string>
     <string name="kg_bio_try_again_or_pattern" msgid="4867893307468801501">"请重试,或绘制解锁图案"</string>
-    <string name="kg_bio_too_many_attempts_pin" msgid="5850845723433047605">"如果出错的尝试次数太多,必须输入 PIN 码才能解锁"</string>
+    <string name="kg_bio_too_many_attempts_pin" msgid="5850845723433047605">"如果错误次数太多,则必须输入 PIN 码才能解锁"</string>
     <string name="kg_bio_too_many_attempts_password" msgid="5551690347827728042">"如果多次尝试失败,必须输入密码才能解锁"</string>
     <string name="kg_bio_too_many_attempts_pattern" msgid="736884689355181602">"如果出错的尝试次数太多,必须绘制图案才能解锁"</string>
     <string name="kg_unlock_with_pin_or_fp" msgid="5635161174698729890">"请使用 PIN 码或指纹解锁"</string>
diff --git a/packages/SystemUI/res/color/brightness_slider_track.xml b/packages/SystemUI/res/color/brightness_slider_track.xml
new file mode 100644
index 0000000..c6e9b65
--- /dev/null
+++ b/packages/SystemUI/res/color/brightness_slider_track.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral2_500" android:lStar="40" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index 88d3ecb..d38da7b 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
@@ -25,7 +25,7 @@
             <shape>
                 <size android:height="@dimen/rounded_slider_track_width" />
                 <corners android:radius="@dimen/rounded_slider_track_corner_radius" />
-                <solid android:color="@androidprv:color/customColorShadeInactive" />
+                <solid android:color="@color/brightness_slider_track" />
             </shape>
         </inset>
     </item>
diff --git a/packages/SystemUI/res/drawable/notification_2025_smart_reply_button_background.xml b/packages/SystemUI/res/drawable/notification_2025_smart_reply_button_background.xml
new file mode 100644
index 0000000..84e228e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notification_2025_smart_reply_button_background.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@color/notification_ripple_untinted_color">
+    <item>
+        <inset
+            android:insetLeft="0dp"
+            android:insetTop="6dp"
+            android:insetRight="0dp"
+            android:insetBottom="6dp">
+            <shape android:shape="rectangle">
+              <corners android:radius="@dimen/notification_2025_smart_reply_button_corner_radius" />
+                <stroke android:width="@dimen/smart_reply_button_stroke_width"
+                        android:color="@color/smart_reply_button_stroke" />
+                <solid android:color="@color/smart_reply_button_background"/>
+            </shape>
+        </inset>
+    </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml b/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
index 73704f8..f17cc96 100644
--- a/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
+++ b/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
@@ -21,7 +21,7 @@
         android:height="2dp"
         android:width="@dimen/rear_display_progress_width">
         <shape android:shape="rectangle">
-            <solid android:color="@androidprv:color/materialColorSurfaceContainer" />
+            <solid android:color="?android:attr/colorAccent"/>
         </shape>
     </item>
     <item
@@ -29,4 +29,4 @@
         android:gravity="center_vertical|fill_horizontal">
         <com.android.systemui.util.RoundedCornerProgressDrawable android:drawable="@drawable/rear_display_dialog_seekbar_progress" />
     </item>
-</layer-list>
\ No newline at end of file
+</layer-list>
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml
index 124aec6a..da2ec43 100644
--- a/packages/SystemUI/res/layout/bluetooth_device_item.xml
+++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml
@@ -52,7 +52,7 @@
         android:gravity="center_vertical"
         android:textSize="14sp" />
 
-    <TextView
+    <com.android.systemui.util.DelayableMarqueeTextView
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:id="@+id/bluetooth_device_summary"
@@ -60,7 +60,9 @@
         android:paddingEnd="10dp"
         android:paddingBottom="15dp"
         android:maxLines="1"
-        android:ellipsize="end"
+        android:ellipsize="marquee"
+        android:marqueeRepeatLimit="1"
+        android:singleLine="true"
         app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name"
         app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
         app:layout_constraintEnd_toStartOf="@+id/guideline"
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 915563b..c7add16 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -85,6 +85,7 @@
         android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
         android:elevation="4dp"
         android:scrollbars="none"
+        android:importantForAccessibility="no"
         app:layout_constraintHorizontal_bias="0"
         app:layout_constraintWidth_percent="1.0"
         app:layout_constraintWidth_max="wrap"
@@ -176,6 +177,8 @@
         app:layout_constraintStart_toStartOf="parent"
         android:layout_marginStart="4dp"
         android:layout_marginBottom="2dp"
+        android:importantForAccessibility="yes"
+        android:contentDescription="@string/clipboard_overlay_window_name"
         android:background="@drawable/clipboard_minimized_background_inset">
         <ImageView
             android:src="@drawable/ic_content_paste"
diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
index 949a6ab..a1b26fc 100644
--- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
@@ -85,13 +85,49 @@
             android:longClickable="false"/>
     </LinearLayout>
 
+    <LinearLayout
+        android:id="@+id/input_routing_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/preset_layout"
+        android:layout_marginTop="@dimen/hearing_devices_layout_margin"
+        android:orientation="vertical"
+        android:visibility="gone">
+        <TextView
+            android:id="@+id/input_routing_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin"
+            android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin"
+            android:paddingStart="@dimen/hearing_devices_small_title_padding_horizontal"
+            android:text="@string/hearing_devices_input_routing_label"
+            android:textAppearance="@style/TextAppearance.Dialog.Title"
+            android:textSize="14sp"
+            android:gravity="center_vertical"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+            android:textDirection="locale"/>
+        <Spinner
+            android:id="@+id/input_routing_spinner"
+            style="@style/BluetoothTileDialog.Device"
+            android:layout_height="@dimen/bluetooth_dialog_device_height"
+            android:layout_marginTop="4dp"
+            android:paddingStart="0dp"
+            android:paddingEnd="0dp"
+            android:background="@drawable/hearing_devices_spinner_background"
+            android:popupBackground="@drawable/hearing_devices_spinner_popup_background"
+            android:dropDownWidth="match_parent"
+            android:longClickable="false"/>
+    </LinearLayout>
+
     <com.android.systemui.accessibility.hearingaid.AmbientVolumeLayout
         android:id="@+id/ambient_layout"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/preset_layout"
+        app:layout_constraintTop_toBottomOf="@id/input_routing_layout"
         android:layout_marginTop="@dimen/hearing_devices_layout_margin" />
 
     <LinearLayout
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 6f8b4cd..9b629ac 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -15,8 +15,8 @@
   ~ limitations under the License.
   -->
 
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/media_output_dialog"
     android:layout_width="@dimen/large_dialog_width"
     android:layout_height="wrap_content"
@@ -35,24 +35,25 @@
         android:orientation="horizontal">
         <ImageView
             android:id="@+id/header_icon"
-            android:layout_width="72dp"
-            android:layout_height="72dp"
+            android:layout_width="@dimen/media_output_dialog_header_album_icon_size"
+            android:layout_height="@dimen/media_output_dialog_header_album_icon_size"
+            android:layout_marginEnd="@dimen/media_output_dialog_header_icon_padding"
             android:importantForAccessibility="no"/>
 
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingStart="12dp"
             android:orientation="vertical">
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:gravity="center_vertical"
+                android:gravity="top"
                 android:orientation="horizontal">
                 <ImageView
                     android:id="@+id/app_source_icon"
                     android:layout_width="20dp"
                     android:layout_height="20dp"
+                    android:layout_marginBottom="7dp"
                     android:gravity="center_vertical"
                     android:importantForAccessibility="no"/>
 
@@ -103,12 +104,11 @@
         android:layout_height="wrap_content" >
     </ViewStub>
 
-    <LinearLayout
+    <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/device_list"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:orientation="vertical">
+        android:layout_height="0dp"
+        android:layout_weight="1">
 
         <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/list_result"
@@ -116,8 +116,11 @@
             android:paddingTop="8dp"
             android:clipToPadding="false"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"/>
-    </LinearLayout>
+            android:layout_height="wrap_content"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintHeight_max="@dimen/media_output_dialog_list_max_height"/>
+    </androidx.constraintlayout.widget.ConstraintLayout>
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 9a66ca9..603cc00 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -71,15 +71,18 @@
                     android:textSize="16sp"
                 />
 
-                <Switch
-                    android:id="@+id/toggle"
+                <com.google.android.material.materialswitch.MaterialSwitch
+                    android:theme="@style/Theme.Material3.DynamicColors.DayNight"
+                    android:id="@+id/material_toggle"
+                    android:filterTouchesWhenObscured="false"
+                    android:clickable="true"
+                    android:focusable="true"
+                    android:padding="8dp"
                     android:layout_height="48dp"
                     android:layout_width="wrap_content"
                     android:layout_gravity="center_vertical"
-                    android:padding="8dp"
-                    android:track="@drawable/settingslib_track_selector"
-                    android:thumb="@drawable/settingslib_thumb_selector"
-                    android:theme="@style/MainSwitch.Settingslib"/>
+                    style="@style/SettingslibSwitchStyle.Expressive"/>
+
             </com.android.systemui.statusbar.notification.row.AppControlView>
 
             <ScrollView
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index b2eaa6c..4bc37f8 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -80,15 +80,16 @@
             />
         </RelativeLayout>
 
-        <Switch
-            android:id="@+id/toggle"
+        <com.google.android.material.materialswitch.MaterialSwitch
+            android:theme="@style/Theme.Material3.DynamicColors.DayNight"
+            android:id="@+id/material_toggle"
+            android:filterTouchesWhenObscured="false"
+            android:clickable="true"
+            android:focusable="true"
+            android:padding="8dp"
             android:layout_height="48dp"
             android:layout_width="wrap_content"
             android:layout_gravity="center_vertical"
-            android:padding="8dp"
-            android:track="@drawable/settingslib_track_selector"
-            android:thumb="@drawable/settingslib_thumb_selector"
-            android:theme="@style/MainSwitch.Settingslib"
-        />
+            style="@style/SettingslibSwitchStyle.Expressive"/>
     </LinearLayout>
 </com.android.systemui.statusbar.notification.row.ChannelRow>
diff --git a/packages/SystemUI/res/layout/notification_2025_info.xml b/packages/SystemUI/res/layout/notification_2025_info.xml
index 7b69166..fa852a2 100644
--- a/packages/SystemUI/res/layout/notification_2025_info.xml
+++ b/packages/SystemUI/res/layout/notification_2025_info.xml
@@ -18,6 +18,7 @@
 <!-- extends LinearLayout -->
 <com.android.systemui.statusbar.notification.row.NotificationInfo
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/notification_guts"
     android:layout_width="match_parent"
@@ -324,18 +325,34 @@
             </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
 
         </LinearLayout>
-
-        <LinearLayout
+        <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/bottom_buttons"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="@*android:dimen/notification_2025_margin"
             android:minHeight="@dimen/notification_2025_guts_button_size"
-            android:gravity="center_vertical"
-            >
+            android:gravity="center_vertical">
+
+            <TextView
+                android:id="@+id/inline_dismiss"
+                android:text="@string/notification_inline_dismiss"
+                android:paddingEnd="@dimen/notification_importance_button_padding"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingTop="8dp"
+                android:paddingBottom="@*android:dimen/notification_2025_margin"
+                app:layout_constraintStart_toStartOf="parent"
+                android:gravity="center"
+                android:minWidth="@dimen/notification_2025_min_tap_target_size"
+                android:minHeight="@dimen/notification_2025_min_tap_target_size"
+                android:maxWidth="200dp"
+                style="@style/TextAppearance.NotificationInfo.Button"
+                android:textSize="@*android:dimen/notification_2025_action_text_size"
+                />
             <TextView
                 android:id="@+id/turn_off_notifications"
                 android:text="@string/inline_turn_off_notifications"
+                android:paddingStart="@dimen/notification_importance_button_padding"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginEnd="32dp"
@@ -345,6 +362,8 @@
                 android:minWidth="@dimen/notification_2025_min_tap_target_size"
                 android:minHeight="@dimen/notification_2025_min_tap_target_size"
                 android:maxWidth="200dp"
+                app:layout_constraintStart_toEndOf="@id/inline_dismiss"
+                app:layout_constraintBaseline_toBaselineOf="@id/inline_dismiss"
                 style="@style/TextAppearance.NotificationInfo.Button"
                 android:textSize="@*android:dimen/notification_2025_action_text_size"/>
             <TextView
@@ -354,12 +373,18 @@
                 android:layout_height="wrap_content"
                 android:paddingTop="8dp"
                 android:paddingBottom="@*android:dimen/notification_2025_margin"
-                android:gravity="center"
+                android:gravity="end"
+                app:layout_constraintEnd_toEndOf="parent"
                 android:minWidth="@dimen/notification_2025_min_tap_target_size"
                 android:minHeight="@dimen/notification_2025_min_tap_target_size"
                 android:maxWidth="125dp"
                 style="@style/TextAppearance.NotificationInfo.Button"
                 android:textSize="@*android:dimen/notification_2025_action_text_size"/>
-        </LinearLayout>
+            <androidx.constraintlayout.helper.widget.Flow
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                app:constraint_referenced_ids="inline_dismiss,turn_off_notifications,done"
+                app:flow_wrapMode="chain"/>
+        </androidx.constraintlayout.widget.ConstraintLayout>
     </LinearLayout>
 </com.android.systemui.statusbar.notification.row.NotificationInfo>
diff --git a/packages/SystemUI/res/layout/notification_2025_smart_action_button.xml b/packages/SystemUI/res/layout/notification_2025_smart_action_button.xml
new file mode 100644
index 0000000..ed90588
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_2025_smart_action_button.xml
@@ -0,0 +1,35 @@
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<!-- android:paddingHorizontal is set dynamically in SmartReplyView. -->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+        style="@android:style/Widget.Material.Button"
+        android:stateListAnimator="@null"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="0dp"
+        android:minHeight="@dimen/notification_2025_smart_reply_button_min_height"
+        android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
+        android:background="@drawable/notification_2025_smart_reply_button_background"
+        android:gravity="center"
+        android:fontFamily="google-sans-flex"
+        android:textSize="@dimen/smart_reply_button_font_size"
+        android:textColor="@color/smart_reply_button_text"
+        android:paddingStart="@dimen/smart_reply_button_action_padding_left"
+        android:paddingEnd="@dimen/smart_reply_button_padding_horizontal"
+        android:drawablePadding="@dimen/smart_action_button_icon_padding"
+        android:textStyle="normal"
+        android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/layout/notification_2025_smart_reply_button.xml b/packages/SystemUI/res/layout/notification_2025_smart_reply_button.xml
new file mode 100644
index 0000000..4f543e5
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_2025_smart_reply_button.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<!-- android:paddingHorizontal is set dynamically in SmartReplyView. -->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+        style="@android:style/Widget.Material.Button"
+        android:stateListAnimator="@null"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="0dp"
+        android:minHeight="@dimen/notification_2025_smart_reply_button_min_height"
+        android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
+        android:background="@drawable/notification_2025_smart_reply_button_background"
+        android:gravity="center"
+        android:fontFamily="google-sans-flex"
+        android:textSize="@dimen/smart_reply_button_font_size"
+        android:textColor="@color/smart_reply_button_text"
+        android:paddingStart="@dimen/smart_reply_button_padding_horizontal"
+        android:paddingEnd="@dimen/smart_reply_button_padding_horizontal"
+        android:textStyle="normal"
+        android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 089ceae..d4bd142 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -341,8 +341,8 @@
             android:paddingEnd="4dp"
             >
             <TextView
-                android:id="@+id/turn_off_notifications"
-                android:text="@string/inline_turn_off_notifications"
+                android:id="@+id/inline_dismiss"
+                android:text="@string/notification_inline_dismiss"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_alignParentStart="true"
@@ -350,6 +350,19 @@
                 android:minWidth="@dimen/notification_importance_toggle_size"
                 android:minHeight="@dimen/notification_importance_toggle_size"
                 android:maxWidth="200dp"
+                android:paddingEnd="@dimen/notification_importance_button_padding"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+            <TextView
+                android:id="@+id/turn_off_notifications"
+                android:text="@string/inline_turn_off_notifications"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toEndOf="@id/inline_dismiss"
+                android:gravity="start|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="200dp"
+                android:paddingStart="@dimen/notification_importance_button_padding"
                 style="@style/TextAppearance.NotificationInfo.Button"/>
             <TextView
                 android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index 4850b35..d1755ef 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -143,8 +143,8 @@
             android:paddingEnd="4dp"
             >
             <TextView
-                android:id="@+id/turn_off_notifications"
-                android:text="@string/inline_turn_off_notifications"
+                android:id="@+id/inline_dismiss"
+                android:text="@string/notification_inline_dismiss"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_alignParentStart="true"
@@ -152,6 +152,19 @@
                 android:minWidth="@dimen/notification_importance_toggle_size"
                 android:minHeight="@dimen/notification_importance_toggle_size"
                 android:maxWidth="200dp"
+                android:paddingEnd="@dimen/notification_importance_button_padding"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+            <TextView
+                android:id="@+id/turn_off_notifications"
+                android:text="@string/inline_turn_off_notifications"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toEndOf="@id/inline_dismiss"
+                android:gravity="start|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="200dp"
+                android:paddingStart="@dimen/notification_importance_button_padding"
                 style="@style/TextAppearance.NotificationInfo.Button"/>
             <TextView
                 android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/promoted_notification_info.xml b/packages/SystemUI/res/layout/promoted_notification_info.xml
index 2e0a0ca..3982a66 100644
--- a/packages/SystemUI/res/layout/promoted_notification_info.xml
+++ b/packages/SystemUI/res/layout/promoted_notification_info.xml
@@ -373,8 +373,8 @@
             android:paddingEnd="4dp"
             >
             <TextView
-                android:id="@+id/turn_off_notifications"
-                android:text="@string/inline_turn_off_notifications"
+                android:id="@+id/inline_dismiss"
+                android:text="@string/notification_inline_dismiss"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_alignParentStart="true"
@@ -382,6 +382,19 @@
                 android:minWidth="@dimen/notification_importance_toggle_size"
                 android:minHeight="@dimen/notification_importance_toggle_size"
                 android:maxWidth="200dp"
+                android:paddingEnd="@dimen/notification_importance_button_padding"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+            <TextView
+                android:id="@+id/turn_off_notifications"
+                android:text="@string/inline_turn_off_notifications"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toEndOf="@id/inline_dismiss"
+                android:gravity="start|center_vertical"
+                android:minWidth="@dimen/notification_importance_toggle_size"
+                android:minHeight="@dimen/notification_importance_toggle_size"
+                android:maxWidth="200dp"
+                android:paddingStart="@dimen/notification_importance_button_padding"
                 style="@style/TextAppearance.NotificationInfo.Button"/>
             <TextView
                 android:id="@+id/done"
diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml
index e80ed26..22599a35 100644
--- a/packages/SystemUI/res/layout/sidefps_view.xml
+++ b/packages/SystemUI/res/layout/sidefps_view.xml
@@ -20,6 +20,7 @@
     android:id="@+id/sidefps_animation"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
+    android:contentDescription="@string/accessibility_side_fingerprint_indicator_label"
     app:lottie_autoPlay="true"
     app:lottie_loop="true"
     app:lottie_rawRes="@raw/sfps_pulse"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/fingerprint_dialogue_unlocked_to_checkmark_success_lottie.json b/packages/SystemUI/res/raw/fingerprint_dialogue_unlocked_to_checkmark_success_lottie.json
index b1d6a27..3f03fcf 100644
--- a/packages/SystemUI/res/raw/fingerprint_dialogue_unlocked_to_checkmark_success_lottie.json
+++ b/packages/SystemUI/res/raw/fingerprint_dialogue_unlocked_to_checkmark_success_lottie.json
@@ -1 +1 @@
-{"v":"5.7.13","fr":60,"ip":0,"op":55,"w":80,"h":80,"nm":"unlocked_to_checkmark_success","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.143,32,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[2.761,0],[0,-2.7],[0,0]],"o":[[0,0],[0,-2.7],[-2.761,0],[0,0],[0,0]],"v":[[5,5],[5,-0.111],[0,-5],[-5,-0.111],[-5.01,4]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":10,"s":[0]}],"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[38,45,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":10,"s":[0]}],"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[37.999,44.999,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.42,0],[0,1.42],[1.42,0],[0,-1.42]],"o":[[1.42,0],[0,-1.42],[-1.42,0],[0,1.42]],"v":[[0,2.571],[2.571,0],[0,-2.571],[-2.571,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":10,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".green200","cl":"green200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.5,40.75,0],"ix":2,"l":2},"a":{"a":0,"k":[12.5,-6.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[60,60,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":19,"s":[112,112,100]},{"t":30,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-10.556,-9.889],[7.444,6.555],[34.597,-20.486]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.854901960784,0.709803921569,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":10,"s":[0]},{"t":20,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":10,"op":910,"st":10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green200","cl":"green200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":15,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[93.5,93.5,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.854901960784,0.709803921569,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":15,"s":[100]}],"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[0]},{"t":20,"s":[4]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":10,"op":77,"st":10,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".grey700","cl":"grey700","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.278431385756,0.278431385756,0.278431385756,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"t":20,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
+{"v":"5.7.13","fr":60,"ip":0,"op":55,"w":80,"h":80,"nm":"unlocked_to_checkmark_success","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onPrimary","cl":"onPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.143,32,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[2.761,0],[0,-2.7],[0,0]],"o":[[0,0],[0,-2.7],[-2.761,0],[0,0],[0,0]],"v":[[5,5],[5,-0.111],[0,-5],[-5,-0.111],[-5.01,4]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":10,"s":[0]}],"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onPrimary","cl":"onPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[38,45,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":10,"s":[0]}],"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onPrimary","cl":"onPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[37.999,44.999,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.42,0],[0,1.42],[1.42,0],[0,-1.42]],"o":[[1.42,0],[0,-1.42],[-1.42,0],[0,1.42]],"v":[[0,2.571],[2.571,0],[0,-2.571],[-2.571,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":10,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".green200","cl":"green200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.5,40.75,0],"ix":2,"l":2},"a":{"a":0,"k":[12.5,-6.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[60,60,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":19,"s":[112,112,100]},{"t":30,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-10.556,-9.889],[7.444,6.555],[34.597,-20.486]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.854901960784,0.709803921569,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":10,"s":[0]},{"t":20,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":10,"op":910,"st":10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green200","cl":"green200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":15,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[93.5,93.5,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.854901960784,0.709803921569,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":15,"s":[100]}],"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[0]},{"t":20,"s":[4]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":10,"op":77,"st":10,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".primary","cl":"primary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.278431385756,0.278431385756,0.278431385756,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"t":20,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 4179d8a..bc08a3e 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Gekoppel aan <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Gekoppel aan <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Vou groep uit."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Voeg toestel by groep."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Verwyder toestel uit groep."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Maak app oop."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Nie gekoppel nie."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Swerwing"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Gehoortoestelle"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Skakel tans aan …"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Kan nie helderheid verstel nie omdat dit deur die boonste app beheer word"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Outodraai"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Outodraai skerm"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Ligging"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgewing"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Regs"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Omgewing"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Linker omgewing"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Regter omgewing"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Vou uit na links- en regsgeskeide kontroles"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Vou in na verenigde kontrole"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Demp omgewing"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ontdemp omgewing"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Nutsgoed"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Intydse Onderskrifte"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Instellings"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokkeer toestelmikrofoon?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokkeer toestelkamera?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aan – gesiggegrond"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Pas toe"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Skakel kennisgewings af"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Stil"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Verstek"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Outomaties"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Gebruik verdeelde skerm met app aan die regterkant"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Gebruik verdeelde skerm met app aan die linkerkant"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Gebruik volskerm"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Gebruik rekenaaraansig"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Gebruik werkskermvensters"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Voeg by posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posisie is ongeldig."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Teël is reeds bygevoeg"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Teël is bygevoeg"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Teël is verwyder"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kitsinstellingswysiger."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Versteek hierdie mediakontrole vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Die huidige mediasessie kan nie versteek word nie."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Versteek"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Hervat"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellings"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> deur <xliff:g id="ARTIST_NAME">%2$s</xliff:g> speel tans vanaf <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> van <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Onbekend"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Stel alle teëls terug?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle Kitsinstellingsteëls sal na die toestel se oorspronklike instellings teruggestel word"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index fde914f..64912d4 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Af"</item>
     <item msgid="4875147066469902392">"Aan"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Onbeskikbaar"</item>
     <item msgid="5044688398303285224">"Af"</item>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index a92a141..1c158b3 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"ከ<xliff:g id="BLUETOOTH">%s</xliff:g> ጋር ተገናኝቷል።"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"ከ<xliff:g id="CAST">%s</xliff:g> ጋር ተገናኝቷል።"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"ቡድንን ዘርጋ።"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"መሣሪያን ወደ ቡድን አክል።"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"መሣሪያን ከቡድን ላይ አስወግድ።"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"መተግበሪያ ክፈት።"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"አልተገናኘም።"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"በማዛወር ላይ"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ግቤት"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"መስሚያ አጋዥ መሣሪያዎች"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"በማብራት ላይ..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"ከላይ ባለው መተግበሪያ ቁጥጥር እየተደረገበት ስለሆነ ብሩህነትን ማስተካከል አልተቻለም"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"በራስ ሰር አሽከርክር"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ማያ ገጽን በራስ-አሽከርክር"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"አካባቢ"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"በዙሪያ ያሉ"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ግራ"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ቀኝ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"በዙሪያ ያሉ"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"በግራ ዙሪያ ያሉ"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"በቀኝ ዙሪያ ያሉ"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ወደ ግራ እና ቀኝ የተለያዩ ቁጥጥሮች ዘርጋ"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ወደ የተዋሃደ ቁጥጥር ሰብስብ"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"በዙሪያ ያሉትን ድምፀ-ከል አድርግ"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"በዙሪያ ያሉትን ድምፅ-ከል አንሳ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"መሣሪያዎች"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"የቀጥታ መግለጫ ጽሑፍ"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"ቅንብሮች"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ማስታወሻ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"የመሣሪያ ማይክሮፎን እገዳ ይነሳ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"የመሣሪያ ካሜራ እገዳ ይነሳ?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"በርቷል - መልክ ላይ የተመሠረተ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ተከናውኗል"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ተግብር"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"ማሳወቂያዎችን አጥፋ"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"ፀጥ ያለ"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ነባሪ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ራስ-ሰር"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"መተግበሪያ በስተቀኝ ላይ ሆኖ የተከፈለ ማያ ገፅን ይጠቀሙ"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"መተግበሪያ በስተግራ ላይ ሆኖ የተከፈለ ማያ ገፅን ይጠቀሙ"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"ሙሉ ገፅ ዕይታን ይጠቀሙ"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"የዴስክቶፕ ዕይታ ይጠቀሙ"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"የዴስክቶፕ መስኮት ይጠቀሙ"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከታች ወዳለ መተግበሪያ ይቀይሩ"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከላይ ወዳለ መተግበሪያ ይቀይሩ"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"በተከፈለ ማያ ገጽ ወቅት፡- መተግበሪያን ከአንዱ ወደ ሌላው ተካ"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ቦታ አክል"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"አቀማመጡ ተቀባይነት የለውም።"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"የ<xliff:g id="POSITION">%1$d</xliff:g> አቀማመጥ"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"ሰቅ አስቀድሞ ታክሏል"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ሰቅ ታክሏል"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ሰቅ ተወግዷል"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"የፈጣን ቅንብሮች አርታዒ።"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"ለ<xliff:g id="APP_NAME">%1$s</xliff:g> የዚህ ሚዲያ መቆጣጠሪያ ይደበቅ?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"የአሁኑ የሚዲያ ክፍለ ጊዜ ሊደበቅ አይቻልም።"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ደብቅ"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"ከቆመበት ቀጥል"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ቅንብሮች"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> በ<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ከ<xliff:g id="APP_LABEL">%3$s</xliff:g> እየተጫወተ ነው"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> ከ<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ያልታወቀ"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ሁሉም ሰቆች ዳግም ይጀምሩ?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ሁሉም የፈጣን ቅንብሮች ሰቆች ወደ የመሣሪያው የመጀመሪያ ቅንብሮች ዳግም ይጀምራሉ"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>፣ <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml
index f8e7a43..24666e7 100644
--- a/packages/SystemUI/res/values-am/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"ጠፍቷል"</item>
     <item msgid="4875147066469902392">"በርቷል"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"አይገኝም"</item>
     <item msgid="5044688398303285224">"ጠፍቷል"</item>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 13f3fd8..b494f4e 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"متصل بـ <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"تم الاتصال بـ <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"سيتم توسيع المجموعة."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"إضافة الجهاز إلى المجموعة"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"إزالة الجهاز من المجموعة"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"سيتم فتح التطبيق."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"غير متصل."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"التجوال"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"الإدخال"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سماعات الأذن الطبية"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"جارٍ التفعيل…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"لا يمكن ضبط مستوى السطوع لأنّ التطبيق العلوي يتحكّم فيه"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"التدوير التلقائي"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"التدوير التلقائي للشاشة"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"الموقع الجغرافي"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"الأصوات المحيطة"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"اليسرى"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"اليمنى"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"الأصوات المحيطة"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"الأصوات المحيطة بالجهة اليسرى"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"الأصوات المحيطة بالجهة اليمنى"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"توسيع لوحة التحكّم الموحّدة إلى عناصر تحكُّم منفصلة على اليسار واليمين"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"تصغير عناصر التحكّم في الصوت إلى لوحة تحكُّم موحّدة"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"كتم الأصوات المحيطة"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"إعادة الأصوات المحيطة"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"الأدوات"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"النسخ النصي التلقائي"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"الإعدادات"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ملاحظات"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"هل تريد إزالة حظر ميكروفون الجهاز؟"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"هل تريد إزالة حظر كاميرا الجهاز؟"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"تفعيل - استنادًا للوجه"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"تمّ"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"تطبيق"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"إيقاف الإشعارات"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"إشعارات صامتة"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"تلقائية"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"تلقائي"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"استخدام \"وضع تقسيم الشاشة\" مع تثبيت التطبيق على اليمين"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"استخدام \"وضع تقسيم الشاشة\" مع تثبيت التطبيق على اليسار"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"استخدام وضع ملء الشاشة"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"استخدام وضع العرض المخصّص للكمبيوتر المكتبي"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"استخدام وضع عرض النوافذ على سطح المكتب"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"التبديل إلى التطبيق على اليسار أو الأسفل أثناء استخدام \"تقسيم الشاشة\""</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"التبديل إلى التطبيق على اليمين أو الأعلى أثناء استخدام \"تقسيم الشاشة\""</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"استبدال تطبيق بآخر في وضع \"تقسيم الشاشة\""</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"الإضافة إلى الموضع <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"الموضِع غير صالح."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"الموضع: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"سبق أن تمت إضافة شاشة المعلومات"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"تمت إضافة البطاقة."</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"تمت إزالة البطاقة."</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"برنامج تعديل الإعدادات السريعة."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"هل تريد إخفاء عنصر التحكم في الوسائط هذا للتطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>؟"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"لا يمكن إخفاء جلسة الوسائط الحالية."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"إخفاء"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"استئناف التشغيل"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"الإعدادات"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"يتم تشغيل <xliff:g id="SONG_NAME">%1$s</xliff:g> للفنان <xliff:g id="ARTIST_NAME">%2$s</xliff:g> من تطبيق <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> من إجمالي <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index aac5a35..31607b9 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"الميزة غير مفعّلة"</item>
     <item msgid="4875147066469902392">"الميزة مفعّلة"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"الميزة غير متاحة"</item>
     <item msgid="5044688398303285224">"الميزة غير مفعّلة"</item>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 067649c..d3ec875 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>ৰ লগত সংযোগ কৰা হ’ল।"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g>ত সংযোগ হ’ল।"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"গোট বিস্তাৰ কৰক।"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"গোটত ডিভাইচ যোগ দিয়ক।"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"গোটৰ পৰা ডিভাইচ আঁতৰাওক।"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"এপ্লিকেশ্বনটো খোলক।"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"সংযোগ হৈ থকা নাই।"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"ৰ\'মিং"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ইনপুট"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"শ্ৰৱণ যন্ত্ৰ"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"অন কৰি থকা হৈছে…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"উজ্জ্বলতা মিলাব নোৱাৰি কাৰণ সেয়া শীৰ্ষৰ এপটোৱে নিয়ন্ত্ৰণ কৰি আছে"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"স্বয়ং-ঘূৰ্ণন"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"স্বয়ং-ঘূৰ্ণন স্ক্ৰীন"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"অৱস্থান"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"আশ-পাশ"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"বাওঁফাল"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"সোঁফাল"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"আশে-পাশে থকা"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"বাওঁফালে আশে-পাশে থকা"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"সোঁফালে আশে-পাশে থকা"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"বাওঁ আৰু সোঁফালৰ পৃথক কৰা নিয়ন্ত্ৰণলৈ সংকোচন কৰক"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"একত্ৰিত নিয়ন্ত্ৰণলৈ সংকোচন কৰক"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"আশ-পাশৰ ধ্বনি মিউট কৰক"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"আশ-পাশৰ ধ্বনি আনমিউট কৰক"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"সঁজুলি"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"লাইভ কেপশ্বন"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"ছেটিং"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"টোকা"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইচৰ মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইচৰ কেমেৰা অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"অন আছে - মুখাৱয়ব ভিত্তিক"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"কৰা হ’ল"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"প্ৰয়োগ কৰক"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"জাননী অফ কৰক"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"নীৰৱ"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ডিফ’ল্ট"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"স্বয়ংক্ৰিয়"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"সোঁফালে থকা এপ্‌টোৰ সৈতে বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰক"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"বাওঁফালে থকা এপ্‌টোৰ সৈতে বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰক"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"পূৰ্ণ স্ক্ৰীন ব্যৱহাৰ কৰক"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ডেস্কটপ ভিউ ব্যৱহাৰ কৰক"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ডেস্কটপ ৱিণ্ড’ৱিং ব্যৱহাৰ কৰক"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত সোঁফালে অথবা তলত থকা এপলৈ সলনি কৰক"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত বাওঁফালে অথবা ওপৰত থকা এপলৈ সলনি কৰক"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"বিভাজিত স্ক্ৰীনৰ ব্যৱহাৰ কৰাৰ সময়ত: কোনো এপ্ এখন স্ক্ৰীনৰ পৰা আনখনলৈ নিয়ক"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থানত যোগ দিয়ক"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"স্থান অমান্য।"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থান"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"ইতিমধ্যে টাইল যোগ দিয়া হৈছে"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"টাইল যোগ দিয়া হৈছে"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"টাইল আঁতৰোৱা হৈছে"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিঙৰ সম্পাদক।"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে এই মিডিয়া নিয়ন্ত্ৰণটো লুকুৱাবনে?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"বৰ্তমানৰ মিডিয়াৰ ছেশ্বনটো লুকুৱাব নোৱাৰি।"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"লুকুৱাওক"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিং"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ত <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ৰ <xliff:g id="SONG_NAME">%1$s</xliff:g> গীতটো প্লে’ হৈ আছে"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>ৰ <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"অজ্ঞাত"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"আটাইবোৰ টাইল ৰিছেট কৰিবনে?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"আটাইবোৰ ক্ষিপ্ৰ ছেটিঙৰ টাইল ডিভাইচৰ মূল ছেটিংছলৈ ৰিছেট হ’ব"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml
index ba30c1e..39bd63e 100644
--- a/packages/SystemUI/res/values-as/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"অফ আছে"</item>
     <item msgid="4875147066469902392">"অন কৰা আছে"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"উপলব্ধ নহয়"</item>
     <item msgid="5044688398303285224">"অফ আছে"</item>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 9df9923..9655ab7 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> üzərindən qoşuldu."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> cihazına qoşulub."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Qrupu genişləndirin."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Qrupa cihaz əlavə edin."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Cihazı qrupdan silin."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Tətbiqi açın."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Qoşulu deyil."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Rouminq"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Giriş"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Eşitmə aparatları"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiv edilir..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Yuxarıdakı tətbiq tərəfindən idarə olunduğu üçün parlaqlığı tənzimləmək mümkün deyil"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avtodönüş"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranın avtomatik dönməsi"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Məkan"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ətraf mühit"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sol"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Sağ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Ətraf mühit"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Sol tərəf"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Sağ tərəf"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Sola və sağa ayrılmış idarəetmələr üçün genişləndirin"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Vahid nəzarət üçün yığcamlaşdırın"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Ətraf mühiti səssiz edin"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ətraf mühiti səssiz rejimdən çıxarın"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alətlər"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Canlı Altyazı"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Ayarlar"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Qeyd"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonu blokdan çıxarılsın?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerası blokdan çıxarılsın?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktiv - Üz əsaslı"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Hazırdır"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Tətbiq edin"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Bildirişləri deaktiv edin"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Səssiz"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Defolt"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Tətbiq sağda olmaqla bölünmüş ekranı istifadə edin"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Tətbiq solda olmaqla bölünmüş ekranı istifadə edin"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Tam ekrandan istifadə edin"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Masaüstü görünüşdən istifadə edin"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Masaüstü pəncərə rejimindən istifadə edin"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə əlavə edin"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Mövqe yanlışdır."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Mozaik əlavə edilib"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Mozaik əlavə edilib"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Mozaik silinib"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sürətli ayarlar redaktoru."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün bu media nizamlayıcısı gizlədilsin?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Cari media sessiyası gizlədilə bilməz."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Gizlədin"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Davam edin"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> tərəfindən <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%3$s</xliff:g> tətbiqindən oxudulur"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>/<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Naməlum"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Bütün mozaiklər sıfırlansın?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Bütün Sürətli Ayarlar mozaiki cihazın orijinal ayarlarına sıfırlanacaq"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index 74b95e2..a78e672 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Deaktiv"</item>
     <item msgid="4875147066469902392">"Aktiv"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Əlçatan deyil"</item>
     <item msgid="5044688398303285224">"Deaktiv"</item>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 6ab7bf1..f463996 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -423,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Levo"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Desno"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Okruženje"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Levo okruženje"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Desno okruženje"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Proširi na kontrole razdvojene na levu i desnu stranu"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Skupi u jedinstvenu kontrolu"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Isključi zvuk okruženja"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Uključi zvuk okruženja"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alatke"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Titl uživo"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Podešavanja"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Beleška"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite da odblokirate mikrofon uređaja?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite da odblokirate kameru uređaja?"</string>
@@ -796,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na osnovu lica"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Primeni"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Isključi obaveštenja"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Nečujno"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Podrazumevano"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatska"</string>
@@ -907,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Koristi podeljeni ekran sa aplikacijom s desne strane"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Koristi podeljeni ekran sa aplikacijom s leve strane"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Koristi prikaz preko celog ekrana"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Koristi prikaz za računare"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Koristi prozorski prikaz na računaru"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pređi u aplikaciju zdesna ili ispod dok je podeljen ekran"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju sleva ili iznad dok koristite podeljeni ekran"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"U režimu podeljenog ekrana: zamena jedne aplikacije drugom"</string>
@@ -1003,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajte na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozicija je nevažeća."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Pločica je već dodata"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Pločica je dodata"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Pločica je uklonjena"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač za Brza podešavanja."</string>
@@ -1203,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Želite da sakrijete ovu kontrolu za medije za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Aktuelna sesija medija ne može da bude sakrivena."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sakrij"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Podešavanja"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> izvođača <xliff:g id="ARTIST_NAME">%2$s</xliff:g> se pušta iz aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> od <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1571,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Želite da resetujete sve pločice?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve pločice Brzih podešavanja će se resetovati na prvobitna podešavanja uređaja"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index 51b667c..bbfcf84 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Isključeno"</item>
     <item msgid="4875147066469902392">"Uključeno"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nedostupno"</item>
     <item msgid="5044688398303285224">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 94f9507..2ece7bd 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -33,7 +33,7 @@
     <string name="battery_saver_dismiss_action" msgid="7199342621040014738">"Не, дзякуй"</string>
     <string name="standard_battery_saver_text" msgid="6855876746552374119">"Стандартны рэжым"</string>
     <string name="extreme_battery_saver_text" msgid="8455810156739865335">"Максімальная"</string>
-    <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Аўтаматычны паварот экрана"</string>
+    <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Аўтапаварот экрана"</string>
     <string name="usb_device_permission_prompt" msgid="4414719028369181772">"Дазволіць праграме <xliff:g id="APPLICATION">%1$s</xliff:g> доступ да прылады <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
     <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Даць праграме \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" доступ да прылады \"<xliff:g id="USB_DEVICE">%2$s</xliff:g>\"?\nУ гэтай праграмы няма дазволу на запіс, аднак яна зможа запісваць аўдыя праз гэту прыладу USB."</string>
     <string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"Дазволіць праграме \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" доступ да прылады \"<xliff:g id="USB_DEVICE">%2$s</xliff:g>\"?"</string>
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Падлучаны да <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Ёсць падключэнне да <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Разгарнуць групу."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Дадаць прыладу ў групу."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Выдаліць прыладу з групы."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Адкрыць праграму."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Няма падключэння."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Роўмінг"</string>
@@ -333,10 +331,9 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Увод"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слыхавыя апараты"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Уключэнне…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Не ўдаецца адрэгуляваць яркасць, бо яна кантралюецца верхняй праграмай"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аўтапаварот"</string>
-    <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аўтаматычны паварот экрана"</string>
+    <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аўтапаварот экрана"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Месцазнаходжанне"</string>
     <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Экранная застаўка"</string>
     <string name="quick_settings_camera_label" msgid="5612076679385269339">"Доступ да камеры"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Навакольныя гукі"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Левы бок"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Правы бок"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Навакольныя гукі"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Навакольныя гукі злева"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Навакольныя гукі справа"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Зрабіць левую і правую панэлі кіравання"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Зрабіць адну панэль кіравання"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Выключыць навакольныя гукі"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Уключыць навакольныя гукі"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Інструменты"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Аўтаматычныя субцітры"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Налады"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Нататка"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблакіраваць мікрафон прылады?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблакіраваць камеру прылады?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Уключана – З улікам паставы галавы"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Гатова"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Прымяніць"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Выключыць апавяшчэнні"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Бязгучны рэжым"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Стандартна"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Аўтаматычна"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Падзяліць экран і памясціць праграму справа"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Падзяліць экран і памясціць праграму злева"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Выкарыстоўваць поўнаэкранны рэжым"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Выкарыстоўваць версію для камп’ютараў"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Выкарыстоўваць рэжым вокнаў працоўнага стала"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пераключыцца на праграму справа або ўнізе на падзеленым экране"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пераключыцца на праграму злева або ўверсе на падзеленым экране"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"У рэжыме падзеленага экрана замяніць адну праграму на іншую"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Дадаць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Няправільнае месца."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Пазіцыя <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Плітка ўжо дададзена"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Плітка дададзена"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Плітка выдалена"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Рэдактар хуткіх налад."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Схаваць гэту панэль мультымедыя для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Не ўдалося схаваць бягучы сеанс мультымедыя."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Схаваць"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Узнавіць"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Налады"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"У праграме \"<xliff:g id="APP_LABEL">%3$s</xliff:g>\" прайграецца кампазіцыя \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\", выканаўца – <xliff:g id="ARTIST_NAME">%2$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> з <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index cbbfe92..e6b3543 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Выключана"</item>
     <item msgid="4875147066469902392">"Уключана"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Недаступна"</item>
     <item msgid="5044688398303285224">"Выключана"</item>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 4615e60..3ab943a 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Има връзка с <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Установена е връзка с/ъс <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Разгъване на групата."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Добавяне на устройството към групата."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Премахване на устройството от групата."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Отваряне на приложението."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Няма връзка."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Роуминг"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Вход"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слухови апарати"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Включва се..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Яркостта не може да се коригира, защото се контролира от приложението на екрана"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авт. ориентация"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично завъртане на екрана"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Местоположение"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Околни звуци"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ляво"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Дясно"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Околни звуци"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Околни звуци отляво"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Околни звуци отдясно"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Разгъване до отделни контроли за ляво и дясно"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Свиване до обединена контрола"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Спиране на околните звуци"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Включване на околните звуци"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Инструменти"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Надписи на живо"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Настройки"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Бележка"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се отблокира ли микрофонът на устройството?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се отблокира ли камерата на устройството?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Вкл. – въз основа на лицето"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Прилагане"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Изключване на известията"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Тих режим"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Стандартно"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматично"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Използване на разделен екран с приложението вдясно"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Използване на разделен екран с приложението вляво"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Използване режима на цял екран"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Използване на изгледа за настолни компютри"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Използване на режима за настолни компютри"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Превключване към приложението вдясно/отдолу в режима на разделен екран"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Превключване към приложението вляво/отгоре в режима на разделен екран"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"При разделен екран: замяна на дадено приложение с друго"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавяне към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Невалидна позиция."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Панелът вече е добавен"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Панелът е добавен"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Панелът е премахнат"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор за бързи настройки."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Скриване на мултимед. контрола за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Текущата сесия за мултимедия не бе скрита."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Скриване"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Възобновяване"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> на <xliff:g id="ARTIST_NAME">%2$s</xliff:g> се възпроизвежда от <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> от <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index 636585d..b46d4df 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Изкл."</item>
     <item msgid="4875147066469902392">"Вкл."</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Не е налице"</item>
     <item msgid="5044688398303285224">"Изкл."</item>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 6247bf1..4ab650e 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>এ সংযুক্ত হয়ে আছে।"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> এর সাথে সংযুক্ত৷"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"গ্রুপ বড় করুন।"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"গ্রুপে ডিভাইস যোগ করুন।"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"গ্রুপ থেকে ডিভাইস সরিয়ে দিন।"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"অ্যাপ্লিকেশন খুলুন।"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"সংযুক্ত নয়৷"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"রোমিং"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ইনপুট"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"হিয়ারিং এড"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"চালু করা হচ্ছে…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"উজ্জ্বলতা অ্যাডজাস্ট করা যাচ্ছে না কারণ এটি সেরা অ্যাপের মাধ্যমে কন্ট্রোল করা হচ্ছে"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"নিজে থেকে ঘুরবে"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"অটো-রোটেট স্ক্রিন"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"লোকেশন"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"সারাউন্ডিং"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"বাঁদিক"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ডানদিক"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"সারাউন্ডিং"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"লেফ্ট সারাউন্ডিং"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"রাইট সারাউন্ডিং"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"বাঁদিক ও ডানদিকের আলাদা করা কন্ট্রোল বড় করুন"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ইউনিফায়েড কন্ট্রোল আড়াল করুন"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"সারাউন্ডিং মিউট করুন"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"সারাউন্ডিং আনমিউট করুন"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"টুল"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"লাইভ ক্যাপশন"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"সেটিংস"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"মনে রাখবেন"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইসের মাইক্রোফোন আনব্লক করতে চান?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইসের ক্যামেরা আনব্লক করতে চান?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"চালু আছে - মুখের হিসেবে"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"হয়ে গেছে"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"প্রয়োগ করুন"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"বিজ্ঞপ্তি বন্ধ করুন"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"সাইলেন্ট"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ডিফল্ট"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"অটোমেটিক"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ডানদিকে বর্তমান অ্যাপে স্প্লিট স্ক্রিন ব্যবহার করুন"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"বাঁদিকে বর্তমান অ্যাপে স্প্লিট স্ক্রিন ব্যবহার করুন"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"ফুল-স্ক্রিন মোড ব্যবহার করুন"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ডেস্কটপ ভিউ ব্যবহার করুন"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ডেস্কটপ উইন্ডোয়িং ব্যবহার করুন"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"স্প্লিট স্ক্রিন ব্যবহার করার সময় ডানদিকের বা নিচের অ্যাপে পাল্টে নিন"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"স্প্লিট স্ক্রিন ব্যবহার করার সময় বাঁদিকের বা উপরের অ্যাপে পাল্টে নিন"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"\'স্প্লিট স্ক্রিন\' থাকাকালীন: একটি অ্যাপ থেকে অন্যটিতে পাল্টান"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>-এ যোগ করুন"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"পজিশন সঠিক নয়।"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"টাইল আগেই যোগ করা হয়েছে"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"টাইল যোগ করা হয়েছে"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"টাইল সরানো হয়েছে"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"দ্রুত সেটিংস সম্পাদক৷"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর মিডিয়া কন্ট্রোল লুকিয়ে রাখতে চান?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"বর্তমান মিডিয়া সেশন লুকিয়ে রাখা যাবে না।"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"লুকান"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"আবার চালু করুন"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"সেটিংস"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>-এর <xliff:g id="SONG_NAME">%1$s</xliff:g> গানটি <xliff:g id="APP_LABEL">%3$s</xliff:g> অ্যাপে চলছে"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>টির মধ্যে <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>টি"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"অজানা"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"সব টাইল রিসেট করবেন?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"সব কুইক সেটিংস টাইল, ডিভাইসের আসল সেটিংসে রিসেট হয়ে যাবে"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index 08231e4..852986c 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"বন্ধ আছে"</item>
     <item msgid="4875147066469902392">"চালু আছে"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"উপলভ্য নেই"</item>
     <item msgid="5044688398303285224">"বন্ধ আছে"</item>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 75f3f08..afa877e 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezan na <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Povezan na <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Proširivanje grupe."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Dodavanje uređaja u grupu."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Uklanjanje uređaja iz grupe."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Otvaranje aplikacije."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Nije povezano."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ulaz"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Nije moguće podesiti osvijetljenost jer njome upravlja gornja aplikacija"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko rotiranje"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lijevo"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Desno"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Okruženje"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Lijevo okruženje"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Desno okruženje"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Proširivanje u zasebne kontrole ulijevo i udesno"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Sužavanje u objedinjenu kontrolu"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Isključivanje zvuka okruženja"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Uključivanje zvuka okruženja"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alati"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatski titlovi"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Postavke"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Bilješka"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokirati mikrofon uređaja?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokirati kameru uređaja?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na osnovu lica"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Primijeni"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Isključi obavještenja"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Nečujno"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Zadano"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Korištenje podijeljenog ekrana s aplikacijom na desnoj strani"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Korištenje podijeljenog ekrana s aplikacijom na lijevoj strani"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Korištenje prikaza preko cijelog ekrana"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Korištenje prikaza na računaru"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Korištenje prikaza u prozorima na računaru"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Nevažeći položaj."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Kartica je već dodana"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kartica je dodana"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kartica je uklonjena"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivanje brzih postavki"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Sakriti kontrolu medija za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Trenutna sesija medija se ne može sakriti."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sakrij"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Pjesma <xliff:g id="SONG_NAME">%1$s</xliff:g> izvođača <xliff:g id="ARTIST_NAME">%2$s</xliff:g> se reproducira pomoću aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> od <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vratiti sve kartice na zadano?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve kartice Brze postavke će se vratiti na originalne postavke uređaja"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index 51b667c..bbfcf84 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Isključeno"</item>
     <item msgid="4875147066469902392">"Uključeno"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nedostupno"</item>
     <item msgid="5044688398303285224">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 19b299f..3e43c79 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"S\'ha connectat a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Està connectat amb <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Desplega el grup."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Afegeix el dispositiu al grup."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Suprimeix el dispositiu del grup."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Obre l\'aplicació."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Sense connexió."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Itinerància"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audiòfons"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"S\'està activant…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"No es pot ajustar la brillantor perquè està controlada per l\'aplicació superior"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Gira automàticament"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Gira la pantalla automàticament"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicació"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Entorn"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerra"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Dreta"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Entorn"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Entorn esquerre"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Entorn dret"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Desplega els controls separats d\'esquerra i dreta"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Replega per unificar el control"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silencia l\'entorn"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Deixa de silenciar l\'entorn"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Eines"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítols instantanis"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Configuració"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vols desbloquejar el micròfon del dispositiu?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vols desbloquejar la càmera del dispositiu?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activat: basat en cares"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Fet"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplica"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desactiva les notificacions"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silenci"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminat"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automàtic"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Utilitzar la pantalla dividida amb l\'aplicació a la dreta"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Utilitzar la pantalla dividida amb l\'aplicació a l\'esquerra"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Utilitza la pantalla completa"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Utilitza la visualització per a ordinadors"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Utilitza l\'enfinestrament d\'escriptori"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Afegeix a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"La posició no és vàlida."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"La targeta ja s\'ha afegit"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"El mosaic s\'ha afegit"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"El mosaic s\'ha suprimit"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuració ràpida."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Amagar aquest control multimèdia per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"La sessió multimèdia actual no es pot amagar."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Amaga"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Reprèn"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuració"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) s\'està reproduint des de l\'aplicació <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> de <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconegut"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vols restablir totes les icones?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Totes les icones de configuració ràpida es restabliran a les opcions originals del dispositiu"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index 3b0b9d7..5094d57 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Desactivat"</item>
     <item msgid="4875147066469902392">"Activat"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"No disponible"</item>
     <item msgid="5044688398303285224">"Desactivat"</item>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 51dfa2e..f374913 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Připojeno k zařízení <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Jste připojeni k zařízení <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Rozbalit skupinu."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Přidat zařízení do skupiny."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Odstranit zařízení ze skupiny."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Otevřít aplikaci."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Nepřipojeno."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Naslouchátka"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapínání…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Jas nelze upravit, protože ho řídí hlavní aplikace"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. otáčení"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčení obrazovky"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolí"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vlevo"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Vpravo"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Okolí"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Okolí vlevo"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Okolí vpravo"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Rozbalit na samostatné ovládání levé a pravé strany"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Sbalit na sjednocené ovládání"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Ztlumit okolí"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Zapnout zvuk okolí"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Nástroje"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Okamžité titulky"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Nastavení"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Poznámka"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokovat mikrofon zařízení?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokovat fotoaparát zařízení?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Zapnuto – podle obličeje"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Hotovo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Použít"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Vypnout oznámení"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Tichý režim"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Výchozí"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Použít rozdělenou obrazovku s aplikací vpravo"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Použít rozdělenou obrazovku s aplikací vlevo"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Použít celou obrazovku"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Použít zobrazení na počítači"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Používat okna jako na počítači"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Přidat dlaždici na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozice není platná."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Karta byla už přidána"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Dlaždice byla přidána"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Dlaždice byla odstraněna"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Skrýt toto ovládání médií aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Aktuální mediální relaci nelze skrýt."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skrýt"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovat"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavení"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Skladba <xliff:g id="SONG_NAME">%1$s</xliff:g> od interpreta <xliff:g id="ARTIST_NAME">%2$s</xliff:g> hrajte z aplikace <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> z <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznámé"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Resetovat všechny dlaždice?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Všechny dlaždice Rychlého nastavení se resetují do původní konfigurace zařízení"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index 4977032..0c7db67 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Vypnuto"</item>
     <item msgid="4875147066469902392">"Zapnuto"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nedostupné"</item>
     <item msgid="5044688398303285224">"Vypnuto"</item>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index ad6633e..0875aad0 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Forbundet med <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Forbundet til <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Udvid gruppe."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Føj enhed til gruppe."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Fjern enhed fra gruppe."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Åbn app."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Ikke tilsluttet."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Høreapparater"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiverer…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Lysstyrken kan ikke justeres, fordi den styres af den øverste app"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Roter automatisk"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Roter skærmen automatisk"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokation"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivelser"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Venstre"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Højre"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Omgivelser"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Omgivelser til venstre"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Omgivelser til højre"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Udvid til adskilte styringselementer til venstre og højre"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Minimer til samlet styringselement"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Ignorer omgivelser"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ignorer ikke omgivelser"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Værktøjer"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Livetekstning"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Indstillinger"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du fjerne blokeringen af enhedens mikrofon?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du fjerne blokeringen af enhedens kamera?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Til – ansigtsbaseret"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Udfør"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Anvend"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Deaktiver notifikationer"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Lydløs"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Brug opdelt skærm med appen til højre"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Brug opdelt skærm med appen til venstre"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Brug fuld skærm"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Brug computervenlig visning"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Brug vinduer på skrivebordet"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Føj til lokation <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Positionen er ugyldig."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Lokation <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Handlingsfeltet er allerede tilføjet"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Feltet blev tilføjet"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Feltet blev fjernet"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsværktøj til Kvikmenu."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Vil du skjule denne mediefunktion for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Den aktuelle mediesession kan ikke skjules."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skjul"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Genoptag"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Indstillinger"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> af <xliff:g id="ARTIST_NAME">%2$s</xliff:g> afspilles via <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> af <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ukendt"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vil du nulstille alle handlingsfelter?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle handlingsfelter i kvikmenuen nulstilles til enhedens oprindelige indstillinger"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index af6dafb..5558b0b 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Fra"</item>
     <item msgid="4875147066469902392">"Til"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ikke tilgængelig"</item>
     <item msgid="5044688398303285224">"Fra"</item>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 8bc3815..8b783fd 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Mit <xliff:g id="BLUETOOTH">%s</xliff:g> verbunden"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Verbunden mit <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Gruppe erweitern."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Gerät zu Gruppe hinzufügen."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Gerät aus Gruppe entfernen."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Anwendung öffnen."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Nicht verbunden"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Eingabe"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hörgerät"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Wird aktiviert…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Die Helligkeit kann nicht angepasst werden, weil sie von der obersten App gesteuert wird"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. drehen"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Bildschirm automatisch drehen"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Standort"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Umgebungsgeräusche"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Rechts"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Umgebungsgeräusche"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Umgebungsgeräusche links"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Umgebungsgeräusche rechts"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"In ein linkes und ein rechtes Steuerfeld maximieren"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Zu einem einzigen Steuerfeld minimieren"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Umgebungsgeräusche stummschalten"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Stummschaltung der Umgebungsgeräusche aufheben"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatische Untertitel"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Einstellungen"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Notiz"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Blockierung des Gerätemikrofons aufheben?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Blockierung der Gerätekamera aufheben?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"An – gesichtsbasiert"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Fertig"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Anwenden"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Benachrichtigungen deaktivieren"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Lautlos"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Splitscreen mit der App auf der rechten Seite nutzen"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Splitscreen mit der App auf der linken Seite nutzen"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Vollbildmodus verwenden"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Desktop-Ansicht verwenden"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Desktop-Windowing verwenden"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Zur Position <xliff:g id="POSITION">%1$d</xliff:g> hinzufügen"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Position ist ungültig."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Kachel bereits hinzugefügt"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Ansicht hinzugefügt"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Ansicht entfernt"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor für Schnelleinstellungen."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Mediensteuerung für <xliff:g id="APP_NAME">%1$s</xliff:g> ausblenden?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Die Mediensitzung kann nicht ausgeblendet werden."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ausblenden"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> von <xliff:g id="ARTIST_NAME">%2$s</xliff:g> wird gerade über <xliff:g id="APP_LABEL">%3$s</xliff:g> wiedergegeben"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> von <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unbekannt"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Alle Kacheln zurücksetzen?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle Schnelleinstellungen-Kacheln werden auf die Standardeinstellungen des Geräts zurückgesetzt"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index 44734ae..2d4d1b6 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Aus"</item>
     <item msgid="4875147066469902392">"An"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nicht verfügbar"</item>
     <item msgid="5044688398303285224">"Aus"</item>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index dade080..c1a9a3a 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Συνδέθηκε στο <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Συνδέθηκε σε <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Αναπτύξτε την ομάδα."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Προσθήκη συσκευής στην ομάδα."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Κατάργηση συσκευής από την ομάδα."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Ανοίξτε την εφαρμογή."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Μη συνδεδεμένο"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Περιαγωγή"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Είσοδος"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Βοηθήματα ακοής"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ενεργοποίηση…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Δεν είναι δυνατή η προσαρμογή της φωτεινότητας, επειδή ελέγχεται από την εφαρμογή στην κορυφή"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Αυτόματη περιστροφή"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Αυτόματη περιστροφή οθόνης"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Τοποθεσία"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ήχοι περιβάλλοντος"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Αριστερά"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Δεξιά"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Ήχοι περιβάλλοντος"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Ήχοι περιβάλλοντος στα αριστερά"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Ήχοι περιβάλλοντος στα δεξιά"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Ανάπτυξη σε ξεχωριστά στοιχεία ελέγχου αριστερά και δεξιά"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Σύμπτυξη σε ενοποιημένο στοιχείο ελέγχου"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Σίγαση ήχων περιβάλλοντος"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Κατάργηση σίγασης ήχων περιβάλλοντος"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Εργαλεία"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Ζωντανοί υπότιτλοι"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Ρυθμίσεις"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Σημείωση"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Κατάργηση αποκλεισμού μικροφώνου συσκευής;"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Κατάργηση αποκλεισμού κάμερας συσκευής;"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ενεργό - Βάσει προσώπου"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Τέλος"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Εφαρμογή"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Απενεργοποίηση ειδοποιήσεων"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Σίγαση"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Προεπιλογή"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Αυτόματο"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Χρήση διαχωρισμού οθόνης με την εφαρμογή στα δεξιά"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Χρήση διαχωρισμού οθόνης με την εφαρμογή στα αριστερά"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Χρήση πλήρους οθόνης"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Χρήση προβολής για υπολογιστές"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Χρήση της λειτουργίας προσαρμογής σε παράθυρο στην επιφάνεια εργασίας"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Εναλλαγή στην εφαρμογή δεξιά ή κάτω κατά τη χρήση διαχωρισμού οθόνης"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Εναλλαγή σε εφαρμογή αριστερά ή επάνω κατά τη χρήση διαχωρισμού οθόνης"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Κατά τον διαχωρισμό οθόνης: αντικατάσταση μιας εφαρμογής με άλλη"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Προσθήκη στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Μη έγκυρη θέση."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Το πλακίδιο έχει ήδη προστεθεί"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Το πλακίδιο προστέθηκε"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Το πλακίδιο καταργήθηκε"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Επεξεργασία γρήγορων ρυθμίσεων."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Απόκρυψη στοιχ. ελέγχου μέσων για <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Αδυναμία απόκρ. τρέχουσας περιόδ. λειτουργ. μέσου."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Απόκρυψη"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Συνέχιση"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ρυθμίσεις"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Γίνεται αναπαραγωγή του <xliff:g id="SONG_NAME">%1$s</xliff:g> από <xliff:g id="ARTIST_NAME">%2$s</xliff:g> στην εφαρμογή <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> από <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index b649db4..76e7071 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Ανενεργό"</item>
     <item msgid="4875147066469902392">"Ενεργό"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Μη διαθέσιμο"</item>
     <item msgid="5044688398303285224">"Ανενεργός"</item>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index fb251dd..1fee916 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Connected to <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Expand group."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Add device to group."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remove device from group."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Open application."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Not connected."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Can\'t adjust brightness because it\'s being controlled by the top app"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Right"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Surroundings"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Left surroundings"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Right surroundings"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expand to left and right separated controls"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Collapse to unified control"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Mute surroundings"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Unmute surroundings"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Settings"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Turn off notifications"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
@@ -910,7 +906,7 @@
     <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="4221409316059910349">"Use fullscreen"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Use desktop view"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Use desktop windowing"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Position invalid."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Tile already added"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Hide this media control for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"The current media session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> is playing from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> of <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
index e17eeb2..34580eb 100644
--- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Off"</item>
     <item msgid="4875147066469902392">"On"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Unavailable"</item>
     <item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 54ef0cc..318f0b4 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -423,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Right"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Surroundings"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Left surroundings"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Right surroundings"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expand to left and right separated controls"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Collapse to unified control"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Mute surroundings"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Unmute surroundings"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Settings"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
@@ -796,7 +794,7 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On - Face-based"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Turn off notifications"</string>
+    <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Turn off"</string>
     <string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
@@ -907,7 +905,7 @@
     <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="4221409316059910349">"Use full screen"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Use desktop view"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Use desktop windowing"</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>
@@ -1003,6 +1001,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Position invalid."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Tile already added"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
@@ -1203,7 +1202,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Hide this media control for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"The current media session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> is playing from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> of <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
index e17eeb2..3014e62 100644
--- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
@@ -56,6 +56,11 @@
     <item msgid="5376619709702103243">"Off"</item>
     <item msgid="4875147066469902392">"On"</item>
   </string-array>
+  <string-array name="tile_states_modes_dnd">
+    <item msgid="6509540227356524582">"Unavailable"</item>
+    <item msgid="8589336868985358191">"Off"</item>
+    <item msgid="726072717827778234">"On"</item>
+  </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Unavailable"</item>
     <item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index fb251dd..1fee916 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Connected to <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Expand group."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Add device to group."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remove device from group."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Open application."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Not connected."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Can\'t adjust brightness because it\'s being controlled by the top app"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Right"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Surroundings"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Left surroundings"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Right surroundings"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expand to left and right separated controls"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Collapse to unified control"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Mute surroundings"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Unmute surroundings"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Settings"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Turn off notifications"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
@@ -910,7 +906,7 @@
     <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="4221409316059910349">"Use fullscreen"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Use desktop view"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Use desktop windowing"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Position invalid."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Tile already added"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Hide this media control for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"The current media session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> is playing from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> of <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
index e17eeb2..34580eb 100644
--- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Off"</item>
     <item msgid="4875147066469902392">"On"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Unavailable"</item>
     <item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index fb251dd..1fee916 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connected to <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Connected to <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Expand group."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Add device to group."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remove device from group."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Open application."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Not connected."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Can\'t adjust brightness because it\'s being controlled by the top app"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Right"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Surroundings"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Left surroundings"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Right surroundings"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expand to left and right separated controls"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Collapse to unified control"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Mute surroundings"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Unmute surroundings"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Settings"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Turn off notifications"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
@@ -910,7 +906,7 @@
     <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="4221409316059910349">"Use fullscreen"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Use desktop view"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Use desktop windowing"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Position invalid."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Tile already added"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Hide this media control for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"The current media session cannot be hidden."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Hide"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> is playing from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> of <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
index e17eeb2..34580eb 100644
--- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Off"</item>
     <item msgid="4875147066469902392">"On"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Unavailable"</item>
     <item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 371638b3..486b43a 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Expandir grupo."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Agregar el dispositivo al grupo."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Quitar el dispositivo del grupo."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Abrir aplicación."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"No conectado"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audífonos"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"La app superior controla el brillo, por lo que no se puede ajustar"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar la pantalla automáticamente"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Sonido envolvente"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Izquierda"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Derecha"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Sonido envolvente"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Entorno de la izquierda"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Entorno de la derecha"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expandir los controles separados a la izquierda y a la derecha"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Contraer al control unificado"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silenciar el sonido envolvente"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Activar el sonido envolvente"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Herramientas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitulado instantáneo"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Configuración"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Quieres desbloquear el micrófono del dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Quieres desbloquear la cámara del dispositivo?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activa - En función del rostro"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Listo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desactivar notificaciones"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silenciada"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Usar la pantalla dividida con la app a la derecha"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Usar la pantalla dividida con la app a la izquierda"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Usar la pantalla completa"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Usar la vista para computadoras de escritorio"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Usar renderización en ventanas para computadoras de escritorio"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Agregar a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posición no válida"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Tarjeta ya agregada"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Se agregó la tarjeta"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Se quitó la tarjeta"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de Configuración rápida"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"¿Ocultar control multimedia para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"No se puede ocultar la sesión multimedia actual."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Se está reproduciendo <xliff:g id="SONG_NAME">%1$s</xliff:g>, de <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> de <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index 91f5f88..3310d2b 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Desactivado"</item>
     <item msgid="4875147066469902392">"Activado"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"No disponible"</item>
     <item msgid="5044688398303285224">"Desactivada"</item>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index b02ab30..ecd3fda 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Mostrar grupo."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Añade un dispositivo al grupo."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Quita un dispositivo del grupo."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Abrir aplicación."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"No conectado."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audífonos"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"No se puede ajustar el brillo porque la aplicación superior lo está controlando"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar pantalla automáticamente"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Alrededores"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Izquierda"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Derecha"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Entorno"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Entorno izquierdo"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Entorno derecho"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expandir a los controles separados de izquierda y derecha"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Contraer al control unificado"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silenciar alrededores"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Dejar de silenciar alrededores"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Herramientas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítulos automáticos"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Ajustes"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Desbloquear el micrófono del dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Desbloquear la cámara del dispositivo?"</string>
@@ -518,7 +513,7 @@
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza hacia la izquierda para iniciar el tutorial de la comunidad"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Cerrar"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Añade, elimina y reordena tus widgets en este espacio"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Añade, elimina y reordena tus widgets"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Añade más widgets"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén pulsado para personalizar los widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activado: basado en caras"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Hecho"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desactivar notificaciones"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silencio"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminado"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Usar la pantalla dividida con la aplicación a la derecha"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Usar la pantalla dividida con la aplicación a la izquierda"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Usar pantalla completa"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Usar vista para ordenador"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Usar el escritorio basado en ventanas"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar a la aplicación de la derecha o de abajo en pantalla dividida"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar a la app de la izquierda o de arriba en pantalla dividida"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Con pantalla dividida: reemplazar una aplicación por otra"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Añadir a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posición no válida."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Tarjeta ya añadida"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Recuadro añadido"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Recuadro quitado"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de ajustes rápidos."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"¿Ocultar este control multimedia para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"La sesión multimedia no se puede ocultar."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ajustes"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Se está reproduciendo <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> de <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"¿Borrar todos los recuadros?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos los recuadros de ajustes rápidos se restablecerán a los ajustes originales del dispositivo"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index c5b6c2e..546190f 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Desactivado"</item>
     <item msgid="4875147066469902392">"Activado"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"No disponible"</item>
     <item msgid="5044688398303285224">"Desactivado"</item>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 578ca4d..2928c96 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ühendatud: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Ühendatud ülekandega <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Grupi laiendamine."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Seadme lisamine gruppi."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Seadme eemaldamine grupist."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Rakenduse avamine."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Ühendus puudub."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Rändlus"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Sisend"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Kuuldeaparaadid"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Sisselülitamine …"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Heledust ei saa reguleerida, kuna seda juhib ülemine rakendus"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. pööramine"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Kuva automaatne pööramine"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Asukoht"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ümbritsevad helid"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vasakule"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Paremale"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Ümbritsevad helid"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Vasak ümbrus"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Parem ümbrus"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Vasaku ja parema poole eraldi juhtimine"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Mõlema poole ühtne juhtimine"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Ümbritsevate helide vaigistamine"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ümbritsevate helide vaigistuse tühistamine"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tööriistad"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Reaalajas subtiitrid"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Seaded"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Märkus"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kas tühistada seadme mikrofoni blokeerimine?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kas tühistada seadme kaamera blokeerimine?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Sees – näopõhine"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Valmis"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Rakenda"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Lülita märguanded välja"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Hääletu"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Vaikeseade"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automaatne"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Jagatud ekraanikuva kasutamine, rakendus kuvatakse paremal"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Jagatud ekraanikuva kasutamine, rakendus kuvatakse vasakul"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Lülita täisekraanile"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Kasuta arvutivaadet"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Kasuta töölaua aknaid"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Sobimatu asukoht."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Asend <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Paan on juba lisatud"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Paan on lisatud"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Paan on eemaldatud"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kiirseadete redigeerija."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Kas peita see rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> meediajuhik?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Praegust meediaseanssi ei saa peita."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Peida"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Jätka"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Seaded"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> esitajalt <xliff:g id="ARTIST_NAME">%2$s</xliff:g> esitatakse rakenduses <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>/<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index e9949bf..1defe92 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Väljas"</item>
     <item msgid="4875147066469902392">"Sees"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Pole saadaval"</item>
     <item msgid="5044688398303285224">"Väljas"</item>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index c6b8f56..99dec01 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> gailura konektatuta."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Hona konektatuta: <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Zabaldu taldea."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Gehitu gailua taldera"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Kendu gailua taldetik."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Ireki aplikazioa."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Konektatu gabe."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Ibiltaritza"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Sarrera"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audifonoak"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktibatzen…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Ezin da argitasuna doitu goiko aplikazioak kontrolatzen duelako"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Biratze automatikoa"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Biratu pantaila automatikoki"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Kokapena"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ingurunea"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ezkerrekoa"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Eskuinekoa"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Ingurunea"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Ezkerreko ingurunea"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Eskuineko ingurunea"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Zabaldu ezkerreko eta eskuineko kontrolatzeko aukerak bereiz erabiltzeko"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Tolestu kontrolatzeko aukerak bateratuta erabiltzeko"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Desaktibatu ingurunearen audioa"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Aktibatu ingurunearen audioa"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tresnak"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Istanteko azpitituluak"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Ezarpenak"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Oharra"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Gailuaren mikrofonoa desblokeatu nahi duzu?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Gailuaren kamera desblokeatu nahi duzu?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktibatuta: aurpegian oinarrituta"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Eginda"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplikatu"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desaktibatu jakinarazpenak"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Isila"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Lehenetsia"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatikoa"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Erabili pantaila zatitua eta ezarri aplikazio hau eskuinean"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Erabili pantaila zatitua eta ezarri aplikazio hau ezkerrean"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Erabili pantaila osoa"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Erabili ordenagailuetarako ikuspegia"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Erabili ordenagailuan leihoak erabiltzeko modua"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren lekuan"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Kokapenak ez du balio."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>garren lekua"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Lauza gehituta dago jada"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Gehitu da lauza"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kendu da lauza"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ezarpen bizkorren editorea."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Multimedia kontrolatzeko aukerak (<xliff:g id="APP_NAME">%1$s</xliff:g>) ezkutatu?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Ezin da ezkutatu multimedia-saioa."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ezkutatu"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Berrekin"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ezarpenak"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) ari da erreproduzitzen <xliff:g id="APP_LABEL">%3$s</xliff:g> bidez"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>/<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ezezagunak"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Lauza guztiak berrezarri nahi dituzu?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Gailuaren jatorrizko ezarpenak berrezarriko dira ezarpen bizkorren lauza guztietan"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index e47b658..923d84f 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Desaktibatuta"</item>
     <item msgid="4875147066469902392">"Aktibatuta"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ez dago erabilgarri"</item>
     <item msgid="5044688398303285224">"Desaktibatuta"</item>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 6f4d1bc..db76009 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"به <xliff:g id="BLUETOOTH">%s</xliff:g> متصل شد."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"به <xliff:g id="CAST">%s</xliff:g> متصل شد."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"گروه را از هم باز می‌کند."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"افزودن دستگاه به گروه."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"حذف دستگاه از گروه."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"برنامه را باز می‌کند."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"متصل نیست."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"فراگردی"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ورودی"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سمعک"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"روشن کردن…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"نمی‌توان روشنایی را تنظیم کرد زیرا برنامه بالایی آن را کنترل می‌کند"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"چرخش خودکار"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"چرخش خودکار صفحه‌نمایش"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"مکان"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"پیرامون"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"چپ"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"راست"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"پیرامون"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"پیرامون چپ"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"پیرامون راست"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ازهم بازکردن برای کنترل‌های جداگانه چپ و راست"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"جمع کردن برای کنترل یکپارچه"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"بی‌صدا کردن پیرامون"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"صدادار کردن پیرامون"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ابزارها"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"زیرنویس زنده ناشنوایان"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"تنظیمات"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"یادداشت"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"میکروفون دستگاه لغو انسداد شود؟"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"دوربین دستگاه لغو انسداد شود؟"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"روشن - براساس چهره"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"تمام"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"اعمال"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"خاموش کردن اعلان‌ها"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"بی‌صدا"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"پیش‌فرض"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"خودکار"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"استفاده از صفحهٔ دونیمه با قرار گرفتن برنامه در سمت راست"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"استفاده از صفحهٔ دونیمه با قرار گرفتن برنامه در سمت چپ"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"استفاده از حالت تمام‌صفحه"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"استفاده از نمای ویژه رایانه رومیزی"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"استفاده از پردازش پنجره‌ای رایانه"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"رفتن به برنامه سمت راست یا پایین درحین استفاده از صفحهٔ دونیمه"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"رفتن به برنامه سمت چپ یا بالا درحین استفاده از صفحهٔ دونیمه"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"درحین صفحهٔ دونیمه: برنامه‌ای را با دیگری جابه‌جا می‌کند"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"افزودن به موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"موقعیت نامعتبر است."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"کاشی قبلاً اضافه شده است"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"کاشی اضافه شد"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"کاشی حذف شد"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ویرایشگر تنظیمات سریع."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"این کنترل رسانه برای <xliff:g id="APP_NAME">%1$s</xliff:g> پنهان شود؟"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"جلسه رسانه کنونی نمی‌تواند پنهان شود."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"پنهان کردن"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"ازسرگیری"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"تنظیمات"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> از <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ازطریق <xliff:g id="APP_LABEL">%3$s</xliff:g> پخش می‌شود"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> از <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"نامشخص"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"همه کاشی‌ها بازنشانی شود؟"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"همه کاشی‌های «تنظیمات فوری» به تنظیمات اصلی دستگاه بازنشانی خواهد شد"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>، <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index 1025f3d..ee94294 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"خاموش"</item>
     <item msgid="4875147066469902392">"روشن"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"دردسترس نیست"</item>
     <item msgid="5044688398303285224">"خاموش"</item>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 527a292..8df9aeb 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -254,10 +254,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Yhteys: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Yhdistetty kohteeseen <xliff:g id="CAST">%s</xliff:g>"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Laajenna ryhmä."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Lisää laite ryhmään."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Poista laite ryhmästä."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Avaa sovellus."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Ei yhteyttä."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -335,8 +333,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Syöttölaite"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Kuulolaitteet"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Otetaan käyttöön…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Kirkkautta ei voi säätää, koska ensisijainen sovellus ohjaa sitä"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automaattinen kääntö"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Käännä näyttöä automaattisesti."</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Sijainti"</string>
@@ -428,18 +425,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ympäristö"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vasen"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Oikea"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Ympäristö"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Ympäristö vasemmalla"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Ympäristö oikealla"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Laajenna vasemmalle ja oikealle erilliset ohjaimet"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Tiivistä yhtenäiseksi säätimeksi"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Mykistä ympäristö"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Poista ympäristön mykistys"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Työkalut"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Livetekstitys"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Asetukset"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Muistiinpano"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kumotaanko laitteen mikrofonin esto?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kumotaanko laitteen kameran esto?"</string>
@@ -801,7 +796,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Päällä – kasvojen perusteella"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Valmis"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Käytä"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Poista ilmoitukset käytöstä"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Äänetön"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Oletus"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automaattinen"</string>
@@ -912,7 +908,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Käytä jaettua näyttöä niin, että sovellus on oikealla"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Käytä jaettua näyttöä niin, että sovellus on vasemmalla"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Käytä koko näytön tilaa"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Käytä tietokonenäkymää"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Käytä työpöydän ikkunointia"</string>
     <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>
@@ -1008,6 +1004,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisää paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Virheellinen sijainti."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Laatta on jo lisätty"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kiekko lisätty"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kiekko poistettu"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Pika-asetusten muokkausnäkymä"</string>
@@ -1208,7 +1205,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Piilotetaanko mediaohjain (<xliff:g id="APP_NAME">%1$s</xliff:g>)?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Tätä median käyttökertaa ei voi piilottaa."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Piilota"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Jatka"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Asetukset"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> soittaa nyt tätä: <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>)"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>/<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1576,6 +1572,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tuntematon"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Nollataanko kaikki laatat?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Kaikki pika-asetuslaatat palautetaan laitteen alkuperäisiin asetuksiin"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index 5452f26..76a77ad 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Poissa päältä"</item>
     <item msgid="4875147066469902392">"Päällä"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ei saatavilla"</item>
     <item msgid="5044688398303285224">"Poissa päältä"</item>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 6a2d7c8..9bd16d9 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connecté à : <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Connecté à <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Développer le groupe."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Ajouter un appareil au groupe."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Retirer l\'appareil du groupe."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Ouvrir l\'appli."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Non connecté"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Itinérance"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Prothèses auditives"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation en cours…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Impossible de régler la luminosité, car elle est contrôlée par l\'appli principale"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Environnement"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Gauche"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Droit"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Environnement"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Environnement à gauche"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Environnement à droite"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Développer les commandes distinctes à gauche et à droite"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Passer au contrôle unifié"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Ignorer les sons de l\'environnement"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Réactiver les sons de l\'environnement"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Outils"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sous-titres instantanés"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Paramètres"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le microphone de l\'appareil?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer l\'appareil photo de l\'appareil?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activé : en fonction du visage"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Terminé"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Appliquer"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Désactiver les notifications"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Mode silencieux"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Par défaut"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Utiliser l\'Écran divisé avec l\'appli à droite"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Utiliser l\'Écran divisé avec l\'appli à gauche"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Utiliser le mode plein écran"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Utiliser l\'affichage sur ordinateur de bureau"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Utiliser le fenêtrage du bureau"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passer à l\'appli à droite ou en dessous avec l\'Écran divisé"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passer à l\'appli à gauche ou au-dessus avec l\'Écran divisé"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"En mode d\'écran divisé : remplacer une appli par une autre"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Position incorrecte."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Tuile déjà ajoutée"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tuile ajoutée"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tuile retirée"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de paramètres rapides."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Masquer ce contrôleur de contenu multimédia pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Impossible de masquer la session multimédia actuelle"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Masquer"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> est en cours de lecteur à partir de <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> de <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Réinitialiser toutes les tuiles?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Toutes les tuiles des paramètres rapides seront réinitialisées aux paramètres par défaut de l\'appareil."</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index e9d3c48..01f3391 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Désactivée"</item>
     <item msgid="4875147066469902392">"Activé"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Non disponible"</item>
     <item msgid="5044688398303285224">"Désactivée"</item>
@@ -68,7 +71,7 @@
   </string-array>
   <string-array name="tile_states_bt">
     <item msgid="5330252067413512277">"Non disponible"</item>
-    <item msgid="5315121904534729843">"Désactivée"</item>
+    <item msgid="5315121904534729843">"Désactivé"</item>
     <item msgid="503679232285959074">"Activé"</item>
   </string-array>
   <string-array name="tile_states_airplane">
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 62916e8..63ac0ca 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Connecté à : <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Connecté à <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Développer le groupe."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Ajouter l\'appareil au groupe."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Supprimer l\'appareil du groupe."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Ouvrir l\'application."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Non connecté"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Itinérance"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Appareils auditifs"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Impossible d\'ajuster la luminosité, car celle-ci est contrôlée par l\'appli principale"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Sons environnants"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Gauche"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Droite"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Sons environnants"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Environnement à gauche"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Environnement à droite"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Développer les commandes gauche et droite"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Réduire en une commande unifiée"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Couper le mode Sons environnants"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Réactiver le mode Sons environnants"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Outils"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sous-titres instantanés"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Paramètres"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le micro de l\'appareil ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer la caméra de l\'appareil ?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Active - En fonction du visage"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"OK"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Appliquer"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Désactiver les notifications"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silencieux"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Par défaut"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Utiliser l\'écran partagé avec l\'appli sur la droite"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Utiliser l\'écran partagé avec l\'appli sur la gauche"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Utiliser le mode plein écran"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Utiliser l\'affichage sur ordinateur"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Utiliser le fenêtrage de l\'ordinateur"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Emplacement non valide."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"La carte a déjà été ajoutée"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Bloc ajouté"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Bloc supprimé"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur Réglages rapides"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Masquer cette commande multimédia pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Session multimédia en cours impossible à masquer."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Masquer"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> est en cours de lecture depuis <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> sur <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Réinitialiser tous les blocs ?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tous les blocs \"Réglages rapides\" seront réinitialisés aux paramètres d\'origine de l\'appareil"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 52c7c0c..620e46c 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Désactivé"</item>
     <item msgid="4875147066469902392">"Activé"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponible"</item>
     <item msgid="5044688398303285224">"Désactivé"</item>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 87980c8..80da559 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Dispositivo conectado: <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Despregar o grupo."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Engadir o dispositivo ao grupo."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Quitar o dispositivo do grupo."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Abrir a aplicación."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Non conectada"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Itinerancia"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audiófonos"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Non se pode axustar o brillo porque o controla a aplicación principal"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Xirar automaticamente"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Xirar pantalla automaticamente"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localización"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambiente"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerdo"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Dereito"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Ambiente"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Ambiente á esquerda"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Ambiente á dereita"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Despregar para controis separados do lado esquerdo e do dereito"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Contraer para control unificado"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silenciar o ambiente"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Activar o son ambiental"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Ferramentas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítulos instantáneos"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Configuración"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Queres desbloquear o micrófono do dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Queres desbloquear a cámara do dispositivo?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activada: baseada na cara"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Feito"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desactivar notificacións"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silenciadas"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Configuración predeterminada"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Usar pantalla dividida coa aplicación na dereita"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Usar pantalla dividida coa aplicación na esquerda"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Usar a pantalla completa"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Usar a vista para ordenadores"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Usar as ventás do ordenador"</string>
     <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>
@@ -997,15 +993,16 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconas das notificacións que teñan baixa prioridade"</string>
     <string name="other" msgid="429768510980739978">"Outros"</string>
-    <string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"activar/desactivar o tamaño do recadro"</string>
-    <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarxeta"</string>
+    <string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"cambiar o tamaño do atallo"</string>
+    <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar atallo"</string>
     <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"engadir o atallo á última posición"</string>
-    <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover tarxeta"</string>
+    <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover atallo"</string>
     <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Engadir o atallo á última posición"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engadir á posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posición non válida."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Atallo xa engadido"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Engadiuse a tarxeta"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Quitouse a tarxeta"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuración rápida."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Queres ocultar este control multimedia para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Non se pode ocultar esta sesión multimedia."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Estase reproducindo <xliff:g id="SONG_NAME">%1$s</xliff:g>, de <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> de <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Categoría descoñecida"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Queres restablecer todos os atallos?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Restablecerase a configuración orixinal do dispositivo para todos os atallos de Configuración rápida"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index 7cf5f82..ca19e0e 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Non"</item>
     <item msgid="4875147066469902392">"Si"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Non dispoñible"</item>
     <item msgid="5044688398303285224">"Non"</item>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index e61d4e1..17bcdf0 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -426,18 +426,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"આસપાસના અવાજો"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ડાબે"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"જમણે"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"આસપાસના અવાજો"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"ડાબી તરફથી આવતા અવાજો"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"જમણી તરફથી આવતા અવાજો"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ડાબે અને જમણે અલગ કરેલા નિયંત્રણો સુધી વિસ્તૃત કરો"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"એકીકૃત નિયંત્રણ સુધી નાનું કરો"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"આસપાસના અવાજો મ્યૂટ કરો"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"આસપાસના અવાજો અનમ્યૂટ કરો"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ટૂલ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"લાઇવ કૅપ્શન"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"સેટિંગ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"નોંધ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ડિવાઇસના માઇક્રોફોનને અનબ્લૉક કરીએ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ડિવાઇસના કૅમેરાને અનબ્લૉક કરીએ?"</string>
@@ -799,7 +797,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ચાલુ છે - ચહેરા આધારિત રોટેશન"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"થઈ ગયું"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"લાગુ કરો"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"નોટિફિકેશન બંધ કરો"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"સાઇલન્ટ"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ડિફૉલ્ટ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ઑટોમૅટિક રીતે"</string>
@@ -910,7 +909,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"હાલની ઍપને જમણી બાજુએ રાખીને વિભાજિત સ્ક્રીનનો ઉપયોગ કરો"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"હાલની ઍપને ડાબી બાજુએ રાખીને વિભાજિત સ્ક્રીનનો ઉપયોગ કરો"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"પૂર્ણ સ્ક્રીનનો ઉપયોગ કરો"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ડેસ્કટૉપ વ્યૂનો ઉપયોગ કરો"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ડેસ્કટૉપ વિન્ડોઇંગનો ઉપયોગ કરો"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે જમણી બાજુ કે નીચેની ઍપ પર સ્વિચ કરો"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે ડાબી બાજુની કે ઉપરની ઍપ પર સ્વિચ કરો"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"વિભાજિત સ્ક્રીન દરમિયાન: એક ઍપને બીજી ઍપમાં બદલો"</string>
@@ -1006,6 +1005,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"જગ્યા પર <xliff:g id="POSITION">%1$d</xliff:g> ઉમેરો"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"સ્થિતિ અમાન્ય છે."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"જગ્યા <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"ટાઇલ પહેલેથી ઉમેરેલી છે"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ટાઇલ ઉમેરી"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ટાઇલ કાઢી નાખી"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ઝડપી સેટિંગ એડિટર."</string>
@@ -1206,7 +1206,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"શું <xliff:g id="APP_NAME">%1$s</xliff:g> માટે મીડિયાના નિયંત્રણો છુપાવીએ?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"હાલનું મીડિયા સત્ર છુપાવી શકાતું નથી."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"છુપાવો"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> પર <xliff:g id="ARTIST_NAME">%2$s</xliff:g>નું <xliff:g id="SONG_NAME">%1$s</xliff:g> ગીત ચાલી રહ્યું છે"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>માંથી <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1574,6 +1573,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"અજાણ"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"તમામ ટાઇલ રીસેટ કરીએ?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"તમામ ઝડપી સેટિંગ ટાઇલને ડિવાઇસના ઑરિજિનલ સેટિંગ પર રીસેટ કરવામાં આવશે"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index a003106..759e436 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"બંધ છે"</item>
     <item msgid="4875147066469902392">"ચાલુ છે"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ઉપલબ્ધ નથી"</item>
     <item msgid="5044688398303285224">"બંધ છે"</item>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 0dfbb4c..f4c05ba 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> से कनेक्ट किया गया."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> से कनेक्ट है."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"ग्रुप को बड़ा करें."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"डिवाइस को ग्रुप में जोड़ें."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"डिवाइस को ग्रुप से हटाएं."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"ऐप्लिकेशन खोलें."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"कनेक्ट नहीं है."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"रोमिंग"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"कान की मशीनें"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ब्लूटूथ चालू हो रहा है…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"स्क्रीन की रोशनी को अडजस्ट नहीं किया जा सकता, क्योंकि इसे टॉप ऐप्लिकेशन कंट्रोल कर रहा है"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रीन का अपने-आप दिशा बदलना (ऑटो-रोटेट)"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"जगह की जानकारी"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"आस-पास का वॉल्यूम"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"बाईं ओर के वॉल्यूम के लिए"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"दाईं ओर के वॉल्यूम के लिए"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"आस-पास का वॉल्यूम"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"बाईं ओर का वॉल्यूम"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"दाईं ओर का वॉल्यूम"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"दाईं और बाईं ओर के वॉल्यूम को अलग-अलग मैनेज करने के लिए, वॉल्यूम पैनल को बड़ा करें"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"यूनिफ़ाइड कंट्रोल पर जाने के लिए पैनल को छोटा करें"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"आस-पास के वॉल्यूम को म्यूट करें"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"आस-पास के वॉल्यूम को अनम्यूट करें"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"टूल"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइव कैप्शन"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"सेटिंग"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"नोट"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"क्या आपको माइक्रोफ़ोन का ऐक्सेस अनब्लॉक करना है?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"क्या आपको कैमरे का ऐक्सेस अनब्लॉक करना है?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"चालू है - चेहरे की गतिविधि के हिसाब से कैमरे को घुमाने की सुविधा"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"हो गया"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"लागू करें"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"सूचनाएं बंद करें"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"बिना आवाज़ के सूचनाएं दिखाएं"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"डिफ़ॉल्ट"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"अपने-आप"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"स्प्लिट स्क्रीन की सुविधा चालू करें और इस ऐप्लिकेशन को दाईं ओर दिखाएं"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"स्प्लिट स्क्रीन की सुविधा चालू करें और इस ऐप्लिकेशन को बाईं ओर दिखाएं"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"फ़ुल स्क्रीन मोड का इस्तेमाल करें"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"डेस्कटॉप व्यू मोड का इस्तेमाल करें"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"डेस्कटॉप विंडोविंग का इस्तेमाल करें"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन पर, दाईं ओर या नीचे के ऐप पर स्विच करने के लिए"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन पर, बाईं ओर या ऊपर के ऐप पर स्विच करने के लिए"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रीन के दौरान: एक ऐप्लिकेशन को दूसरे ऐप्लिकेशन से बदलें"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर जोड़ें"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"मौजूदा जगह अमान्य है."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"टाइल की पोज़िशन <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"टाइल पहले से जोड़ा गया है"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"टाइल जोड़ी गई"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"टाइल हटाई गई"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"त्वरित सेटिंग संपादक."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"क्या <xliff:g id="APP_NAME">%1$s</xliff:g> के लिए, इस मीडिया कंट्रोल को छिपाना है?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"मौजूदा मीडिया सेशन को छिपाया नहीं जा सकता."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"छिपाएं"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"फिर से शुरू करें"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> पर, <xliff:g id="ARTIST_NAME">%2$s</xliff:g> का <xliff:g id="SONG_NAME">%1$s</xliff:g> चल रहा है"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g> में से <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index ae08316..410f25d 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"बंद है"</item>
     <item msgid="4875147066469902392">"चालू है"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"उपलब्ध नहीं है"</item>
     <item msgid="5044688398303285224">"बंद है"</item>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 4dba930..9d25094 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Spojen na <xliff:g id="BLUETOOTH">%s</xliff:g> ."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Povezani ste sa sljedećim uređajem: <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Proširite grupu."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Dodajte uređaj u grupu."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Uklonite uređaj iz grupe."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Otvorite aplikaciju."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Nije povezano."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušna pomagala"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Svjetlina se ne može prilagoditi jer njome upravlja aplikacija pri vrhu"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko zakretanje"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko zakretanje zaslona"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lijevo"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Desno"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Okruženje"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Okruženje s lijeve strane"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Okruženje s desne strane"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Proširi u zasebne kontrole slijeva i zdesna"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Sažmi u objedinjenu kontrolu"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Isključi zvuk okruženja"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Uključi zvuk okruženja"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alati"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatski titlovi"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Postavke"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Napomena"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite li deblokirati mikrofon uređaja?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite li deblokirati kameru uređaja?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na temelju lica"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Primijeni"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Isključi obavijesti"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Bešumno"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Zadano"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Upotreba podijeljenog zaslona s aplikacijom s desne strane"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Upotreba podijeljenog zaslona s aplikacijom s lijeve strane"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Upotreba prikaza na cijelom zaslonu"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Upotreba prikaza na računalu"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Upotreba prikaza u prozorima na računalu"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Položaj nije važeći."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Pločica je već dodana"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kartica je dodana"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kartica je uklonjena"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač brzih postavki."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Želite li sakriti kontroler medija za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Trenutačna medijska sesija ne može se sakriti."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sakrij"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g>, <xliff:g id="ARTIST_NAME">%2$s</xliff:g> reproducira se putem aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> od <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Želite li poništiti sve pločice?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve pločice Brze postavke vratit će se na izvorne postavke uređaja"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index 51b667c..bbfcf84 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Isključeno"</item>
     <item msgid="4875147066469902392">"Uključeno"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nedostupno"</item>
     <item msgid="5044688398303285224">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 891cf28..bdeecec 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Csatlakoztatva a következőhöz: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Csatlakozva a következőhöz: <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Csoport kibontása."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Eszköz hozzáadása csoporthoz."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Eszköz eltávolítása csoportból."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Alkalmazás megnyitása."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Nincs csatlakozva."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Bevitel"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hallókészülék"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Bekapcsolás…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Nem lehet módosítani a fényerőt, mert a felső alkalmazás vezérli"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatikus elforgatás"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatikus képernyőforgatás"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Környezet"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Bal"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Jobb"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Környezet"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Bal oldali környezet"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Jobb oldali környezet"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Kibontás a balra és jobbra elválasztott vezérlőkhöz"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Összecsukás az egységes vezérléshez"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Környezet némítása"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Környezet némításának feloldása"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Eszközök"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Élő feliratozás"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Beállítások"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Megjegyzés"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Feloldja az eszköz mikrofonjának letiltását?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Feloldja az eszköz kamerájának letiltását?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Be: Arcalapú"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Kész"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Alkalmaz"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Az értesítések kikapcsolása"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Néma"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Alapértelmezett"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatikus"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Osztott képernyő használata, az alkalmazás a jobb oldalon van"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Osztott képernyő használata, az alkalmazás a bal oldalon van"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Teljes képernyő használata"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Asztali nézet használata"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Asztali ablakkezelési mód használata"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Hozzáadás a következő pozícióhoz: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Érvénytelen pozíció."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. hely"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"A kártya már hozzá van adva"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kártya hozzáadva"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kártya eltávolítva"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Gyorsbeállítások szerkesztője"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Elrejti ezt a(z) <xliff:g id="APP_NAME">%1$s</xliff:g>-médiavezérlőt?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Az aktuális média-munkamenet nem rejthető el."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Elrejtés"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Folytatás"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Beállítások"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> <xliff:g id="SONG_NAME">%1$s</xliff:g> című száma hallható itt: <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>/<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index c8f7b0f..8bd5721 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Ki"</item>
     <item msgid="4875147066469902392">"Be"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nem áll rendelkezésre"</item>
     <item msgid="5044688398303285224">"Ki"</item>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index f231822..d448486 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Միացված է <xliff:g id="BLUETOOTH">%s</xliff:g>-ին:"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Միացված է <xliff:g id="CAST">%s</xliff:g>-ին:"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Ծավալել խումբը։"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Սարքը ավելացնել խմբին։"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Հեռացնել սարքը խմբից։"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Բացել հավելվածը։"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Միացված չէ:"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Ռոումինգ"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Մուտքագրում"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Լսողական սարք"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Միացում…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Հնարավոր չէ կարգավորել պայծառությունը, քանի որ այն կառավարվում է գլխավոր հավելվածի կողմից"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ինքնապտտում"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ավտոմատ պտտել էկրանը"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Տեղորոշում"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Շրջակայք"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ձախ"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Աջ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Շրջակայք"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Ձախ շրջակայք"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Աջ շրջակայք"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Ծավալել՝ դարձնելով կառավարման աջ և ձախ առանձնացված տարրեր"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Ծալել՝ դարձնելով կառավարման մեկ միասնական տարր"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Անջատել շրջակայքի ձայները"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Միացնել շրջակայքի ձայները"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Գործիքներ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Կենդանի ենթագրեր"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Կարգավորումներ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Նշում"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Արգելահանե՞լ սարքի խոսափողը"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Արգելահանե՞լ սարքի տեսախցիկը"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Միաց․ – Դիմաճանաչման հիման վրա"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Փակել"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Կիրառել"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Անջատել ծանուցումները"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Անձայն"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Կանխադրված"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Ավտոմատ"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Տրոհել էկրանը և տեղավորել այս հավելվածը աջ կողմում"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Տրոհել էկրանը և տեղավորել այս հավելվածը ձախ կողմում"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Օգտագործեք լիաէկրան ռեժիմը"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Օգտագործեք համակարգչային տարբերակը"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Օգտագործել համակարգչի պատուհանի ռեժիմը"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Անցեք աջ կողմի կամ ներքևի հավելվածին տրոհված էկրանի միջոցով"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Անցեք աջ կողմի կամ վերևի հավելվածին տրոհված էկրանի միջոցով"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Տրոհված էկրանի ռեժիմում մեկ հավելվածը փոխարինել մյուսով"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ավելացնել դիրք <xliff:g id="POSITION">%1$d</xliff:g>-ում"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Դիրքն անվավեր է։"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Սալիկն արդեն ավելացվել է"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Սալիկն ավելացվեց"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Սալիկը հեռացվեց"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Արագ կարգավորումների խմբագրիչ:"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Թաքցնե՞լ <xliff:g id="APP_NAME">%1$s</xliff:g>-ի մեդիա աշխատաշրջանի կառավարման տարրը։"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Չհաջողվեց թաքցնել ընթացիկ մուլտիմեդիա աշխատաշրջանը։"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Թաքցնել"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Շարունակել"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Կարգավորումներ"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Այժմ նվագարկվում է <xliff:g id="SONG_NAME">%1$s</xliff:g> երգը <xliff:g id="ARTIST_NAME">%2$s</xliff:g>-ի կատարմամբ <xliff:g id="APP_LABEL">%3$s</xliff:g> հավելվածից"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>՝ <xliff:g id="TOTAL_TIME">%2$s</xliff:g>-ից"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Անհայտ"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Զրոյացնե՞լ բոլոր սալիկները"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Արագ կարգավորումների բոլոր սալիկները կզրոյացվեն սարքի սկզբնական կարգավորումների համաձայն։"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index e7eee79..3497c40 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Անջատված է"</item>
     <item msgid="4875147066469902392">"Միացված է"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Հասանելի չէ"</item>
     <item msgid="5044688398303285224">"Անջատված է"</item>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index e0e69a9..5867bf7 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Terhubung ke <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Terhubung ke <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Luaskan grup."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Tambahkan perangkat ke grup."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Hapus perangkat dari grup."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Buka aplikasi."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Tidak terhubung."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Alat bantu dengar"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Mengaktifkan…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Tidak dapat menyesuaikan kecerahan karena sedang dikontrol oleh aplikasi atas"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Putar Otomatis"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Putar layar otomatis"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Suara sekitar"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kiri"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Kanan"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Suara sekitar"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Suara sekitar kiri"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Suara sekitar kanan"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Luaskan ke kontrol terpisah kiri dan kanan"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Ciutkan ke kontrol terpadu"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Bisukan suara sekitar"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Bunyikan suara sekitar"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alat"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Teks Otomatis"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Setelan"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Catatan"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Berhenti memblokir mikrofon perangkat?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Berhenti memblokir kamera perangkat?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktif - Berbasis deteksi wajah"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Selesai"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Terapkan"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Nonaktifkan notifikasi"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Senyap"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatis"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Gunakan layar terpisah dengan aplikasi di sebelah kanan"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Gunakan layar terpisah dengan aplikasi di sebelah kiri"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Gunakan layar penuh"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Gunakan tampilan desktop"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Gunakan mode jendela desktop"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan ke posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posisi tidak valid."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Kartu telah ditambahkan"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kartu ditambahkan"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kartu dihapus"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor setelan cepat."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Sembunyikan kontrol media untuk <xliff:g id="APP_NAME">%1$s</xliff:g> ini?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Sesi media aktif tidak dapat disembunyikan."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sembunyikan"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Lanjutkan"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Setelan"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> oleh <xliff:g id="ARTIST_NAME">%2$s</xliff:g> sedang diputar dari <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> dari <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tidak diketahui"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset semua kartu?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Semua kartu Setelan Cepat akan direset ke setelan asli perangkat"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index 182924d..7df0b0d 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Nonaktif"</item>
     <item msgid="4875147066469902392">"Aktif"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Tidak tersedia"</item>
     <item msgid="5044688398303285224">"Mati"</item>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index faff4c5..f949400 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Tengt við <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Tengt við <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Stækka hóp."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Bæta tæki við hóp."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Fjarlægja tæki úr hóp."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Opna forrit."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Engin tenging."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Reiki"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Inntak"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Heyrnartæki"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Kveikir…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Ekki er hægt að breyta birtustiginu vegna þess að efsta forritið stjórnar því"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Sjálfvirkur snúningur"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Snúa skjá sjálfkrafa"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Staðsetning"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Umhverfi"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vinstri"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Hægri"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Umhverfi"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Umhverfi til vinstri"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Umhverfi til hægri"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Aðskilja stýringar til vinstri og hægri"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Taka saman í eina stýringu"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Þagga umhverfi"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Kveikja á hljóði umhverfis"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Verkfæri"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Skjátextar í rauntíma"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Stillingar"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Glósa"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Opna fyrir hljóðnema tækisins?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Opna fyrir myndavél tækisins?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Kveikt – út frá andliti"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Lokið"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Nota"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Slökkva á tilkynningum"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Hljóðlaust"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Sjálfgefið"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Sjálfvirk"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Notaðu skjáskiptingu fyrir forritið til hægri"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Notaðu skjáskiptingu fyrir forritið til vinstri"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Nota allan skjáinn"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Nota tölvuútgáfu"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Nota gluggastillingu í tölvu"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bæta við í stöðu <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Staða ógild."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Staða <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Skífu hefur þegar verið bætt við"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Reit bætt við"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Reitur fjarlægður"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Flýtistillingaritill."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Fela þessa efnisstýringu fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Ekki tókst að fela opna efnislotu."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Fela"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Halda áfram"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Stillingar"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> með <xliff:g id="ARTIST_NAME">%2$s</xliff:g> er í spilun á <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> af <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Óþekkt"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Endurstilla alla reiti?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Allir flýtistillingareitir munu endurstillast á upprunalegar stillingar tækisins"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index 58ce0ae..d1b04e0 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Slökkt"</item>
     <item msgid="4875147066469902392">"Kveikt"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ekki í boði"</item>
     <item msgid="5044688398303285224">"Slökkt"</item>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index e6f5dee..708fa78 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -138,7 +138,7 @@
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Interrompi registrazione"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Condivisione dello schermo in corso"</string>
     <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Condivisione di contenuti in corso…"</string>
-    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vuoi interrompere la condivisione dello schermo?"</string>
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Interrompere la condivisione dello schermo?"</string>
     <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Interrompere la condivisione?"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Al momento stai condividendo l\'intero schermo con <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Al momento stai condividendo l\'intero schermo con un\'app"</string>
@@ -423,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Audio ambientale"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sinistra"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Destra"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Audio ambientale"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Audio ambientale a sinistra"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Audio ambientale a destra"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Espandi controlli separati a sinistra e a destra"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Comprimi in controllo unificato"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Disattiva audio ambientale"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Riattiva audio ambientale"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Strumenti"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sottotitoli in tempo reale"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Impostazioni"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vuoi sbloccare il microfono del dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vuoi sbloccare la fotocamera del dispositivo?"</string>
@@ -796,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On - Rotazione basata sul viso"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Fine"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Applica"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Disattiva notifiche"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Modalità silenziosa"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Modalità predefinita"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatico"</string>
@@ -907,7 +906,7 @@
     <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="4221409316059910349">"Utilizza la modalità a schermo intero"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Utilizza la visualizzazione desktop"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Usa windowing del desktop"</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>
@@ -1003,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Aggiungi alla posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posizione non valida."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Riquadro già aggiunto"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Riquadro aggiunto"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Riquadro rimosso"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor di impostazioni rapide."</string>
@@ -1203,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Nascondere questo controllo multimediale per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Imposs. nascondere sessione multimediale corrente."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Nascondi"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Riprendi"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Impostazioni"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> di <xliff:g id="ARTIST_NAME">%2$s</xliff:g> è in riproduzione da <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> di <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index 5c56d08..afbd3d9 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Off"</item>
     <item msgid="4875147066469902392">"On"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Non disponibile"</item>
     <item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 8e2df32..35500fb 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"התבצע חיבור אל <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"מחובר אל <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"הרחבת הקבוצה."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"הוספת מכשיר לקבוצה"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"הסרת מכשיר מקבוצה"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"פתיחת האפליקציה."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"אין חיבור."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"נדידה"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"קלט"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"מכשירי שמיעה"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ההפעלה מתבצעת…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"אי אפשר לשנות את הבהירות כי היא נקבעת על ידי האפליקציה העליונה"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"סיבוב אוטומטי"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"סיבוב אוטומטי של המסך"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"מיקום"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"הרעשים בסביבה"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"שמאל"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ימין"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"הרעשים בסביבה"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"הרעשים בסביבה בצד שמאל"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"הרעשים בסביבה בצד ימין"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"הרחבה לאמצעי בקרה נפרדים לצד שמאל ולצד ימין"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"כיווץ לאמצעי בקרה מאוחד"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"השתקת הרעשים בסביבה"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ביטול השתקת הרעשים בסביבה"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"כלים"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"כתוביות מיידיות"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"הגדרות"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"פתק"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"לבטל את חסימת המיקרופון של המכשיר?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"לבטל את חסימת המצלמה של המכשיר?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"פועל – מבוסס על זיהוי פנים"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"סיום"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"אישור"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"השבתת ההתראות"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"שקט"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ברירת מחדל"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"באופן אוטומטי"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"שימוש במסך מפוצל כשהאפליקציה בצד ימין"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"שימוש במסך מפוצל כשהאפליקציה בצד שמאל"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"שימוש במסך מלא"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"שימוש בתצוגה למחשב"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"עיבוד החלק הנצפה בלבד במחשב"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"מעבר לאפליקציה משמאל או למטה בזמן שימוש במסך מפוצל"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"מעבר לאפליקציה מימין או למעלה בזמן שימוש במסך מפוצל"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"כשהמסך מפוצל: החלפה בין אפליקציה אחת לאחרת"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"הוספה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"המיקום לא תקין."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"כרטיס המידע כבר נוסף"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"הלחצן נוסף"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"הלחצן הוסר"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"עורך הגדרות מהירות."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"להסתיר את בקר המדיה הזה לאפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"לא ניתן להסתיר את סשן המדיה הנוכחי."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"הסתרה"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"המשך"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"הגדרות"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> של <xliff:g id="ARTIST_NAME">%2$s</xliff:g> מופעל מ-<xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> מתוך <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"לא ידוע"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"לאפס את כל הלחצנים?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"כל הלחצנים ב\'הגדרות מהירות\' יאופסו להגדרות המקוריות של המכשיר"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"‫<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index d1bd612a..42aa531 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"כבוי"</item>
     <item msgid="4875147066469902392">"פועל"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"לא זמין"</item>
     <item msgid="5044688398303285224">"כבוי"</item>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 5daa645e..fc792b6 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -423,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"周囲の音"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"右"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"周囲の音"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"周囲の音(左)"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"周囲の音(右)"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"開く - 左右それぞれで制御する"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"閉じる - まとめて制御する"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"周囲の音をミュート"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"周囲の音のミュートを解除"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ツール"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"自動字幕起こし"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"設定"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"注"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"デバイスのマイクのブロックを解除しますか?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"デバイスのカメラのブロックを解除しますか?"</string>
@@ -796,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ON - 顔ベース"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"完了"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"適用"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"通知を OFF にする"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"サイレント"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"デフォルト"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
@@ -907,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"分割画面の使用(アプリを右側に表示)"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"分割画面の使用(アプリを左側に表示)"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"全画面表示に切り替える"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"デスクトップ ビューを使用する"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"デスクトップ ウィンドウを使用する"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"分割画面の使用時に右側または下部のアプリに切り替える"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"分割画面の使用時に左側または上部のアプリに切り替える"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"分割画面中: アプリを順に置換する"</string>
@@ -1003,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> に追加"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"位置が無効です。"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"タイルはすでに追加されています"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"タイルを追加しました"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"タイルを削除しました"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"クイック設定エディタ"</string>
@@ -1203,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> のこのコントロールを非表示にしますか?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"現在のメディア セッションは非表示にできません。"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"非表示"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"再開"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g>(アーティスト名: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>)が <xliff:g id="APP_LABEL">%3$s</xliff:g> で再生中"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>/<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
index cce5ceb..4827ad3 100644
--- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"OFF"</item>
     <item msgid="4875147066469902392">"ON"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"使用不可"</item>
     <item msgid="5044688398303285224">"OFF"</item>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 68f48b0..e13a1b3 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"დაკავშირებულია <xliff:g id="BLUETOOTH">%s</xliff:g>-თან."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"დაკავშირებულია მოწყობილობასთან: <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"ჯგუფის გაფართოება."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"მოწყობილობის ჯგუფში დამატება."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"მოწყობილობის ჯგუფიდან ამოშლა."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"აპლიკაციის გახსნა."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"არ არის დაკავშირებული."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"როუმინგი"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"შეყვანა"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"სმენის მოწყობილობები"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ირთვება…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"სიკაშკაშის კორექტირება ვერ ხერხდება, რადგან ის იმართება გახსნილი აპის მიერ"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ავტოროტაცია"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ეკრანის ავტომატური შეტრიალება"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"მდებარეობა"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"გარემოცვა"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"მარცხენა"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"მარჯვენა"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"გარემოცვა"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"მარცხენა გარემოცვა"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"მარჯვენა გარემოცვა"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"განცალკევებული მართვის საშუალებების გაფართოება მარცხნივ და მარჯვნივ"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ერთიანი მართვის ჩაკეცვა"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"გარემოცვის დადუმება"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"გარემოცვის დადუმების მოხსნა"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ხელსაწყოები"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ავტოსუბტიტრები"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"პარამეტრები"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ჩანიშვნა"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"გსურთ მოწყობილობის მიკროფონის განბლოკვა?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"გსურთ მოწყობილობის კამერის განბლოკვა?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ჩართული — სახის მიხედვით"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"მზადაა"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"მისადაგება"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"შეტყობინებების გამორთვა"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"ჩუმი"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ნაგულისხმევი"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ავტომატური"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ეკრანის გაყოფის გამოყენება აპზე მარჯვნივ"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ეკრანის გაყოფის გამოყენება აპზე მარცხნივ"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"სრული ეკრანის გამოყენება"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"დესკტოპის ხედის გამოყენება"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"დესკტოპის ფანჯრის რეჟიმის გამოყენება"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ეკრანის გაყოფის გამოყენებისას აპზე მარჯვნივ ან ქვემოთ გადართვა"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ეკრანის გაყოფის გამოყენებისას აპზე მარცხნივ ან ზემოთ გადართვა"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"ეკრანის გაყოფის დროს: ერთი აპის მეორით ჩანაცვლება"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"დამატება პოზიციაზე <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"პოზიცია არასწორია."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"მოზაიკის ფილა უკვე დამატებულია"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"მოზაიკის ფილა დაემატა"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"მოზაიკის ფილა ამოიშალა"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"სწრაფი პარამეტრების რედაქტორი."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"დაიმალოს მედიის ეს კონტროლერი <xliff:g id="APP_NAME">%1$s</xliff:g> აპში?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"მედიის მიმდინარე სესიის დამალვა შეუძლებელია."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"დამალვა"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"გაგრძელება"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"პარამეტრები"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g>, <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, უკრავს <xliff:g id="APP_LABEL">%3$s</xliff:g>-დან"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>-დან <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"უცნობი"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"გსურთ ყველა ფილის გადაყენება?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"სწრაფი პარამეტრების ყველა ფილა გადაყენდება მოწყობილობის ორიგინალ პარამეტრებზე"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
index 7a22981..ebf28c8 100644
--- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"გამორთულია"</item>
     <item msgid="4875147066469902392">"ჩართულია"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"მიუწვდომელია"</item>
     <item msgid="5044688398303285224">"გამორთულია"</item>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 1ecc743..5fc8b47 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -247,15 +247,13 @@
     <string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Құрылғы деректерін конфигурациялау үшін басыңыз."</string>
     <string name="accessibility_bluetooth_device_settings_gear_with_name" msgid="114373701123165491">"<xliff:g id="DEVICE_NAME">%s</xliff:g>. Құрылғы мәліметтерін конфигурациялау"</string>
     <string name="accessibility_bluetooth_device_settings_see_all" msgid="5260390270128256620">"Барлық құрылғыны көру"</string>
-    <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="7988547106800504256">"Жаңа құрылғыны жұптау"</string>
+    <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="7988547106800504256">"Жаңа құрылғымен жұптау"</string>
     <string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарея зарядының мөлшері белгісіз."</string>
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> қосылған."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> трансляциясына қосылды."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Топты жайыңыз."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Құрылғыны топқа қосады."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Құрылғыны топтан өшіреді."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Қолданбаны ашыңыз."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Жалғанбаған."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Роуминг"</string>
@@ -308,7 +306,7 @@
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Жұптасқан құрылғылар жоқ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Құрылғыны жалғау не ажырату үшін түртіңіз."</string>
-    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Жаңа құрылғыны жұптау"</string>
+    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Жаңа құрылғымен жұптау"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Барлығын көру"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ты пайдалану"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Қосылды"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Кіріс"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Есту аппараттары"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Қосылып жатыр…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Жарықтықты реттеу мүмкін емес, себебі ол жетекші қолданба арқылы басқарылады."</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматты түрде бұру"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматты айналатын экран"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Локация"</string>
@@ -418,7 +415,7 @@
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Қосулы"</string>
     <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратулы"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Есту құрылғылары"</string>
-    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңа құрылғыны жұптау"</string>
+    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңа құрылғымен жұптау"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңа құрылғыны жұптау үшін басыңыз."</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Параметрлер жинағын жаңарту мүмкін болмады."</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Параметрлер жинағы"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Айнала"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Сол жақ"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Оң жақ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Қоршаған дыбыстар"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Сол жақтағы дыбыстар"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Оң жақтағы дыбыстар"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Сол жақ және оң жақ бөлек бақылау құралдарына жаю"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Біріктірілген бақылау құралына жию"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Айналаның дыбысын өшіру"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Айналаның дыбысын қосу"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Құралдар"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Параметрлер"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Ескертпе"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Құрылғы микрофонын блоктан шығару керек пе?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Құрылғы камерасын блоктан шығару керек пе?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Қосулы – бет негізінде"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Дайын"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Қолдану"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Хабарландыруларды өшіру"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Үнсіз"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Әдепкі"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматты"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Қолданбаны бөлінген экранның оң жағынан пайдалану"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Қолданбаны бөлінген экранның сол жағынан пайдалану"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Толық экранды пайдалану"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Компьютерлік нұсқаны пайдалану"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Компьютер терезесін пайдалану"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлінген экранда оң не төмен жақтағы қолданбаға ауысу"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлінген экранда сол не жоғары жақтағы қолданбаға ауысу"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлу кезінде: бір қолданбаны басқасымен алмастыру"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> орнына қосу"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Орын жарамсыз."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> орны"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Бөлшек қосылып қойған."</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Бөлшек қосылды."</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Бөлшек өшірілді."</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Жылдам параметрлер өңдегіші."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін медиа контроллері жасырылсын ба?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Ағымдағы мультимедиа сеансын жасыру мүмкін емес."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Жасыру"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Жалғастыру"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> қолданбасында <xliff:g id="ARTIST_NAME">%2$s</xliff:g> орындайтын \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" әні ойнатылуда."</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>/<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Белгісіз"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Барлық бөлшекті бастапқы күйге қайтару керек пе?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Барлық \"Жылдам параметрлер\" бөлшегі құрылғының бастапқы параметрлеріне қайтарылады."</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
index 2b839c8..45316aa 100644
--- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Өшірулі"</item>
     <item msgid="4875147066469902392">"Қосулы"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Қолжетімсіз"</item>
     <item msgid="5044688398303285224">"Өшірулі"</item>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 8c5ea90..579c509 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"បាន​ភ្ជាប់​ទៅ <xliff:g id="BLUETOOTH">%s</xliff:g> ។"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"បានភ្ជាប់ទៅ <xliff:g id="CAST">%s</xliff:g>"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"ពង្រីកក្រុម។"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"បញ្ចូលឧបករណ៍ទៅក្រុម។"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"ដកឧបករណ៍ចេញពីក្រុម។"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"បើកកម្មវិធី។"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"មិន​បាន​តភ្ជាប់​។"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"រ៉ូ​មីង"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"បញ្ចូល"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"កំពុង​បើក..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"មិនអាចកែតម្រូវកម្រិតពន្លឺបានទេ ដោយសារវាកំពុងស្ថិតក្រោមការគ្រប់គ្រងរបស់កម្មវិធីខាងលើគេ"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"បង្វិល​ស្វ័យ​ប្រវត្តិ"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"បង្វិលអេក្រង់ស្វ័យប្រវត្តិ"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"ទី​តាំង​"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"មជ្ឈដ្ឋានជុំវិញ"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ឆ្វេង"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ស្ដាំ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"មជ្ឈដ្ឋានជុំវិញ"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"មជ្ឈដ្ឋានជុំវិញខាងឆ្វេង"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"មជ្ឈដ្ឋានជុំវិញខាងស្ដាំ"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ពង្រីកទៅជាការគ្រប់គ្រងខាងឆ្វេង និងខាងស្ដាំដាច់ដោយឡែកពីគ្នា"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"បង្រួមទៅជាការគ្រប់គ្រងដែលបានរួមបញ្ចូលគ្នា"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"បិទសំឡេងមជ្ឈដ្ឋានជុំវិញ"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"បើកសំឡេងមជ្ឈដ្ឋានជុំវិញ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ឧបករណ៍"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"អក្សររត់ក្នុងពេលជាក់ស្ដែង"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"ការកំណត់"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"កំណត់ចំណាំ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ឈប់ទប់ស្កាត់​មីក្រូហ្វូន​របស់ឧបករណ៍ឬ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ឈប់ទប់ស្កាត់​កាមេរ៉ា​របស់ឧបករណ៍ឬ?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"បើក - ផ្អែកលើមុខ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"រួចរាល់"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ប្រើ"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"បិទ​ការជូន​ដំណឹង"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"ស្ងាត់"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"លំនាំដើម"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ស្វ័យប្រវត្តិ"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ប្រើមុខងារបំបែកអេក្រង់ជាមួយកម្មវិធីនៅខាងស្ដាំ"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ប្រើមុខងារបំបែកអេក្រង់ជាមួយកម្មវិធីនៅខាងឆ្វេង"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"ប្រើអេក្រង់ពេញ"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ប្រើទិដ្ឋភាព​លើកុំព្យូទ័រ"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ប្រើមុខងារវិនដូកុំព្យូទ័រ"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ប្ដូរទៅកម្មវិធីនៅខាងស្ដាំ ឬខាងក្រោម ពេលកំពុងប្រើមុខងារ​បំបែកអេក្រង់"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ប្ដូរទៅកម្មវិធីនៅខាងឆ្វេង ឬខាងលើ ពេលកំពុងប្រើមុខងារ​បំបែកអេក្រង់"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"ក្នុងអំឡុងពេលប្រើមុខងារបំបែកអេក្រង់៖ ជំនួសកម្មវិធីពីមួយទៅមួយទៀត"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"បញ្ចូលទៅ​ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ទីតាំងគ្មានសុពលភាព។"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"បានបញ្ចូលប្រអប់រួចហើយ"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"បានបញ្ចូលប្រអប់"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"បាន​ផ្លាស់ទី​ប្រអប់"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"កម្មវិធីកែការកំណត់រហ័ស"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"លាក់ផ្ទាំង​គ្រប់គ្រង​មេឌៀនេះសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"មិនអាចលាក់វគ្គមេឌៀបច្ចុប្បន្នបានទេ។"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"លាក់"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"បន្ត"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ការកំណត់"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ច្រៀងដោយ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> កំពុងចាក់ពី <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> នៃ <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml
index 7085509..ec12008 100644
--- a/packages/SystemUI/res/values-km/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"បិទ"</item>
     <item msgid="4875147066469902392">"បើក"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"មិនមានទេ"</item>
     <item msgid="5044688398303285224">"បិទ"</item>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 7c785d9..c84b33b 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ಗೆ ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> ಗೆ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"ಗುಂಪು ವಿಸ್ತರಿಸಿ."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"ಸಾಧನವನ್ನು ಗುಂಪಿಗೆ ಸೇರಿಸಿ."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"ಸಾಧನವನ್ನು ಗುಂಪಿನಿಂದ ತೆಗೆದುಹಾಕಿ."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"ಅಪ್ಲಿಕೇಶನ್ ತೆರೆಯಿರಿ."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"ರೋಮಿಂಗ್"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ಇನ್‌ಪುಟ್"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ಆನ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"ಬ್ರೈಟ್‌ನೆಸ್ ಅನ್ನು ಹೊಂದಾಣಿಕೆ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ, ಏಕೆಂದರೆ ಅದನ್ನು ಟಾಪ್ ಆ್ಯಪ್ ನಿಯಂತ್ರಿಸುತ್ತಿದೆ"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ಸ್ವಯಂ-ತಿರುಗುವಿಕೆ"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ಪರದೆಯನ್ನು ಸ್ವಯಂ-ತಿರುಗಿಸಿ"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"ಸ್ಥಳ"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ಎಡ"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ಬಲ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"ಎಡಭಾಗದ ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"ಬಲಭಾಗದ ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ಪ್ರತ್ಯೇಕ ಎಡ ಮತ್ತು ಬಲ ಕಂಟ್ರೋಲ್‌ಗಳಿಗಾಗಿ ವಿಸ್ತರಿಸಿ"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ಯೂನಿಫೈಡ್ ಕಂಟ್ರೋಲ್‌ಗಾಗಿ ಕುಗ್ಗಿಸಿ"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್ ಅನ್ನು ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್ ಅನ್ನು ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ಟೂಲ್‌ಗಳು"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ಟಿಪ್ಪಣಿ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ಆನ್ ಆಗಿದೆ - ಮುಖ-ಆಧಾರಿತ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ಅನ್ವಯಿಸಿ"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಫ್ ಮಾಡಿ"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"ನಿಶ್ಶಬ್ದ"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ಡೀಫಾಲ್ಟ್"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ಸ್ವಯಂಚಾಲಿತ"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ಬಲಭಾಗದಲ್ಲಿ ಆ್ಯಪ್ ಮೂಲಕ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಬಳಸಿ"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ಎಡಭಾಗದಲ್ಲಿ ಆ್ಯಪ್ ಮೂಲಕ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಬಳಸಿ"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"ಫುಲ್‌ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಳಸಿ"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ಡೆಸ್ಕ್‌ಟಾಪ್ ವೀಕ್ಷಣೆಯನ್ನು ಬಳಸಿ"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ಡೆಸ್ಕ್‌ಟಾಪ್ ವಿಂಡೋಯಿಂಗ್ ಅನ್ನು ಬಳಸಿ"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ಬಳಸುವಾಗ ಬಲಭಾಗ ಅಥವಾ ಕೆಳಭಾಗದಲ್ಲಿರುವ ಆ್ಯಪ್‌ಗೆ ಬದಲಿಸಿ"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ಬಳಸುವಾಗ ಎಡಭಾಗ ಅಥವಾ ಮೇಲ್ಭಾಗದಲ್ಲಿರುವ ಆ್ಯಪ್‌ಗೆ ಬದಲಿಸಿ"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸುವ ಸಮಯದಲ್ಲಿ: ಒಂದು ಆ್ಯಪ್‌ನಿಂದ ಮತ್ತೊಂದು ಆ್ಯಪ್‌ಗೆ ಬದಲಿಸಿ"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸೇರಿಸಿ"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ಸ್ಥಾನವು ಅಮಾನ್ಯವಾಗಿದೆ."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"ಟೈಲ್ ಅನ್ನು ಈಗಾಗಲೇ ಸೇರಿಸಲಾಗಿದೆ"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ಟೈಲ್ ಸೇರಿಸಲಾಗಿದೆ"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ಟೈಲ್ ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‍ಗಳ ಎಡಿಟರ್."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಈ ಮಾಧ್ಯಮ ಕಂಟ್ರೋಲ್‌ಗಳನ್ನು ಮರೆಮಾಡಬೇಕೆ?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"ಪ್ರಸ್ತುತ ಮಾಧ್ಯಮ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ಮರೆಮಾಡಿ"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"ಪುನರಾರಂಭಿಸಿ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ಅವರ <xliff:g id="SONG_NAME">%1$s</xliff:g> ಹಾಡನ್ನು <xliff:g id="APP_LABEL">%3$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g> ರಲ್ಲಿ <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ಅಪರಿಚಿತ"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ಎಲ್ಲಾ ಟೈಲ್‌ಗಳನ್ನು ರೀಸೆಟ್ ಮಾಡಬೇಕೆ?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ಎಲ್ಲಾ ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಟೈಲ್‌ಗಳನ್ನು ಸಾಧನದ ಮೂಲ ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ರೀಸೆಟ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index 487be13..ad57922 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"ಆಫ್"</item>
     <item msgid="4875147066469902392">"ಆನ್"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ಲಭ್ಯವಿಲ್ಲ"</item>
     <item msgid="5044688398303285224">"ಆಫ್"</item>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b3c21b1..048050c 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>에 연결되었습니다."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g>에 연결됨"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"그룹을 펼칩니다."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"기기를 그룹에 추가합니다."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"그룹에서 기기를 삭제합니다."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"애플리케이션을 엽니다."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"연결되지 않았습니다."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"로밍"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"입력"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"보청기"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"켜는 중..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"상위 앱에서 밝기를 제어하고 있으므로 밝기를 조절할 수 없습니다."</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"자동 회전"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"화면 자동 회전"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"위치"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"주변 소리"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"왼쪽"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"오른쪽"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"주변 소리"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"왼쪽 주변 소리"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"오른쪽 주변 소리"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"왼쪽 및 오른쪽 개별 제어로 확장"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"통합 제어로 축소"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"주변 소리 음소거"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"주변 소리 음소거 해제"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"도구"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"실시간 자막"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"설정"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"메모"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"기기 마이크를 차단 해제하시겠습니까?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"기기 카메라를 차단 해제하시겠습니까?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"켜짐 - 얼굴 기준"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"완료"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"적용"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"알림 사용 중지"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"무음"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"기본값"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"자동"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"앱이 오른쪽에 오도록 화면 분할 사용"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"앱이 왼쪽에 오도록 화면 분할 사용"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"전체 화면 사용"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"데스크톱 뷰 사용"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"데스크톱 창 사용"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"화면 분할을 사용하는 중에 오른쪽 또는 아래쪽에 있는 앱으로 전환"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"화면 분할을 사용하는 중에 왼쪽 또는 위쪽에 있는 앱으로 전환하기"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"화면 분할 중: 다른 앱으로 바꾸기"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> 위치에 추가"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"위치가 잘못되었습니다."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> 위치"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"타일이 이미 추가되어 있음"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"타일 추가됨"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"타일 삭제됨"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"빠른 설정 편집기"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g>의 미디어 컨트롤을 숨길까요?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"현재 미디어 세션은 숨길 수 없습니다."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"숨기기"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"다시 시작"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"설정"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g>에서 <xliff:g id="ARTIST_NAME">%2$s</xliff:g>의 <xliff:g id="SONG_NAME">%1$s</xliff:g> 재생 중"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>/<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"알 수 없음"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"모든 타일을 재설정하시겠습니까?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"모든 빠른 설정 타일이 기기의 원래 설정으로 재설정됩니다."</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index dfdcefe..49113ee 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"꺼짐"</item>
     <item msgid="4875147066469902392">"켜짐"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"이용 불가"</item>
     <item msgid="5044688398303285224">"꺼짐"</item>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 9aedbb5..e6b1081 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> менен туташкан."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> менен туташты."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Топту жайып көрсөтүү."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Түзмөктү топко кошуңуз."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Түзмөктү топтон алып салыңыз."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Колдонмону ачуу."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Интернет жок."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Роуминг"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Киргизүү"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Угуу аппараттары"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Күйгүзүлүүдө…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Жарыктыкты тууралоого болбойт, анткени аны жогорку колдонмо көзөмөлдөйт"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авто буруу"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Экранды авто буруу"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Жайгашкан жер"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Айланадагы үндөр"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Сол"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Оң"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Айланадагы үндөр"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Сол жактагы үндөр"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Оң жактагы үндөр"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Cол жана оң жактагы өзүнчө башкаруу элементтерине жайып көрсөтүү"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Бирдиктүү башкаруу элементине жыйыштыруу"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Айланадагы үндөрдү басуу"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Айланадагы үндөрдү чыгаруу"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Куралдар"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Ыкчам коштомо жазуулар"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Параметрлер"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Учкай маалымат"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Түзмөктүн микрофонун бөгөттөн чыгарасызбы?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Түзмөктүн камерасын бөгөттөн чыгарасызбы?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Күйүк – Жүздүн негизинде"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Бүттү"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Колдонуу"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Билдирмелерди өчүрүү"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Үнсүз"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Демейки"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматтык"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Колдонмону оңго жылдырып, экранды бөлүү"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Колдонмону солго жылдырып, экранды бөлүү"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Толук экранды колдонуу"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Компьютердик версияны колдонуу"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Иш тактанын терезелерин колдонуу"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлүнгөн экранда сол же төмөн жактагы колдонмого которулуу"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлүнгөн экранды колдонуп жатканда сол же жогору жактагы колдонмого которулуңуз"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлүү режиминде бир колдонмону экинчисине алмаштыруу"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>-позицияга кошуу"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Абал жараксыз."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>-позиция"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Карточка кошулган"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Карта кошулду"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Карта өчүрүлдү"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ыкчам параметрлер түзөткүчү."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> \'да ушул медиа башкарууну жашырасызбы?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Учурдагы медиа сеансын жашыруу мүмкүн эмес."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Жашыруу"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Улантуу"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ыры (аткаруучу: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) <xliff:g id="APP_LABEL">%3$s</xliff:g> колдонмосунан ойнотулуп жатат"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g> ичинен <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Белгисиз"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Бардык параметрлерди кайра коесузбу?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Бардык ыкчам параметрлер түзмөктүн баштапкы маанилерине кайтарылат"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index 6077f66..68f8987 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Өчүк"</item>
     <item msgid="4875147066469902392">"Күйүк"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Жеткиликсиз"</item>
     <item msgid="5044688398303285224">"Өчүк"</item>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 47b3235..e7229e8 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"ເຊື່ອມ​ຕໍ່​ຫາ <xliff:g id="BLUETOOTH">%s</xliff:g> ແລ້ວ."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"ເຊື່ອມຕໍ່ຫາ <xliff:g id="CAST">%s</xliff:g> ແລ້ວ."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"ຂະຫຍາຍກຸ່ມ."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"ເພີ່ມອຸປະກອນໃສ່ໃນກຸ່ມ."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"ລຶບອຸປະກອນອອກຈາກກຸ່ມ."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"ເປີດແອັບພລິເຄຊັນ."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"ບໍ່ໄດ້ເຊື່ອມຕໍ່."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"ໂຣມມິງ"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ການປ້ອນຂໍ້ມູນ"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ເຄື່ອງຊ່ວຍຟັງ"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ກຳລັງເປີດ..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"ບໍ່ສາມາດປັບຄວາມສະຫວ່າງໄດ້ເນື່ອງຈາກຄວບຄຸມໂດຍແອັບທາງເທິງ"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ໝຸນ​ອັດ​ຕະ​ໂນ​ມັດ"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ໝຸນໜ້າຈໍອັດຕະໂນມັດ"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"ສະຖານທີ່"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ສຽງແວດລ້ອມ"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ຊ້າຍ"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ຂວາ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"ສຽງແວດລ້ອມ"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"ສຽງແວດລ້ອມຂ້າງຊ້າຍ"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"ສຽງແວດລ້ອມຂ້າງຂວາ"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ຂະຫຍາຍເປັນການຄວບຄຸມທີ່ແຍກເບື້ອງຊ້າຍ ແລະ ຂວາ"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ຫຍໍ້ລົງເປັນການຄວບຄຸມແບບຮວມ"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ປິດສຽງແວດລ້ອມ"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ເຊົາປິດສຽງແວດລ້ອມ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ເຄື່ອງມື"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ຄຳບັນຍາຍສົດ"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"ການຕັ້ງຄ່າ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ບັນທຶກ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ປົດບລັອກໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ປົດບລັອກກ້ອງຖ່າຍຮູບອຸ​ປະ​ກອນບໍ?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ເປີດ - ອ້າງອີງໃບໜ້າ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ແລ້ວໆ"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ນຳໃຊ້"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"ປິດການແຈ້ງເຕືອນ"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"ປິດສຽງ"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ຄ່າເລີ່ມຕົ້ນ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ອັດຕະໂນມັດ"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ໃຊ້ໂໝດແບ່ງໜ້າຈໍໂດຍໃຫ້ແອັບຢູ່ເບື້ອງຂວາ"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ໃຊ້ໂໝດແບ່ງໜ້າຈໍໂດຍໃຫ້ແອັບຢູ່ເບື້ອງຊ້າຍ"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"ໃຊ້ແບບເຕັມຈໍ"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ໃຊ້ມຸມມອງເດັສທັອບ"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ໃຊ້ໜ້າຈໍເດັສທັອບ"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ສະຫຼັບໄປໃຊ້ແອັບຢູ່ຂວາ ຫຼື ທາງລຸ່ມໃນຂະນະທີ່ໃຊ້ແບ່ງໜ້າຈໍ"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ສະຫຼັບໄປໃຊ້ແອັບຢູ່ຊ້າຍ ຫຼື ທາງເທິງໃນຂະນະທີ່ໃຊ້ແບ່ງໜ້າຈໍ"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"ໃນລະຫວ່າງແບ່ງໜ້າຈໍ: ໃຫ້ປ່ຽນຈາກແອັບໜຶ່ງເປັນອີກແອັບໜຶ່ງ"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ເພີ່ມໃສ່ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ຕຳແໜ່ງບໍ່ຖືກຕ້ອງ."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"ເພີ່ມແຜ່ນແລ້ວ"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ເພີ່ມແຜ່ນແລ້ວ"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ລຶບແຜ່ນແລ້ວ"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ຕົວແກ້ໄຂການຕັ້ງຄ່າດ່ວນ"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"ເຊື່ອງຕົວຄວບຄຸມມີເດຍນີ້ສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"ບໍ່ສາມາດເຊື່ອງເຊດຊັນມີເດຍປັດຈຸບັນໄດ້."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ເຊື່ອງ"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"ສືບຕໍ່"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ການຕັ້ງຄ່າ"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ໂດຍ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ກຳລັງຫຼິ້ນຈາກ <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> ຈາກ <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ບໍ່ຮູ້ຈັກ"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ຣີເຊັດແຜ່ນທັງໝົດບໍ?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ແຜ່ນການຕັ້ງຄ່າດ່ວນທັງໝົດຈະຣີເຊັດເປັນການຕັ້ງຄ່າແບບເກົ່າຂອງອຸປະກອນ"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
index 294d6af..90390e2 100644
--- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"ປິດ"</item>
     <item msgid="4875147066469902392">"ເປີດ"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
     <item msgid="5044688398303285224">"ປິດ"</item>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 5971e73..6563cb9 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Prisijungta prie „<xliff:g id="BLUETOOTH">%s</xliff:g>“."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Prisijungta prie <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Išskleisti grupę."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Pridėti įrenginį prie grupės."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Pašalinti įrenginį iš grupės."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Atidaryti programą."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Neprijungta."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Tarptinklinis ryšys"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Įvestis"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Klausos aparatai"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Įjungiama…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Negalima koreguoti šviesumo, nes jį valdo viršuje esanti programa"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatinis pasukimas"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatiškai sukti ekraną"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Vietovė"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Aplinka"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kairė"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Dešinė"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Aplinka"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Aplinka kairėje"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Aplinka dešinėje"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Išskleisti į atskirus kairįjį ir dešinįjį valdiklius"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Sutraukti į bendrą valdiklį"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Nutildyti aplinką"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Įjungti aplinkos garsą"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Įrankiai"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitrai realiuoju laiku"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Nustatymai"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Pastaba"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Panaikinti įrenginio mikrofono blokavimą?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Panaikinti įrenginio fotoaparato blokavimą?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Įjungta – pagal veidą"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Atlikta"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Taikyti"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Išjungti pranešimus"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Tylūs"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Numatytasis"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatinis"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Naudokite išskaidyto ekrano režimą su programa dešinėje"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Naudokite išskaidyto ekrano režimą su programa kairėje"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Naudoti viso ekrano režimą"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Naudoti rodinio versiją staliniams kompiuteriams"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Įjungti darbalaukio pateikimo lange režimą"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridėkite <xliff:g id="POSITION">%1$d</xliff:g> pozicijoje"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Padėtis netinkama."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> pozicija"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Išklotinė jau pridėta"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Išklotinė pridėta"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Išklotinė pašalinta"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sparčiųjų nustatymų redagavimo priemonė."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Slėpti šį programos „<xliff:g id="APP_NAME">%1$s</xliff:g>“ medijos valdiklį?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Dabartinio medijos seanso negalima paslėpti."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Slėpti"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Tęsti"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nustatymai"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> – „<xliff:g id="SONG_NAME">%1$s</xliff:g>“ leidžiama iš „<xliff:g id="APP_LABEL">%3$s</xliff:g>“"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> iš <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index 46c6f69..3e1f3c7 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Išjungta"</item>
     <item msgid="4875147066469902392">"Įjungta"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nepasiekiama"</item>
     <item msgid="5044688398303285224">"Išjungta"</item>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index bb8d702..0c030b7 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ir izveidots savienojum ar <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Savienots ar ierīci <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Izvērst grupu."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Pievienot ierīci grupai."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Noņemt ierīci no grupas."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Atvērt lietojumprogrammu."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Savienojums nav izveidots."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Viesabonēšana"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ievade"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Dzirdes aparāti"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Notiek ieslēgšana…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Nevar mainīt spilgtumu, jo to kontrolē lietotne augšpusē"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automātiska pagriešana"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automātiska ekrāna pagriešana"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Atrašanās vieta"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Apkārtnes skaņas"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Pa kreisi"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Pa labi"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Apkārtnes skaņas"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Apkārtnes skaņas pa kreisi"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Apkārtnes skaņas pa labi"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Izvērst, lai rādītu atsevišķu kreiso un labo vadīklu"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Sakļaut, lai rādītu vienotu vadīklu"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Izslēgt apkārtnes skaņas"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ieslēgt apkārtnes skaņas"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Rīki"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitri reāllaikā"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Iestatījumi"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Piezīme"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vai atbloķēt ierīces mikrofonu?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vai vēlaties atbloķēt ierīces kameru?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ieslēgta — ar sejas noteikšanu"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gatavs"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Lietot"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Izslēgt paziņojumus"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Klusums"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Noklusējums"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automātiski"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Izmantot ekrāna sadalīšanu ar lietotni labajā pusē"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Izmantot ekrāna sadalīšanu ar lietotni kreisajā pusē"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Izmantot pilnekrāna režīmu"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Izmantot skatu datorā"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Izmantot darbvirsmas logu režīmu"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pievienot elementu pozīcijā numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Nederīga pozīcija."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozīcija numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Elements jau ir pievienots"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Elements ir pievienots"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Elements ir noņemts"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ātro iestatījumu redaktors."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Vai paslēpt šo lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> multivides vadīklu?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Pašreizējo multivides sesiju nevar paslēpt."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Paslēpt"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Atsākt"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Iestatījumi"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Tiek atskaņots fails “<xliff:g id="SONG_NAME">%1$s</xliff:g>” (izpildītājs: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) no lietotnes <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> no <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nezināma"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vai atiestatīt visus elementus?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Visiem ātro iestatījumu elementiem tiks atiestatīti sākotnējie iestatījumi"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index 8e9268a..e55babc 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Izslēgts"</item>
     <item msgid="4875147066469902392">"Ieslēgts"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nav pieejams"</item>
     <item msgid="5044688398303285224">"Izslēgts"</item>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 189390f..b798f45 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Поврзано со <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Поврзано со <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Проширете ја групата."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Додај го уредот во групата."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Отстрани го уредот од групата."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Отворете ја апликацијата."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Не е поврзана"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Роаминг"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Влез"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слушни помагала"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Се вклучува…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Не може да се приспособи осветленоста бидејќи е контролирана од горната апликација"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматско ротирање"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматско ротирање на екранот"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Опкружување"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Лево"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Десно"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Опкружување"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Опкружување одлево"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Опкружување оддесно"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Прошири на одвоените контроли одлево и оддесно"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Собери на унифицирана контрола"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Исклучи го звукот на опкружувањето"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Вклучи го звукот на опкружувањето"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Алатки"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Автоматски титлови"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Поставки"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Белешка"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се одблокира пристапот до микрофонот на уредот?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се одблокира пристапот до камерата на уредот?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Вклучено - според лице"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Примени"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Исклучи известувања"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Безгласно"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Стандардно"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Користете поделен екран со апликацијата оддесно"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Користете поделен екран со апликацијата одлево"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Користете цел екран"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Користете приказ на компјутер"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Користете „Режим со прозорци на работната површина“"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Префрлете се на апликацијата десно или долу при користењето поделен екран"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Префрлете се на апликацијата лево или горе при користењето поделен екран"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"При поделен екран: префрлете ги аплик. од едната на другата страна"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додавање на позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Позицијата е погрешна."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Плочката е веќе додадена"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Додадена е плочка"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Отстранета е плочка"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уредник за брзи поставки."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Да се скријат контролите за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Аудиовизуелнава сесија не може да се скрие."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Скриј"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Продолжи"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Поставки"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> од <xliff:g id="ARTIST_NAME">%2$s</xliff:g> е пуштено на <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> од <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Непознато"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Да се ресетираат сите плочки?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Сите плочки на „Брзи поставки“ ќе се ресетираат на првичните поставки на уредот"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index 752c386..8986ca5 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Исклучено"</item>
     <item msgid="4875147066469902392">"Вклучено"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Недостапно"</item>
     <item msgid="5044688398303285224">"Исклучено"</item>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 7e4282f..eb7da33 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -423,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"സറൗണ്ടിംഗ്‌സ്"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ഇടത്"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"വലത്"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"സറൗണ്ടിംഗ്‌സ്"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"ഇടത് സറൗണ്ടിംഗ്‌സ്"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"വലത് സറൗണ്ടിംഗ്‌സ്"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"വേർതിരിച്ച ഇടത്, വലത് നിയന്ത്രണങ്ങളിലേക്ക് വികസിപ്പിക്കുക"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ഏകീകൃത നിയന്ത്രണത്തിലേക്ക് ചുരുക്കുക"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"സറൗണ്ടിംഗ്‌സ് മ്യൂട്ട് ചെയ്യുക"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"സറൗണ്ടിംഗ്‌സ് അൺമ്യൂട്ട് ചെയ്യുക"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ടൂളുകൾ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"തത്സമയ ക്യാപ്ഷൻ"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"ക്രമീകരണം"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"കുറിപ്പ്"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ഉപകരണ മൈക്രോഫോൺ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ഉപകരണ ക്യാമറ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
@@ -796,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ഓണാണ് - ഫേസ് ബേസ്‌ഡ്"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"പൂർത്തിയായി"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ബാധകമാക്കുക"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"അറിയിപ്പുകൾ ഓഫാക്കുക"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"നിശബ്‌ദം"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ഡിഫോൾട്ട്"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"സ്വയമേവ"</string>
@@ -907,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"വലതുവശത്തുള്ള ആപ്പിനൊപ്പം സ്‌ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുക"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ഇടതുവശത്തുള്ള ആപ്പിനൊപ്പം സ്‌ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുക"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"പൂർണ സ്ക്രീൻ ഉപയോഗിക്കുക"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ഡെസ്‌ക്ടോപ്പ് വ്യൂ ഉപയോഗിക്കുക"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ഡെസ്ക്ടോപ്പ് വിൻഡോയിംഗ് ഉപയോഗിക്കുക"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ വലതുവശത്തെ/താഴത്തെ ആപ്പിലേക്ക് മാറുക"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ ഇടതുവശത്തെ/മുകളിലെ ആപ്പിലേക്ക് മാറൂ"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"സ്‌ക്രീൻ വിഭജന മോഡിൽ: ഒരു ആപ്പിൽ നിന്ന് മറ്റൊന്നിലേക്ക് മാറുക"</string>
@@ -1003,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> എന്ന സ്ഥാനത്തേക്ക് ചേർക്കുക"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"സ്ഥാനം അസാധുവാണ്."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"ടൈൽ ഇതിനകം ചേർത്തിട്ടുണ്ട്"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ടൈൽ ചേർത്തു"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ടൈൽ നീക്കം ചെയ്‌തു"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string>
@@ -1203,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> ആപ്പിനുള്ള ഈ മീഡിയാ കൺട്രോൾ മറയ്ക്കണോ?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"നിലവിലെ മീഡിയ സെഷൻ മറയ്ക്കാനാകില്ല."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"മറയ്‌ക്കുക"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"പുനരാരംഭിക്കുക"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ക്രമീകരണം"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> എന്ന ആർട്ടിസ്റ്റിന്റെ <xliff:g id="SONG_NAME">%1$s</xliff:g> എന്ന ഗാനം <xliff:g id="APP_LABEL">%3$s</xliff:g> ആപ്പിൽ പ്ലേ ചെയ്യുന്നു"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>-ൽ <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1571,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"അജ്ഞാതം"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"എല്ലാ ടൈലുകളും റീസെറ്റ് ചെയ്യണോ?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"എല്ലാ ദ്രുത ക്രമീകരണ ടൈലുകളും ഉപകരണത്തിന്റെ ഒറിജിനൽ ക്രമീകരണത്തിലേക്ക് റീസെറ്റ് ചെയ്യും"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
index e197c92..a7098c9 100644
--- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"ഓഫാണ്"</item>
     <item msgid="4875147066469902392">"ഓണാണ്"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ലഭ്യമല്ല"</item>
     <item msgid="5044688398303285224">"ഓഫാണ്"</item>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index c6ab2c5..3adddc2 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>-тай холбогдсон."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g>-д холбогдсон."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Бүлгийг дэлгэнэ үү."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Бүлэгт төхөөрөмж нэмнэ."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Бүлгээс төхөөрөмж хасна."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Аппликейшныг нээнэ үү."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Холбогдоогүй."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Роуминг"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Оролт"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Сонсголын төхөөрөмж"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Асааж байна…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Гэрэлтүүлгийг давуу эрхтэй аппаас хянаж байгаа тул тохируулах боломжгүй"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматаар эргэх"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Дэлгэцийг автоматаар эргүүлэх"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Байршил"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Орчин тойрон"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Зүүн"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Баруун"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Орчин тойрон"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Зүүн талын орчин тойрны дуу"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Баруун талын орчин тойрны дуу"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Зүүн, баруун талын тусдаа тохиргоо руу дэлгэх"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Нэгдсэн тохиргоо руу хураах"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Орчин тойрны дууг хаах"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Орчин тойрны дууг нээх"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Хэрэгсэл"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Шууд тайлбар"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Тохиргоо"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Тэмдэглэл"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Төхөөрөмжийн микрофоныг блокоос гаргах уу?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Төхөөрөмжийн камерыг блокоос гаргах уу?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Асаалттай - Царайнд суурилсан"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Болсон"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Ашиглах"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Мэдэгдлийг унтраах"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Чимээгүй"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Өгөгдмөл"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автомат"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Аппыг баруун талд байгаагаар дэлгэцийг хуваахыг ашиглах"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Аппыг зүүн талд байгаагаар дэлгэцийг хуваахыг ашиглах"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Бүтэн дэлгэцийг ашиглах"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Дэлгэц дээр харагдах байдлыг ашиглах"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Дэлгэцийн цонх үүсгэх онцлогийг ашиглах"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Дэлгэц хуваахыг ашиглаж байхдаа баруун талд эсвэл доор байх апп руу сэлгэ"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Дэлгэц хуваахыг ашиглаж байхдаа зүүн талд эсвэл дээр байх апп руу сэлгэ"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Дэлгэц хуваах үеэр: аппыг нэгээс нөгөөгөөр солих"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> байрлалд нэмнэ үү"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Байрлал буруу байна."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> байрлал"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Хавтан аль хэдийн нэмсэн"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Хавтан нэмсэн"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Хавтанг хассан"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Шуурхай тохиргоо засварлагч."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Энэ медиа хяналтыг <xliff:g id="APP_NAME">%1$s</xliff:g>-д нуух уу?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Одоогийн медиа харилцан үйлдлийг нуух боломжгүй."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Нуух"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Үргэлжлүүлэх"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Тохиргоо"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> дээр тоглуулж буй <xliff:g id="ARTIST_NAME">%2$s</xliff:g>-н <xliff:g id="SONG_NAME">%1$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>-н <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Тодорхойгүй"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Бүх хавтанг шинэчлэх үү?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Шуурхай тохиргооны бүх хавтан төхөөрөмжийн эх тохиргоо руу шинэчлэгдэнэ"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index 7ca88e9..9b5a6e3 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Унтраалттай"</item>
     <item msgid="4875147066469902392">"Асаалттай"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Боломжгүй"</item>
     <item msgid="5044688398303285224">"Унтраалттай"</item>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 9fa4051..08ef7b2 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> शी कनेक्‍ट केले."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> शी कनेक्ट केले."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"गटाचा विस्तार करा."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"डिव्हाइस गटामध्ये जोडा."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"डिव्हाइस गटामधून काढून टाका."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"अ‍ॅप उघडा."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"कनेक्ट केले नाही."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"रोमिंग"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"श्रवणयंत्रे"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सुरू करत आहे…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"ब्राइटनेस टॉप ॲपद्वारे नियंत्रित केला जात असल्यामुळे ॲडजस्ट करू शकत नाही"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ऑटो-रोटेट स्क्रीन"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"जवळपासचे"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"डावे"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"उजवे"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"सभोवताली"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"डाव्या बाजूचा आवाज"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"उजव्या बाजूचा आवाज"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"डाव्या आणि उजव्या स्वतंत्र नियंत्रणांचा विस्तार करा"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"युनिफाइड नियंत्रणासाठी कोलॅप्स करा"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"जवळपासचे आवाज म्यूट करा"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"जवळपासचे आवाज अनम्यूट करा"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"टूल"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइव्ह कॅप्शन"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"सेटिंग्ज"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"टीप"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिव्हाइसचा मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिव्हाइसचा कॅमेरा अनब्लॉक करायचा आहे का?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"सुरू - चेहऱ्यावर आधारित"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"पूर्ण झाले"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"लागू करा"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"सूचना बंद करा"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"सायलंट"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"डीफॉल्ट"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ऑटोमॅटिक"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ॲप उजवीकडे ठेवून स्प्लिट स्क्रीन वापरा"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ॲप डावीकडे ठेवून स्प्लिट स्क्रीन वापरा"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"फुल स्क्रीन वापरा"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"डेस्कटॉप दृश्य पहा"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"डेस्कटॉप विंडोइंग वापरा"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन वापरताना उजवीकडील किंवा खालील अ‍ॅपवर स्विच करा"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन वापरताना डावीकडील किंवा वरील अ‍ॅपवर स्विच करा"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रीनदरम्यान: एक अ‍ॅप दुसऱ्या अ‍ॅपने बदला"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> स्थानावर जोडा"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"स्थान चुकीचे आहे."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थान <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"टाइल आधीपासून जोडली आहे"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"टाइल जोडली"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"टाइल काढून टाकली"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिंग्ज संपादक."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी हा मीडिया नियंत्रक लपवायचा आहे का?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"सध्याचे मीडिया सेशन लपवू शकत नाही."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"लपवा"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"पुन्हा सुरू करा"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग्ज"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> मध्ये <xliff:g id="ARTIST_NAME">%2$s</xliff:g> चे <xliff:g id="SONG_NAME">%1$s</xliff:g> प्ले होत आहे"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g> पैकी <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"अज्ञात"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"सर्व टाइल रीसेट करायच्या?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"सर्व क्विक सेटिंग्ज टाइल डिव्हाइसच्या मूळ सेटिंग्जवर रीसेट केल्या जातील"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index 58c4d06..a5930d9 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"बंद आहे"</item>
     <item msgid="4875147066469902392">"सुरू आहे"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"उपलब्ध नाही"</item>
     <item msgid="5044688398303285224">"बंद आहे"</item>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 91e7e63..5aec756 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -423,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Persekitaran"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kiri"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Kanan"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Persekitaran"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Persekitaran kiri"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Persekitaran kanan"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Kembangkan kepada kawalan berasingan sebelah kiri dan kanan"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Kuncupkan kepada kawalan yang disatukan"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Redamkan persekitaran"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Nyahredam persekitaran"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alatan"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sari Kata Langsung"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Tetapan"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Nyahsekat mikrofon peranti?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Nyahsekat kamera peranti?"</string>
@@ -796,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Hidup - Berasaskan wajah"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Selesai"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Guna"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Matikan pemberitahuan"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Senyap"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Lalai"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatik"</string>
@@ -907,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Gunakan skrin pisah dengan apl pada sebelah kanan"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Gunakan skrin pisah dengan apl pada sebelah kiri"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Gunakan skrin penuh"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Gunakan paparan desktop"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Gunakan tetingkap desktop"</string>
     <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>
@@ -1003,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan pada kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Kedudukan tidak sah."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Jubin sudah ditambahkan"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Jubin ditambah"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Jubin dialih keluar"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor tetapan pantas."</string>
@@ -1203,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Sembunyikan kawalan media ini untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Sesi media semasa tidak dapat disembunyikan."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Sembunyikan"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Sambung semula"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Tetapan"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> oleh <xliff:g id="ARTIST_NAME">%2$s</xliff:g> dimainkan daripada <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> daripada <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1571,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tidak diketahui"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Tetapkan semula semua jubin?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Semua jubin Tetapan Pantas akan ditetapkan semula kepada tetapan asal peranti"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
index 5a55b76..eca55a3 100644
--- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Mati"</item>
     <item msgid="4875147066469902392">"Hidup"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Tidak tersedia"</item>
     <item msgid="5044688398303285224">"Mati"</item>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 703562e..98f937b 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>သို့ ချိတ်ဆက်ထား"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> သို့ချိတ်ဆက်ထားပါသည်။"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"အုပ်စုကို ပိုပြသည်။"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"အဖွဲ့သို့ စက်ပစ္စည်းထည့်ရန်။"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"အဖွဲ့မှ စက်ပစ္စည်းကို ဖယ်ရှားရန်။"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"အပလီကေးရှင်းကို ဖွင့်သည်။"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"ချိတ်ဆက်မထားပါ"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"ပြင်ပကွန်ရက်သုံးခြင်း"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"အဝင်"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"နားကြားကိရိယာ"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ဖွင့်နေသည်…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"အပေါ်အက်ပ်မှ ထိန်းချုပ်ထားသောကြောင့် တောက်ပမှုကို ချိန်ညှိ၍ မရပါ"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"အော်တို-လည်"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"မျက်နှာပြင်အား အလိုအလျောက်လှည့်ခြင်း"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"တည်နေရာ"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ဝန်းကျင်အသံ"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ဘယ်"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ညာ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"ဝန်းကျင်အသံ"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"ဘယ်ဘက် ဝန်းကျင်အသံ"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"ညာဘက် ဝန်းကျင်အသံ"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ဘယ်ညာခွဲထားသော ထိန်းချုပ်မှုများအဖြစ် ပိုပြပါ"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ပေါင်းစည်းထားသော ထိန်းချုပ်မှုအဖြစ် လျှော့ပြပါ"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ဝန်းကျင်အသံ ပိတ်ရန်"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ဝန်းကျင်အသံ ပြန်ဖွင့်ရန်"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"တူးလ်များ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"တိုက်ရိုက်စာတန်း"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"ဆက်တင်များ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"မှတ်စု"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"စက်၏မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"စက်၏ကင်မရာကို ပြန်ဖွင့်မလား။"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ဖွင့် - မျက်နှာအခြေခံ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ပြီးပြီ"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"အသုံးပြုရန်"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"အကြောင်းကြားချက်များ ပိတ်ရန်"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"အသံပိတ်ရန်"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"မူလ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"အလိုအလျောက်"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"အက်ပ်ကို ညာ၌ထားကာ မျက်နှာပြင် ခွဲ၍ပြသခြင်း သုံးရန်"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"အက်ပ်ကို ဘယ်၌ထားကာ မျက်နှာပြင် ခွဲ၍ပြသခြင်း သုံးရန်"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"ဖန်သားပြင်အပြည့် သုံးရန်"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ဒက်စ်တော့မြင်ကွင်း သုံးရန်"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ဒက်စ်တော့ဝင်းဒိုးမုဒ် သုံးရန်"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"မျက်နှာပြင်ခွဲ၍ပြသခြင်း သုံးစဉ် ညာ (သို့) အောက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးစဉ် ဘယ် (သို့) အထက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"မျက်နှာပြင် ခွဲ၍ပြသစဉ်- အက်ပ်တစ်ခုကို နောက်တစ်ခုနှင့် အစားထိုးရန်"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထားသို့ ပေါင်းထည့်ရန်"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"နေရာ မမှန်ပါ။"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထား"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"အကွက်ငယ် ထည့်ပြီးဖြစ်သည်"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"အကွက်ငယ်ကို ထည့်ပြီးပါပြီ"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"အကွက်ငယ်ကို ဖယ်ရှားပြီးပါပြီ"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"မြန်ဆန်သည့် ဆက်တင်တည်းဖြတ်စနစ်"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် ဤမီဒီယာထိန်းချုပ်မှု ဖျောက်ထားမလား။"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"လက်ရှိ မီဒီယာစက်ရှင်ကို ဝှက်၍မရပါ။"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ဖျောက်ထားမည်"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"ဆက်လုပ်ရန်"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ဆက်တင်များ"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ၏ <xliff:g id="SONG_NAME">%1$s</xliff:g> ကို <xliff:g id="APP_LABEL">%3$s</xliff:g> တွင် ဖွင့်ထားသည်"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g> အနက် <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"အမျိုးအမည်မသိ"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"အကွက်ငယ်အားလုံးကို ပြင်ဆင်သတ်မှတ်မလား။"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"အမြန်ဆက်တင်များ အကွက်ငယ်အားလုံးကို စက်ပစ္စည်း၏ မူရင်းဆက်တင်များသို့ ပြင်ဆင်သတ်မှတ်ပါမည်"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>၊ <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml
index 7789205..2bd2390 100644
--- a/packages/SystemUI/res/values-my/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"ပိတ်"</item>
     <item msgid="4875147066469902392">"ဖွင့်"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"မရနိုင်ပါ"</item>
     <item msgid="5044688398303285224">"ပိတ်"</item>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index b75f644..627ab44 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Koblet til <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Koblet til <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Utvid gruppen."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Legg til enheten i gruppen."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Fjern enheten fra gruppen."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Åpne appen."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Ikke tilkoblet."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Innenhet"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Høreapparater"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Slår på …"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Kan ikke justere lysstyrken, fordi den kontrolleres av den øvre appen"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotér automatisk"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotér skjermen automatisk"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Sted"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivelser"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Venstre"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Høyre"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Omgivelser"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Omgivelser til venstre"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Omgivelser til høyre"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Utvid til separate kontroller for venstre og høyre"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Skjul til samlet kontroll"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Kutt lyden for omgivelsene"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Slå på lyden for omgivelsene"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Verktøy"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Direkteteksting"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Innstillinger"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Merknad"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du oppheve blokkeringen av enhetsmikrofonen?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du oppheve blokkeringen av enhetskameraet?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"På – ansiktsbasert"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Ferdig"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Bruk"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Slå av varsler"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Lydløs"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Bruk delt skjerm med appen til høyre"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Bruk delt skjerm med appen til venstre"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Bruk fullskjerm"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Bruk datamaskinvisning"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Bruk datamaskinvinduer"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Legg til posisjonen <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posisjonen er ugyldig."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisjon <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Brikken er lagt til allerede"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"En infobrikke er lagt til"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"En infobrikke er fjernet"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsvindu for hurtiginnstillinger."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Vil du skjule denne mediekontrollen for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Den nåværende medieøkten kan ikke skjules."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skjul"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Gjenoppta"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Innstillinger"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> av <xliff:g id="ARTIST_NAME">%2$s</xliff:g> spilles av fra <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> av <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ukjent"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vil du tilbakestille alle brikkene?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle brikker for hurtiginnstillinger tilbakestilles til enhetens opprinnelige innstillinger"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index 25b2205..fca868e 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Av"</item>
     <item msgid="4875147066469902392">"På"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Utilgjengelig"</item>
     <item msgid="5044688398303285224">"Av"</item>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index cd20def..6090ca5 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> मा जडित।"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> मा कनेक्ट गरियो।"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"समूह एक्स्पान्ड गर्नुहोस्।"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"समूहमा डिभाइस हाल्नुहोस्।"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"समूहबाट डिभाइस हटाउनुहोस्।"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"एप खोल्नुहोस्।"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"जडान नगरिएको।"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"रोमिङ"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"हियरिङ डिभाइसहरू"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सक्रिय गर्दै…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"सिरानको एपले चमक नियन्त्रण गरिरहेकाले चमक मिलाउन मिल्दैन"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"अटो रोटेट"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रिन स्वतःघुम्ने"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"लोकेसन"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"वरपरका आवाज"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"बायाँ"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"दायाँ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"सराउन्डिङ साउन्ड"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"बायाँतिरको सराउन्डिङ साउन्ड"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"दायाँतिरको सराउन्डिङ साउन्ड"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"दायाँ र बायाँतर्फको भोल्युम छुट्टाछुट्टै व्यवस्थापन गर्न भोल्युम प्यानल छुट्ट्याउनुहोस्"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"कोल्याप्स गरी एउटै कन्ट्रोल बनाउनुहोस्"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"वरपरका आवाज म्युट गर्नुहोस्"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"वरपरका आवाज अनम्युट गर्नुहोस्"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"टुलहरू"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइभ क्याप्सन"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"सेटिङ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"नोट"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिभाइसको माइक्रोफोन अनब्लक गर्ने हो?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिभाइसको क्यामेरा अनब्लक गर्ने हो?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"अन छ - अनुहारमा आधारित"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"सम्पन्न भयो"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"लागू गर्नुहोस्"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"सूचनाहरू अफ गर्नुहोस्"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"साइलेन्ट"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"डिफल्ट"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"स्वचालित"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"हालको एप दायाँ भागमा पारेर स्प्लिट स्क्रिन प्रयोग गर्नुहोस्"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"हालको एप बायाँ भागमा पारेर स्प्लिट स्क्रिन प्रयोग गर्नुहोस्"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"फुल स्क्रिन प्रयोग गर्नुहोस्"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"डेस्कटप भ्यू प्रयोग गर्नुहोस्"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"डेस्कटप विन्डोइङ प्रयोग गर्नुहोस्"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा दायाँ वा तलको एप चलाउनुहोस्"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा बायाँ वा माथिको एप चलाउनुहोस्"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रिन प्रयोग गरिएका बेला: एउटा स्क्रिनमा भएको एप अर्कोमा लैजानुहोस्"</string>
@@ -997,8 +993,7 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकताका सूचना आइकनहरू देखाउनुहोस्"</string>
     <string name="other" msgid="429768510980739978">"अन्य"</string>
-    <!-- no translation found for accessibility_qs_edit_toggle_tile_size_action (1485194410119733586) -->
-    <skip />
+    <string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"टाइलको आकार टगल गर्नुहोस्"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाउनुहोस्"</string>
     <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"अन्तिम स्थानमा टाइल हाल्नुहोस्"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल सार्नुहोस्"</string>
@@ -1007,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल यो अवस्था <xliff:g id="POSITION">%1$d</xliff:g> मा हाल्नुहोस्"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"पोजिसन अवैध छ।"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"टाइल हालिसकिएको छ"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"टाइल हालियो"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"टाइल हटाइयो"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिङ सम्पादक।"</string>
@@ -1207,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> का हकमा यो मिडिया कन्ट्रोल लुकाउने हो?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"हालको मिडिया सत्र लुकाउन मिल्दैन।"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"लुकाउनुहोस्"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"सुचारु गर्नुहोस्"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिङ"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> को <xliff:g id="SONG_NAME">%1$s</xliff:g> बोलको गीत <xliff:g id="APP_LABEL">%3$s</xliff:g> मा बज्दै छ"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g> मध्ये <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1551,10 +1546,8 @@
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"आफूले हालसालै चलाएका एपहरू हेर्न तीन वटा औँलाले टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"आफ्ना सबै एपहरू हेर्न आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
     <string name="redacted_notification_single_line_title" msgid="212019960919261670">"जानकारी लुकाउन सम्पादन गरिएको"</string>
-    <!-- no translation found for public_notification_single_line_text (3576190291791654933) -->
-    <skip />
-    <!-- no translation found for redacted_otp_notification_single_line_text (5179964116354454118) -->
-    <skip />
+    <string name="public_notification_single_line_text" msgid="3576190291791654933">"हेर्न अनलक गर्नुहोस्"</string>
+    <string name="redacted_otp_notification_single_line_text" msgid="5179964116354454118">"कोड हेर्न अनलक गर्नुहोस्"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"सान्दर्भिक शिक्षा"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"पछाडि जान आफ्नो टचप्याड प्रयोग गर्नुहोस्"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"तीन वटा औँला प्रयोग गरी बायाँ वा दायाँतिर स्वाइप गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
@@ -1577,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"अज्ञात"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"सबै टाइलहरू रिसेट गर्ने हो?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"द्रुत सेटिङका सबै टाइलहरू रिसेट गरी डिभाइसका मूल सेटिङ लागू गरिने छन्"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index fe812f8..71f415a 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"अफ छ"</item>
     <item msgid="4875147066469902392">"अन छ"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"उपलब्ध छैन"</item>
     <item msgid="5044688398303285224">"अफ छ"</item>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 1df27df..ebe9ad4 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Verbonden met <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Verbonden met <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Groep uitvouwen."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Voeg het apparaat aan de groep toe."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Verwijder het apparaat uit de groep."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"App openen."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Niet verbonden."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hoortoestellen"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aanzetten…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Kan de helderheid niet aanpassen omdat deze wordt beheerd door de bovenste app"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatisch draaien"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Scherm automatisch draaien"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Locatie"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgevingsgeluid"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Rechts"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Omgevingsgeluid"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Omgevingsgeluid links"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Omgevingsgeluid rechts"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Uitvouwen naar gescheiden bediening voor links en rechts"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Samenvouwen tot geïntegreerde bediening"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Omgevingsgeluid uitzetten"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Omgevingsgeluid aanzetten"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live ondertiteling"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Instellingen"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Notitie"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Microfoon van apparaat niet meer blokkeren?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Apparaatcamera niet meer blokkeren?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aan: op basis van gezicht"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Toepassen"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Meldingen uitzetten"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Stil"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Standaard"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Gesplitst scherm gebruiken met de app aan de rechterkant"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Gesplitst scherm gebruiken met de app aan de linkerkant"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Volledig scherm gebruiken"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Desktopweergave gebruiken"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Desktopvensterfuncties gebruiken"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Toevoegen aan positie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Positie ongeldig."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Positie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Tegel is al toegevoegd"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tegel toegevoegd"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tegel verwijderd"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor voor \'Snelle instellingen\'."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Deze mediabediening verbergen voor <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"De huidige mediasessie kan niet worden verborgen."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Verbergen"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Hervatten"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellingen"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> van <xliff:g id="ARTIST_NAME">%2$s</xliff:g> wordt afgespeeld via <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> van <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index bb8fbe0..01de4cb 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Uit"</item>
     <item msgid="4875147066469902392">"Aan"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Niet beschikbaar"</item>
     <item msgid="5044688398303285224">"Uit"</item>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index ddb9157..8569c7a 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -423,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ପରିପାର୍ଶ୍ୱ"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ବାମ"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ଡାହାଣ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"ପରିପାର୍ଶ୍ୱ"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"ବାମ ପରିପାର୍ଶ୍ୱ"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"ଡାହାଣ ପରିପାର୍ଶ୍ୱ"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ବାମ ଏବଂ ଡାହାଣ ଅଲଗା ନିୟନ୍ତ୍ରଣକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ଏକତ୍ରିତ ନିୟନ୍ତ୍ରଣକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ପରିପାର୍ଶ୍ୱକୁ ମ୍ୟୁଟ କରନ୍ତୁ"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ପରିପାର୍ଶ୍ୱକୁ ଅନମ୍ୟୁଟ କରନ୍ତୁ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ଟୁଲ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ଲାଇଭ କେପ୍ସନ"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"ସେଟିଂସ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ନୋଟ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ କରିବେ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ଡିଭାଇସର କେମେରାକୁ ଅନବ୍ଲକ କରିବେ?"</string>
@@ -796,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ଚାଲୁ ଅଛି - ଫେସ-ଆଧାରିତ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ହୋଇଗଲା"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ଲାଗୁ କରନ୍ତୁ"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"ବିଜ୍ଞପ୍ତି ବନ୍ଦ କରନ୍ତୁ"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"ନୀରବ"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ଡିଫଲ୍ଟ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ସ୍ୱଚାଳିତ"</string>
@@ -907,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ଡାହାଣରେ ଆପ ସହିତ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ବାମରେ ଆପ ସହିତ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ଡେସ୍କଟପ ଭ୍ୟୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ଡେସ୍କଟପ ୱିଣ୍ଡୋଇଂ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ଡାହାଣପଟର ବା ତଳର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ବାମପଟର ବା ଉପରର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ସମୟରେ: କୌଣସି ଆପକୁ ଗୋଟିଏରୁ ଅନ୍ୟ ଏକ ଆପରେ ବଦଳାନ୍ତୁ"</string>
@@ -1003,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ଅବସ୍ଥିତିରେ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ଅବସ୍ଥିତି ଅବୈଧ ଅଟେ।"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ଅବସ୍ଥିତି <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"ପୂର୍ବରୁ ଟାଇଲକୁ ଯୋଗ କରାଯାଇଛି"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ଟାଇଲ୍ ଯୋଗ କରାଯାଇଛି"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ଟାଇଲ୍ କାଢ଼ି ଦିଆଯାଇଛି"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଏଡିଟର୍।"</string>
@@ -1203,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ଏହି ମିଡିଆ ନିୟନ୍ତ୍ରଣକୁ ଲୁଚାଇବେ?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"ବର୍ତ୍ତମାନର ମିଡିଆ ସେସନକୁ ଲୁଚାଯାଇପାରିବ ନାହିଁ।"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ଲୁଚାନ୍ତୁ"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ସେଟିଂସ୍"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ରୁ <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ଙ୍କ <xliff:g id="SONG_NAME">%1$s</xliff:g> ଚାଲୁଛି"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>ରୁ <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1571,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ଅଜଣା"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ସମସ୍ତ ଟାଇଲକୁ ରିସେଟ କରିବେ?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ସମସ୍ତ କୁଇକ ସେଟିଂସ ଟାଇଲ ଡିଭାଇସର ମୂଳ ସେଟିଂସରେ ରିସେଟ ହୋଇଯିବ"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index eae984b..37d3c95 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"ବନ୍ଦ ଅଛି"</item>
     <item msgid="4875147066469902392">"ଚାଲୁ ଅଛି"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ଉପଲବ୍ଧ ନାହିଁ"</item>
     <item msgid="5044688398303285224">"ବନ୍ଦ ଅଛି"</item>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 39c02d2..c7735a21 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -423,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ਆਲੇ-ਦੁਆਲੇ"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ਖੱਬੇ"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ਸੱਜੇ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"ਸਰਾਊਂਡ ਸਾਊਂਡ"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"ਖੱਬੇ ਪਾਸੇ ਦਾ ਸਰਾਊਂਡ ਸਾਊਂਡ"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"ਸੱਜੇ ਪਾਸੇ ਦਾ ਸਰਾਊਂਡ ਸਾਊਂਡ"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ਖੱਬੇ ਅਤੇ ਸੱਜੇ ਪਾਸੇ ਦੇ ਸ਼ੋਰ ਨੂੰ ਵੱਖ-ਵੱਖ ਕੰਟਰੋਲ ਕਰਨ ਲਈ ਵਿਸਤਾਰ ਕਰੋ"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ਏਕੀਕ੍ਰਿਤ ਕੰਟਰੋਲ \'ਤੇ ਜਾਣ ਲਈ ਸਮੇਟੋ"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ਆਲੇ-ਦੁਆਲੇ ਦੇ ਸ਼ੋਰ ਨੂੰ ਮਿਊਟ ਕਰੋ"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ਆਲੇ-ਦੁਆਲੇ ਦੇ ਸ਼ੋਰ ਨੂੰ ਅਣਮਿਊਟ ਕਰੋ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ਟੂਲ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ਲਾਈਵ ਸੁਰਖੀਆਂ"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ਨੋਟ-ਕਥਨ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
@@ -796,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ਚਾਲੂ ਹੈ - ਚਿਹਰਾ-ਆਧਾਰਿਤ"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ਹੋ ਗਿਆ"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ਲਾਗੂ ਕਰੋ"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"ਸੂਚਨਾਵਾਂ ਬੰਦ ਕਰੋ"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"ਸ਼ਾਂਤ"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ਸਵੈਚਲਿਤ"</string>
@@ -907,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ਸੱਜੇ ਪਾਸੇ ਵਾਲੀ ਐਪ ਨਾਲ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ਖੱਬੇ ਪਾਸੇ ਵਾਲੀ ਐਪ ਨਾਲ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ਡੈਸਕਟਾਪ ਦ੍ਰਿਸ਼ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ਡੈਸਕਟਾਪ \'ਤੇ ਇੱਕ ਤੋਂ ਵੱਧ ਵਿੰਡੋਆਂ ਦਿਖਾਉਣ ਵਾਲੀ ਸੁਵਿਧਾ ਵਰਤੋ"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਸੱਜੇ ਜਾਂ ਹੇਠਾਂ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਖੱਬੇ ਜਾਂ ਉੱਪਰ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੌਰਾਨ: ਇੱਕ ਐਪ ਨਾਲ ਦੂਜੀ ਐਪ ਨੂੰ ਬਦਲੋ"</string>
@@ -1003,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ਸਥਾਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ਮੌਜੂਦਾ ਥਾਂ ਅਵੈਧ ਹੈ।"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ਸਥਾਨ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"ਟਾਇਲ ਨੂੰ ਪਹਿਲਾਂ ਹੀ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ਟਾਇਲ ਨੂੰ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ਟਾਇਲ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਗਿਆ"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਸੰਪਾਦਕ।"</string>
@@ -1203,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਇਹ ਮੀਡੀਆ ਕੰਟਰੋਲ ਲੁਕਾਉਣਾ ਹੈ?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"ਮੌਜੂਦਾ ਮੀਡੀਆ ਸੈਸ਼ਨ ਲੁਕਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ਲੁਕਾਓ"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> ਤੋਂ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ਦਾ <xliff:g id="SONG_NAME">%1$s</xliff:g> ਚੱਲ ਰਿਹਾ ਹੈ"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g> ਵਿੱਚੋਂ <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1571,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ਅਗਿਆਤ"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ਕੀ ਸਾਰੀਆਂ ਟਾਇਲਾਂ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ਸਾਰੀਆਂ ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਟਾਇਲਾਂ ਡੀਵਾਈਸ ਦੀਆਂ ਮੂਲ ਸੈਟਿੰਗਾਂ \'ਤੇ ਰੀਸੈੱਟ ਹੋ ਜਾਣਗੀਆਂ"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index 2baff30..43b8734 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"ਬੰਦ ਹੈ"</item>
     <item msgid="4875147066469902392">"ਚਾਲੂ ਹੈ"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ਅਣਉਪਲਬਧ ਹੈ"</item>
     <item msgid="5044688398303285224">"ਬੰਦ ਹੈ"</item>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index b93bf30..7c49073 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Połączono z <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Połączono z urządzeniem <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Rozwiń grupę."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Dodaj urządzenie do grupy."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Usuń urządzenie z grupy."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Otwórz aplikację."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Nie połączono."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Wejście"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparaty słuchowe"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Włączam…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Nie można dostosować jasności, ponieważ reguluje ją aplikacja na pierwszym planie"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoobracanie"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoobracanie ekranu"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokalizacja"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Otoczenie"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Po lewej"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Po prawej"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Otoczenie"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Otoczenie z lewej strony"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Otoczenie z prawej strony"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Rozwiń, aby oddzielić elementy sterujące po lewej i po prawej stronie"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Zwiń do ujednoliconego sterowania"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Wycisz otoczenie"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Wyłącz wyciszenie otoczenia"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Narzędzia"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Napisy na żywo"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Ustawienia"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Notatka"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokować mikrofon urządzenia?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokować aparat urządzenia?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Włączono – na podstawie twarzy"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gotowe"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Zastosuj"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Wyłącz powiadomienia"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Ciche"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Domyślne"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatycznie"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Podziel ekran z aplikacją widoczną po prawej"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Podziel ekran z aplikacją widoczną po lewej"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Użyj trybu pełnoekranowego"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Użyj wersji na komputery"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Używaj trybu okien na pulpicie"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodaj w pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Nieprawidłowa pozycja."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozycja <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Kafelek został już dodany"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Dodano kartę"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Usunięto kartę"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Edytor szybkich ustawień."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Ukryć sterowanie multimediami w aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Nie można ukryć tej sesji multimediów."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ukryj"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Wznów"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ustawienia"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Aplikacja <xliff:g id="APP_LABEL">%3$s</xliff:g> odtwarza utwór <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>)"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> z <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nieznane"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Zresetować wszystkie kafelki?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Wszystkie kafelki Szybkich ustawień zostaną zresetowane do oryginalnych ustawień urządzenia"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index bd3a828..abc1867 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Wyłączone"</item>
     <item msgid="4875147066469902392">"Włączone"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Niedostępny"</item>
     <item msgid="5044688398303285224">"Wyłączona"</item>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 7c9028b..40b85cc 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Expandir grupo."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Adicionar dispositivo ao grupo."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remover dispositivo do grupo."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Abrir aplicativo."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Sem conexão."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ativando…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Não é possível ajustar o brilho, porque ele está sendo controlado pelo app principal"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Som ambiente"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lado esquerdo"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Lado direito"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Som ambiente"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Ambiente à esquerda"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Ambiente à direita"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Abrir para controles separados da esquerda e da direita"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Fechar para controle unificado"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silenciar som ambiente"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ativar som ambiente"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Ferramentas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legenda instantânea"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Configurações"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Observação"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada (reconhecimento facial)"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desativar notificações"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silenciosas"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Padrão"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Usar a tela dividida com o app à direita"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Usar a tela dividida com o app à esquerda"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Usar tela cheia"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Usar a versão para computadores"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Usar modo de janela para computador"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posição inválida."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"O bloco já foi adicionado"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Bloco adicionado"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Bloco removido"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Ocultar este controle de mídia para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Não é possível ocultar a sessão de mídia atual."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Tocando <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> no app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> de <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecidos"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Redefinir todos os blocos?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os blocos \"Configurações rápidas\" serão redefinidos para as configurações originais do dispositivo"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index 0233a36..30e45fe 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Desativado"</item>
     <item msgid="4875147066469902392">"Ativado"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponível"</item>
     <item msgid="5044688398303285224">"Apagada"</item>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index a8e0f05..934c717 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ligado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Ligado a <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Expanda o grupo."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Adicionar dispositivo ao grupo."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remover dispositivo do grupo."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Abra a aplicação."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Sem ligação."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"A ativar..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Não é possível ajustar o brilho porque está a ser controlado pela app principal"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotação auto."</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rodar o ecrã automaticamente"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambiente"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerda"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Direita"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Ambiente"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Ambiente à esquerda"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Ambiente à direita"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expandir para controlos separados do lado direito e esquerdo"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Reduzir para controlo unificado"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Desativar som do ambiente"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Reativar som do ambiente"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Ferramentas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legendas instantâneas"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Definições"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmara do dispositivo?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada – Com base no rosto"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desativar notificações"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silencioso"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Predefinição"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Use o ecrã dividido com a app à direita"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Use o ecrã dividido com a app à esquerda"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Use o ecrã inteiro"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Use a vista de computador"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Use janelas de computador"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicione à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posição inválida."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Mosaico já adicionado"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Cartão adicionado"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Cartão removido"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de definições rápidas."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Ocultar controlo de multimédia para a app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Não pode ocultar a sessão de multimédia atual."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Definições"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> em reprodução a partir da app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> de <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index 3beca27..42a465e 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Desligado"</item>
     <item msgid="4875147066469902392">"Ligado"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponível"</item>
     <item msgid="5044688398303285224">"Desligada"</item>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 7c9028b..40b85cc 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectado a <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Conectado a <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Expandir grupo."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Adicionar dispositivo ao grupo."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Remover dispositivo do grupo."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Abrir aplicativo."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Sem conexão."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ativando…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Não é possível ajustar o brilho, porque ele está sendo controlado pelo app principal"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Som ambiente"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lado esquerdo"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Lado direito"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Som ambiente"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Ambiente à esquerda"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Ambiente à direita"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Abrir para controles separados da esquerda e da direita"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Fechar para controle unificado"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silenciar som ambiente"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ativar som ambiente"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Ferramentas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legenda instantânea"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Configurações"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Observação"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada (reconhecimento facial)"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desativar notificações"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silenciosas"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Padrão"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Usar a tela dividida com o app à direita"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Usar a tela dividida com o app à esquerda"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Usar tela cheia"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Usar a versão para computadores"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Usar modo de janela para computador"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posição inválida."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"O bloco já foi adicionado"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Bloco adicionado"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Bloco removido"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Ocultar este controle de mídia para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Não é possível ocultar a sessão de mídia atual."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ocultar"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Tocando <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> no app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> de <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecidos"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Redefinir todos os blocos?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os blocos \"Configurações rápidas\" serão redefinidos para as configurações originais do dispositivo"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index 0233a36..30e45fe 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Desativado"</item>
     <item msgid="4875147066469902392">"Ativado"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponível"</item>
     <item msgid="5044688398303285224">"Apagada"</item>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 23270ea..3f8324e 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Conectat la <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"S-a stabilit conexiunea la <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Extinde grupul."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Adaugă dispozitivul în grup."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Elimină dispozitivul din grup."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Deschide aplicația."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Neconectat."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Intrare"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparate auditive"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Se activează..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Nu se poate ajusta luminozitatea deoarece este controlată de aplicația de sus"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotire automată"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotirea automată a ecranului"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Locație"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Împrejurimi"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Stânga"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Dreapta"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Împrejurimi"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Împrejurimi din stânga"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Împrejurimi din dreapta"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Extinde comenzile separate la stânga și la dreapta"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Restrânge la comanda unificată"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Dezactivează sunetul ambiental"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Activează sunetul ambiental"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Instrumente"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitrări live"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Setări"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Notă"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblochezi microfonul dispozitivului?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblochezi camera dispozitivului?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activată – În funcție de chip"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Gata"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Aplică"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Dezactivează notificările"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Silențios"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Prestabilite"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automat"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Folosește ecranul împărțit cu aplicația în dreapta"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Folosește ecranul împărțit cu aplicația în stânga"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Folosește ecranul complet"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Folosește afișarea pe desktop"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Folosește funcția de windowing pe desktop"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adaugă pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Poziție nevalidă."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Cardul a fost adăugat deja"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Cardul a fost adăugat"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Cardul a fost eliminat"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editorul pentru setări rapide."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Ascunzi comanda media pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Sesiunea media actuală nu se poate ascunde."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ascunde"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Reia"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> de la <xliff:g id="ARTIST_NAME">%2$s</xliff:g> se redă în <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> din <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Necunoscută"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Resetezi toate cardurile?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Toate cardurile Setări rapide se vor reseta la setările inițiale ale dispozitivului"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index b555987..5646077 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Dezactivată"</item>
     <item msgid="4875147066469902392">"Activată"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponibilă"</item>
     <item msgid="5044688398303285224">"Dezactivată"</item>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index e4c1d45..450d048 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>: подключено."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Подключено к: <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Развернуть группу."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Добавить устройство в группу."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Удалить устройство из группы."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Открыть приложение."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Не подключено"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Роуминг"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Устройство ввода"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слуховые аппараты"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Включение…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Невозможно изменить яркость, поскольку ею управляет приложение сверху"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоповорот"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоповорот экрана"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Геолокация"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Окружающие звуки"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Левый"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Правый"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Окружающие звуки"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Звуки слева"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Звуки справа"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Разделить на левый и правый элемент управления"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Объединить в один элемент управления"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Заглушить окружающие звуки"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Не заглушать окружающие звуки"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Инструменты"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Автоматические субтитры"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Настройки"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Заметка"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблокировать микрофон устройства?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблокировать камеру устройства?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Включить (на основе распознавания лиц)"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Применить"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Выключить уведомления"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Без звука"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"По умолчанию"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматически"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Разделить экран и поместить открытое приложение справа"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Разделить экран и поместить открытое приложение слева"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Полноэкранный режим"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Версия для ПК"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Использовать компьютерные окна"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Перейти к приложению справа или внизу на разделенном экране"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Перейти к приложению слева или вверху на разделенном экране"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"В режиме разделения экрана заменить одно приложение другим"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Недопустимое расположение."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Параметр уже добавлен"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Панель добавлена"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Панель удалена"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор быстрых настроек."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Скрыть этот элемент в приложении \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Этот мультимедийный сеанс невозможно скрыть."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Скрыть"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Возобновить"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Воспроизводится медиафайл \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" (исполнитель: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) из приложения \"<xliff:g id="APP_LABEL">%3$s</xliff:g>\"."</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> из <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Неизвестно"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Сбросить все параметры?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Для всех параметров быстрых настроек будут восстановлены значения по умолчанию."</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index 8279183..9ffa7f1 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Откл."</item>
     <item msgid="4875147066469902392">"Вкл."</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Функция недоступна"</item>
     <item msgid="5044688398303285224">"Откл."</item>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 437a35b..c6326f2 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> වෙත සම්බන්ධ කරන ලදි."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> වෙත සම්බන්ධ විය."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"සමූහය දිගහැරීම"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"සමූහයට උපාංගය එක් කරන්න."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"උපාංගය සමූහයෙන් ඉවත් කරන්න."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"යෙදුම විවෘත කරන්න."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"සම්බන්ධ වී නැත."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"රෝමිං"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ආදානය"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ශ්‍රවණාධාරක"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ක්‍රියාත්මක කරමින්…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"ඉහළ යෙදුම මඟින් එය පාලනය වන නිසා දීප්තිය ගැළපුම් කළ නොහැක"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ස්වයංක්‍රීය කරකැවීම"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ස්වයංක්‍රීයව-භ්‍රමණය වන තිරය"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"ස්ථානය"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"වටපිටාව"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"වම"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"දකුණ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"වටපිටාව"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"වම් වටපිටාව"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"දකුණු වටපිටාව"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"වමට සහ දකුණට වෙන් වූ පාලන වෙත පුළුල් කරන්න"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ඒකාබද්ධ පාලනයට හකුළන්න"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"අවට පරිසරය නිහඬ කරන්න"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"අවට නිහඬ නොකරන්න"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"මෙවලම්"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"සජීවී සිරස්තල"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"සැකසීම්"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"සටහන"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"උපාංග මයික්‍රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"උපාංග කැමරාව අවහිර කිරීම ඉවත් කරන්නද?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ක්‍රියාත්මකයි - මුහුණ-පදනම්ව"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"නිමයි"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"යොදන්න"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"දැනුම්දීම් අක්‍රිය කරන්න"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"නිහඬ"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"පෙරනිමි"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ස්වයංක්‍රිය"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"දකුණේ යෙදුම සමග බෙදීම් තිරය භාවිතා කරන්න"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"වම් පැත්තේ යෙදුම සමග බෙදීම් තිරය භාවිතා කරන්න"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"පූර්ණ තිරය භාවිතා කරන්න"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ඩෙස්ක්ටොප් දසුන භාවිතා කරන්න"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ඩෙස්ක්ටොප් කවුළුකරණය භාවිතා කරන්න"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"බෙදුම් තිරය භාවිත කරන අතරතුර දකුණේ හෝ පහළින් ඇති යෙදුමට මාරු වන්න"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"බෙදුම් තිරය භාවිත කරන අතරතුර වමේ හෝ ඉහළ ඇති යෙදුමට මාරු වන්න"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"බෙදුම් තිරය අතරතුර: යෙදුමක් එකකින් තවත් එකක් ප්‍රතිස්ථාපනය කරන්න"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ස්ථානයට එක් කරන්න"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ස්ථානය අවලංගුයි."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"ටයිලය දැනටමත් එක් කර ඇත"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ටයිල් එක එක් කරන ලදි"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ටයිල් ඉවත් කරන ලදි"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ඉක්මන් සැකසුම් සංස්කාරකය."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා මෙම මාධ්‍ය පාලනය වසන්නද?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"වත්මන් මාධ්‍ය සැසිය සැඟවිය නොහැකිය."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"සඟවන්න"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"නැවත පටන් ගන්න"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"සැකසීම්"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>ගේ <xliff:g id="SONG_NAME">%1$s</xliff:g> ගීතය <xliff:g id="APP_LABEL">%3$s</xliff:g> වෙතින් ධාවනය වෙමින් පවතී"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>කින් <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"නොදනී"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"සියලු ටයිල් නැවත සකසන්න ද?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"සියලු ඉක්මන් සැකසීම් ටයිල් උපාංගයේ මුල් සැකසීම් වෙත නැවත සකසනු ඇත"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index 9397ca7..7eeaefd 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"අක්‍රියයි"</item>
     <item msgid="4875147066469902392">"සක්‍රියයි"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"නොමැත"</item>
     <item msgid="5044688398303285224">"අක්‍රියයි"</item>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 859c976..fe1dbbd 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Pripojené k zariadeniu <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Pripojené k zariadeniu <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Rozbaliť skupinu"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Pridať zariadenie do skupiny"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Odstrániť zariadenie zo skupiny"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Otvoriť aplikáciu"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Nepripojené."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Načúvadlá"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapína sa…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Jas sa nedá upraviť, pretože ho ovláda horná aplikácia"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčanie"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčanie obrazovky"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolie"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vľavo"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Vpravo"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Okolie"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Okolie zľava"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Okolie sprava"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Rozbaliť na samostatné ovládanie ľavej a pravej strany"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Zbaliť na jednotné ovládanie"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Vypnúť zvuk okolia"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Zapnúť zvuk okolia"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Nástroje"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Živý prepis"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Nastavenia"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Poznámka"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Chcete odblokovať mikrofón zariadenia?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Chcete odblokovať kameru zariadenia?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Zapnuté – podľa tváre"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Hotovo"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Použiť"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Vypnúť upozornenia"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Tiché"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Predvolené"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Rozdelenie obrazovky, aktuálna aplikácia vpravo"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Rozdelenie obrazovky, aktuálna aplikácia vľavo"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Používať celú obrazovku"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Používať zobrazenie v počítači"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Používať windowing na pracovnej ploche"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridať na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozícia je neplatná."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozícia"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Karta už bola pridaná"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Karta bola pridaná"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Karta bola odstránená"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rýchlych nastavení"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Chcete tento ovládač médií pre <xliff:g id="APP_NAME">%1$s</xliff:g> skryť?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Aktuálna relácia média sa nedá skryť."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skryť"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovať"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavenia"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> od interpreta <xliff:g id="ARTIST_NAME">%2$s</xliff:g> sa prehráva z aplikácie <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> z <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznáme"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Chcete resetovať všetky karty?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Všetky karty rýchlych nastavení sa resetujú na pôvodné nastavenia zariadenia"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
index 3e61884..5a4ce99 100644
--- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Vypnuté"</item>
     <item msgid="4875147066469902392">"Zapnuté"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nie je k dispozícii"</item>
     <item msgid="5044688398303285224">"Vypnuté"</item>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 2a1ed469..22d21bc 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Povezava vzpostavljena z: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Vzpostavljena povezava: <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Razširitev skupine."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Dodajte napravo v skupino."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Odstranite napravo iz skupine."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Odpiranje aplikacije."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Ni povezan."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Gostovanje"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vhodna naprava"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Vklapljanje …"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Svetlosti ni mogoče prilagoditi, ker jo nadzoruje aplikacija na vrhu"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Samodejno sukanje"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Samodejno sukanje zaslona"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolica"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Levo"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Desno"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Okolica"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Okolica na levi"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Okolica na desni"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Razširitev na ločene kontrolnike za levo in desno stran"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Strnitev v enotni kontrolnik"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Izklop okoliškega zvoka"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Vklop okoliškega zvoka"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Orodja"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Samodejni podnapisi"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Nastavitve"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Opomba"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite odblokirati mikrofon v napravi?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite odblokirati fotoaparat v napravi?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Vklopljeno – na podlagi obraza"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Končano"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Uporabi"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Izklopi obvestila"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Tiho"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Privzeto"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Samodejno"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Uporaba razdeljenega zaslona z aplikacijo na desni"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Uporaba razdeljenega zaslona z aplikacijo na levi"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Uporaba celozaslonskega načina"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Uporaba pogleda za namizni računalnik"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Uporaba namiznega načina prikaza več oken hkrati"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Položaj je neveljaven."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Ploščica je že dodana"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Ploščica je bila dodana"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Ploščica je bila odstranjena"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Urejevalnik hitrih nastavitev."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Želite skriti ta nadzor predstavnosti za <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Trenutne seje predstavnosti ni mogoče skriti."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Skrij"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Nadaljuj"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavitve"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Skladba <xliff:g id="SONG_NAME">%1$s</xliff:g> izvajalca <xliff:g id="ARTIST_NAME">%2$s</xliff:g> se predvaja iz aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> od <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznano"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Želite ponastaviti vse ploščice?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Vse ploščice v hitrih nastavitvah bodo ponastavljene na prvotne nastavitve naprave."</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
index 8146802..8f243de 100644
--- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Izklopljeno"</item>
     <item msgid="4875147066469902392">"Vklopljeno"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ni na voljo"</item>
     <item msgid="5044688398303285224">"Izklopljeno"</item>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 916595f..203b9fa 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Lidhur me <xliff:g id="BLUETOOTH">%s</xliff:g>"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Është lidhur me <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Zgjero grupin."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Shto pajisjen te grupi."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Hiq pajisjen nga grupi."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Hap aplikacionin."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Nuk është i lidhur."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Hyrja"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparatet e dëgjimit"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Po aktivizohet…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Ndriçimi nuk mund të rregullohet pasi po kontrollohet nga aplikacioni lart"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rrotullim automatik"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rrotullimi automatik i ekranit"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Vendndodhja"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambienti rrethues"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Majtas"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Djathtas"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Ambienti rrethues"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Ambienti rrethues majtas"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Ambienti rrethues djathtas"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Zgjero te kontrollet e veçuara majtas dhe djathtas"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Palos te kontrolli i unifikuar"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Vendos në heshtje ambientin rrethues"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Anulo vendosjen në heshtje të ambientit rrethues"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Veglat"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Titrat në çast"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Cilësimet"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Shënim"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Të zhbllokohet mikrofoni i pajisjes?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Të zhbllokohet kamera e pajisjes?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktiv - Në bazë të fytyrës"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"U krye"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Zbato"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Çaktivizo njoftimet"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Në heshtje"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"E parazgjedhur"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatike"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Përdor ekranin e ndarë me aplikacionin në të djathtë"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Përdor ekranin e ndarë me aplikacionin në të majtë"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Përdor ekranin e plotë"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Përdor pamjen e desktopit"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Përdor ndërfaqen me dritare në desktop"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Shto te pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozicion i pavlefshëm."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Pllakëza është shtuar tashmë"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Pllakëza u shtua"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Pllakëza u hoq"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redaktori i cilësimeve të shpejta."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Të fshihet kontrolluesi i medias për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Seanca aktuale e medias nuk mund të fshihet."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Fshih"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Vazhdo"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Cilësimet"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> nga <xliff:g id="ARTIST_NAME">%2$s</xliff:g> po luhet nga <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> nga <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nuk njihet"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Të rivendosen të gjitha pllakëzat?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Të gjitha pllakëzat e \"Cilësimeve të shpejta\" do të rivendosen te cilësimet origjinale të pajisjes"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index 7ee5631..ac14099 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Joaktiv"</item>
     <item msgid="4875147066469902392">"Aktiv"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Nuk ofrohet"</item>
     <item msgid="5044688398303285224">"Joaktiv"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 6bf3a69..97ca62a 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -423,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Окружење"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Лево"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Десно"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Окружење"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Лево окружење"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Десно окружење"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Прошири на контроле раздвојене на леву и десну страну"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Скупи у јединствену контролу"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Искључи звук окружења"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Укључи звук окружења"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Алатке"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Титл уживо"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Подешавања"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Белешка"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Желите да одблокирате микрофон уређаја?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Желите да одблокирате камеру уређаја?"</string>
@@ -796,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Укључено – на основу лица"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Примени"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Искључи обавештења"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Нечујно"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Подразумевано"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Аутоматска"</string>
@@ -907,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Користи подељени екран са апликацијом с десне стране"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Користи подељени екран са апликацијом с леве стране"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Користи приказ преко целог екрана"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Користи приказ за рачунаре"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Користи прозорски приказ на рачунару"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пређи у апликацију здесна или испод док је подељен екран"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пређите у апликацију слева или изнад док користите подељени екран"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"У режиму подељеног екрана: замена једне апликације другом"</string>
@@ -1003,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додајте на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Позиција је неважећа."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Плочица је већ додата"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Плочица је додата"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Плочица је уклоњена"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уређивач за Брза подешавања."</string>
@@ -1203,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Желите да сакријете ову контролу за медије за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Актуелна сесија медија не може да буде сакривена."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Сакриј"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Настави"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Подешавања"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> извођача <xliff:g id="ARTIST_NAME">%2$s</xliff:g> се пушта из апликације <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> од <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1571,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Непознато"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Желите да ресетујете све плочице?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Све плочице Брзих подешавања ће се ресетовати на првобитна подешавања уређаја"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
index df9e574..e7e34ae 100644
--- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Искључено"</item>
     <item msgid="4875147066469902392">"Укључено"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Недоступно"</item>
     <item msgid="5044688398303285224">"Искључено"</item>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index aa9f4a4..4cdc37d7 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ansluten till <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Ansluten till <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Utöka gruppen."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Lägg till enheten i gruppen."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Ta bort enheten från gruppen."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Öppna appen."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Inte ansluten."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ingång"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hörapparater"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiverar …"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Det går inte att ändra ljusstyrkan eftersom den styrs av den översta appen"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotera automatiskt"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotera skärmen automatiskt"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Plats"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivningsläge"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vänster"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Höger"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Omgivningsljud"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Omgivningsljud till vänster"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Omgivningsljud till höger"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Utöka till kontroller till vänster och höger"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Komprimera till enhetlig kontroll"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Stäng av omgivningsljudet"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Slå på omgivningsljudet"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Verktyg"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Inställningar"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Anteckning"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vill du återaktivera enhetens mikrofon?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vill du återaktivera enhetens kamera?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"På – ansiktsbaserad"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Klart"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Tillämpa"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Inaktivera aviseringar"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Tyst"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automatiskt"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Använd delad skärm med appen till höger"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Använd delad skärm med appen till vänster"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Använd helskärm"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Använd datorvyn"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Använd fönsterplacering på datorn"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Byt till appen till höger eller nedanför när du använder delad skärm"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Byt till appen till vänster eller ovanför när du använder delad skärm"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Med delad skärm: ersätt en app med en annan"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lägg till på position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Positionen är ogiltig."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Kortet har redan lagts till"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kortet har lagts till"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kortet har tagits bort"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigerare för snabbinställningar."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Vill du dölja denna mediastyrning för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Den aktuella mediesessionen kan inte döljas."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Dölj"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Återuppta"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Inställningar"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> med <xliff:g id="ARTIST_NAME">%2$s</xliff:g> spelas upp från <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> av <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Okänt"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vill du återställa alla rutor?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alla Snabbinställningsrutor återställs till enhetens ursprungliga inställningar"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index 76822d0..7ec70ea 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Av"</item>
     <item msgid="4875147066469902392">"På"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Inte tillgängligt"</item>
     <item msgid="5044688398303285224">"Av"</item>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 72bfb83..1cb61fd 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Imeunganishwa kwenye <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Imeunganishwa kwenye <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Panua kikundi."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Weka kifaa kwenye kikundi."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Ondoa kifaa kwenye kikundi."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Fungua programu."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Haijaunganishwa."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Mitandao ya ng\'ambo"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vifaa vya kuingiza sauti"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Visaidizi vya kusikia"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Inawasha..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Imeshindwa kurekebisha mwangaza kwa sababu inadhibitiwa na programu iliyo juu"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Zungusha kiotomatiki"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Skrini ijizungushe kiotomatiki"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Mahali"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Mazingira"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kushoto"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Kulia"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Mandhari"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Mandhari ya kushoto"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Mandhari ya kulia"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Panua iwe vidhibiti vilivyotenganishwa kushoto na kulia"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Kunja iwe kidhibiti cha pamoja"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Zima sauti ya mazingira"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Rejesha sauti ya mazingira"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Zana"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Manukuu Papo Hapo"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Mipangilio"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Dokezo"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Ungependa kuwacha kuzuia maikrofoni ya kifaa?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Ungependa kuacha kuzuia kamera ya kifaa?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Imewashwa - Inayolenga nyuso"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Nimemaliza"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Tumia"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Zima arifa"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Kimya"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Chaguomsingi"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatiki"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Tumia hali ya kugawa skrini na programu ya sasa iwe upande wa kulia"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Tumia hali ya kugawa skrini na programu ya sasa iwe upande wa kushoto"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Tumia skrini nzima"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Tumia mwonekano wa kompyuta"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Tumia hali ya madirisha ya kompyuta ya mezani"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ongeza kwenye nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Nafasi si sahihi."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Tayari umeweka kigae"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kigae kimewekwa"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kigae kimeondolewa"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kihariri cha Mipangilio ya haraka."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Ungependa kuficha kidhibiti hiki kwenye <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Kipindi cha sasa cha maudhui hakiwezi kufichwa."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ficha"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Endelea"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Mipangilio"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ulioimbwa na <xliff:g id="ARTIST_NAME">%2$s</xliff:g> unacheza katika <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> kati ya <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Visivyojulikana"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Ungependa kubadilisha vigae vyote?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Vigae vyote vya Mipangilio ya Haraka vitabadilishwa kuwa katika mipangilio halisi ya kifaa"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
index a16a7b7..9e26281 100644
--- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Kimezimwa"</item>
     <item msgid="4875147066469902392">"Kimewashwa"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Hakipatikani"</item>
     <item msgid="5044688398303285224">"Imezimwa"</item>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 31ab515..3109a13 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>க்கு இணைக்கப்பட்டது."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> உடன் இணைக்கப்பட்டுள்ளது."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"குழுவை விரிவாக்கும்."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"குழுவில் சாதனத்தைச் சேர்க்கும்."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"குழுவிலிருந்து சாதனத்தை அகற்றும்."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"ஆப்ஸைத் திறக்கும்."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"இணைக்கப்படவில்லை."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"ரோமிங்"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"உள்ளீடு"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"செவித்துணைக் கருவி"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ஆன் செய்கிறது…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"ஒளிர்வை மேலுள்ள ஆப்ஸ் கட்டுப்படுத்துவதால் அதை மாற்ற முடியவில்லை"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"தானாகச் சுழற்று"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"திரையைத் தானாகச் சுழற்று"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"இருப்பிடம்"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"சுற்றுப்புறங்கள்"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"இடது"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"வலது"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"சுற்றுப்புறங்கள்"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"இடதுபுறச் சுற்றுப்புறங்கள்"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"வலதுபுறச் சுற்றுப்புறங்கள்"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"இடது மற்றும் வலதுபுறம் உள்ள கட்டுப்பாடுகளை விரிவாக்கும்"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ஒருங்கிணைந்த கட்டுப்பாட்டுக்குச் சுருக்கும்"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"சுற்றுப்புறங்களின் ஒலியை அடக்கும்"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"சுற்றுப்புறங்களின் ஒலியை இயக்கும்"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"கருவிகள்"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"உடனடி வசனம்"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"அமைப்புகள்"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"குறிப்பு"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"சாதனத்தின் மைக்ரோஃபோனுக்கான தடுப்பை நீக்கவா?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"சாதனத்தின் கேமராவுக்கான தடுப்பை நீக்கவா?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ஆன் - முகம் அடிப்படையிலானது"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"முடிந்தது"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"பயன்படுத்து"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"அறிவிப்புகளை முடக்கு"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"சைலன்ட்"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"இயல்புநிலை"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"தானியங்கு"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ஆப்ஸ் வலதுபுறம் வரும்படி திரைப் பிரிப்பைப் பயன்படுத்துதல்"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ஆப்ஸ் இடதுபுறம் வரும்படி திரைப் பிரிப்பைப் பயன்படுத்துதல்"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"முழுத்திரையைப் பயன்படுத்து"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"டெஸ்க்டாப் காட்சியைப் பயன்படுத்து"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"டெஸ்க்டாப் சாளரமாக்குதலைப் பயன்படுத்துதல்"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது வலது/கீழ் உள்ள ஆப்ஸுக்கு மாறுதல்"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது இடது/மேலே உள்ள ஆப்ஸுக்கு மாறுதல்"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"திரைப் பிரிப்பின்போது: ஓர் ஆப்ஸுக்குப் பதிலாக மற்றொன்றை மாற்றுதல்"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>ல் சேர்க்கும்"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"நிலை தவறானது."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"இடம்: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"கட்டம் ஏற்கெனவே சேர்க்கப்பட்டது"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"கட்டம் சேர்க்கப்பட்டது"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"கட்டம் அகற்றப்பட்டது"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"விரைவு அமைப்புகள் திருத்தி."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கான இந்த மீடியா கட்டுப்பாடுகளை மறைக்கவா?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"தற்போதைய மீடியா அமர்வை மறைக்க முடியாது."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"மறை"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"தொடர்க"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"அமைப்புகள்"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> இன் <xliff:g id="SONG_NAME">%1$s</xliff:g> பாடல் <xliff:g id="APP_LABEL">%3$s</xliff:g> ஆப்ஸில் பிளேயாகிறது"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> / <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"தெரியவில்லை"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"அனைத்துக் கட்டங்களையும் மீட்டமைக்கவா?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"விரைவு அமைப்புகளின் கட்டங்கள் அனைத்தும் சாதனத்தின் அசல் அமைப்புகளுக்கு மீட்டமைக்கப்படும்"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index dc1c514..d3773a7 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"முடக்கப்பட்டுள்ளது"</item>
     <item msgid="4875147066469902392">"இயக்கப்பட்டுள்ளது"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"கிடைக்கவில்லை"</item>
     <item msgid="5044688398303285224">"முடக்கப்பட்டுள்ளது"</item>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index d26bfac..1bf3ab2 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g>కి కనెక్ట్ చేయబడింది."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"గ్రూప్‌ను విస్తరించండి."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"గ్రూప్‌కి పరికరాన్ని జోడించండి."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"గ్రూప్ నుండి పరికరాన్ని తీసివేయండి."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"యాప్‌ను తెరవండి."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"కనెక్ట్ చేయబడలేదు."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"రోమింగ్"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ఇన్‌పుట్"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"వినికిడి పరికరాలు"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ఆన్ చేస్తోంది…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"దీనిని టాప్ యాప్ కంట్రోల్ చేస్తోంది కనుక బ్రైట్‌నెస్‌ను సర్దుబాటు చేయడం సాధ్యం కాదు"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ఆటో-రొటేట్‌"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"స్క్రీన్ ఆటో-రొటేట్‌"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"లొకేషన్"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"పరిసరాలు"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ఎడమ వైపునకు"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"కుడి వైపునకు"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"పరిసరాలు"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"ఎడమ వైపు పరిసరాలు"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"కుడి వైపు పరిసరాలు"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ఎడమ, కుడి అని వేరు చేయబడిన కంట్రోల్స్‌కు విస్తరించండి"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"యూనిఫైడ్ కంట్రోల్‌కు కుదించండి"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"పరిసరాలను మ్యూట్ చేయండి"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"పరిసరాలను అన్‌మ్యూట్ చేయండి"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"టూల్స్"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"లైవ్ క్యాప్షన్"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"సెట్టింగ్‌లు"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"గమనిక"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్‌ను అన్‌బ్లాక్ చేయమంటారా?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరంలోని కెమెరాను అన్‌బ్లాక్ చేయమంటారా?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"\'ముఖం ఆధారం\'ను - ఆన్ చేయండి"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"పూర్తయింది"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"అప్లయి చేయి"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"నోటిఫికేషన్‌లను ఆఫ్ చేయి"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"నిశ్శబ్దం"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ఆటోమేటిక్ సెట్టింగ్"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"ఆటోమేటిక్"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"కుడి వైపు ప్రస్తుత యాప్‌తో స్ప్లిట్ స్క్రీన్‌ను ఉపయోగించండి"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ఎడమ వైపు ప్రస్తుత యాప్‌తో స్ప్లిట్ స్క్రీన్‌ను ఉపయోగించండి"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"ఫుల్ స్క్రీన్‌ను ఉపయోగించండి"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"డెస్క్‌టాప్ వీక్షణను ఉపయోగించండి"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"డెస్క్‌టాప్ విండోయింగ్‌ను ఉపయోగించండి"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు కుడి లేదా కింద యాప్‌నకు మారండి"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు ఎడమ లేదా పైన యాప్‌నకు మారండి"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"స్ప్లిట్ స్క్రీన్ సమయంలో: ఒక దాన్నుండి మరో దానికి యాప్ రీప్లేస్ చేయండి"</string>
@@ -997,8 +993,7 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"తక్కువ ప్రాధాన్యత నోటిఫికేషన్ చిహ్నాలను చూపించు"</string>
     <string name="other" msgid="429768510980739978">"ఇతరం"</string>
-    <!-- no translation found for accessibility_qs_edit_toggle_tile_size_action (1485194410119733586) -->
-    <skip />
+    <string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"టైల్ సైజ్‌ను టోగుల్ చేయండి"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"టైల్‌ను తీసివేయండి"</string>
     <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"చివరి పొజిషన్‌కు టైల్‌ను జోడించండి"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"టైల్‌ను తరలించండి"</string>
@@ -1007,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> స్థానానికి జోడించండి"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ప్రస్తుత పొజిషన్ చెల్లదు."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"టైల్‌ను ఇప్పటికే జోడించారు"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"టైల్ జోడించబడింది"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"టైల్ తీసివేయబడింది"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"శీఘ్ర సెట్టింగ్‌ల ఎడిటర్."</string>
@@ -1207,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం ఈ మీడియా కంట్రోల్‌ను దాచి ఉంచాలా?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"ప్రస్తుత మీడియా సెషన్‌ను దాచడం సాధ్యం కాదు."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"దాచండి"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"కొనసాగించండి"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"సెట్టింగ్‌లు"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> పాడిన <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%3$s</xliff:g> నుండి ప్లే అవుతోంది"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="TOTAL_TIME">%2$s</xliff:g>లో <xliff:g id="ELAPSED_TIME">%1$s</xliff:g>"</string>
@@ -1551,10 +1546,8 @@
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"ఇటీవలి యాప్‌లను చూడటానికి, టచ్‌ప్యాడ్‌లో మూడు వేళ్లతో పైకి స్వైప్ చేసి, హోల్డ్ చేయండి"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"మీ యాప్‌లన్నింటినీ చూడటానికి, మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి"</string>
     <string name="redacted_notification_single_line_title" msgid="212019960919261670">"దాచిపెట్టినది"</string>
-    <!-- no translation found for public_notification_single_line_text (3576190291791654933) -->
-    <skip />
-    <!-- no translation found for redacted_otp_notification_single_line_text (5179964116354454118) -->
-    <skip />
+    <string name="public_notification_single_line_text" msgid="3576190291791654933">"చూడటానికి అన్‌లాక్ చేయండి"</string>
+    <string name="redacted_otp_notification_single_line_text" msgid="5179964116354454118">"కోడ్‌ను చూడటానికి అన్‌లాక్ చేయండి"</string>
     <string name="contextual_education_dialog_title" msgid="4630392552837487324">"సందర్భోచిత విద్య"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"వెనుకకు వెళ్లడానికి మీ టచ్‌ప్యాడ్‌ను ఉపయోగించండి"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"మూడు వేళ్లతో ఎడమ / కుడి వైపునకు స్వైప్ చేయండి. మరిన్ని సంజ్ఞల గురించి తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
@@ -1577,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"తెలియదు"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"టైల్స్ అన్ని రీసెట్ చేయాలా?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"అన్ని క్విక్ సెట్టింగ్‌ల టైల్స్, పరికరం తాలూకు ఒరిజినల్ సెట్టింగ్‌లకు రీసెట్ చేయబడతాయి"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index d118429..72343a3 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"ఆఫ్‌లో ఉంది"</item>
     <item msgid="4875147066469902392">"ఆన్‌లో ఉంది"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"అందుబాటులో లేదు"</item>
     <item msgid="5044688398303285224">"ఆఫ్‌లో ఉంది"</item>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 9196f95..99aca81 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -423,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"เสียงแวดล้อม"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ซ้าย"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ขวา"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"เสียงแวดล้อม"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"เสียงแวดล้อมด้านซ้าย"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"เสียงแวดล้อมด้านขวา"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ขยายเป็นการควบคุมที่แยกด้านซ้ายและขวา"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ยุบเป็นการควบคุมแบบรวม"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ปิดเสียงแวดล้อม"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"เปิดเสียงแวดล้อม"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"เครื่องมือ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"คำบรรยายสด"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"การตั้งค่า"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"จดบันทึก"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"เลิกบล็อกไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"เลิกบล็อกกล้องของอุปกรณ์ใช่ไหม"</string>
@@ -796,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"เปิด - ตามใบหน้า"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"เสร็จสิ้น"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"ใช้"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"ปิดการแจ้งเตือน"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"ปิดเสียง"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ค่าเริ่มต้น"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"อัตโนมัติ"</string>
@@ -907,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"ใช้โหมดแยกหน้าจอโดยให้แอปอยู่ด้านขวา"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"ใช้โหมดแยกหน้าจอโดยให้แอปอยู่ด้านซ้าย"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"ใช้โหมดเต็มหน้าจอ"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ใช้มุมมองบนเดสก์ท็อป"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ใช้หน้าต่างเดสก์ท็อป"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"เปลี่ยนไปใช้แอปทางด้านขวาหรือด้านล่างขณะใช้โหมดแยกหน้าจอ"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"เปลี่ยนไปใช้แอปทางด้านซ้ายหรือด้านบนขณะใช้โหมดแยกหน้าจอ"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"ระหว่างใช้โหมดแยกหน้าจอ: เปลี่ยนแอปหนึ่งเป็นอีกแอปหนึ่ง"</string>
@@ -1003,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"เพิ่มไปยังตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ตำแหน่งไม่ถูกต้อง"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"เพิ่มการ์ดแล้ว"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"เพิ่มชิ้นส่วนแล้ว"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"นำชิ้นส่วนออกแล้ว"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ตัวแก้ไขการตั้งค่าด่วน"</string>
@@ -1203,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"ซ่อนตัวควบคุมสื่อนี้สำหรับ <xliff:g id="APP_NAME">%1$s</xliff:g> ไหม"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"ซ่อนเซสชันสื่อในปัจจุบันไม่ได้"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"ซ่อน"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"เล่นต่อ"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"การตั้งค่า"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"กำลังเปิดเพลง <xliff:g id="SONG_NAME">%1$s</xliff:g> ของ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> จาก <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> จาก <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1571,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ไม่ทราบ"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"รีเซ็ตการ์ดทั้งหมดใช่ไหม"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"การ์ดการตั้งค่าด่วนทั้งหมดจะรีเซ็ตเป็นการตั้งค่าเดิมของอุปกรณ์"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index 9fba3bd..b969056 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"ปิด"</item>
     <item msgid="4875147066469902392">"เปิด"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ไม่พร้อมใช้งาน"</item>
     <item msgid="5044688398303285224">"ปิด"</item>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index a6b0d6a..726cf21 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Nakakonekta sa <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Nakakonekta sa <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"I-expand ang grupo."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Idagdag ang device sa grupo."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Alisin ang device sa grupo."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Buksan ang application."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Hindi nakakonekta."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Roaming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Mga hearing aid"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ino-on…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Hindi ma-adjust ang liwanag dahil kinokontrol ito ng nangingibabaw na app"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"I-auto rotate"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Awtomatikong i-rotate ang screen"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasyon"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Paligid"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kaliwa"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Kanan"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Paligid"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Kaliwang bahagi ng paligid"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Kanang bahagi ng paligid"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"I-expand sa kaliwa at kanang magkahiwalay na mga kontrol"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"I-collapse sa pinag-isang kontrol"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"I-mute ang paligid"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"I-unmute ang paligid"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Mga Tool"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Instant Caption"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Mga Setting"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Tala"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"I-unblock ang mikropono ng device?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"I-unblock ang camera ng device?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Naka-on - Batay sa mukha"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Tapos na"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Ilapat"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"I-off ang mga notification"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Naka-silent"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Awtomatiko"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Gumamit ng split screen nang nasa kanan ang app"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Gumamit ng split screen nang nasa kaliwa ang app"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Gamitin ang full screen"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Gamitin ang desktop view"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Gamitin ang desktop windowing"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Idagdag sa posisyong <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Invalid ang posisyon."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Naidagdag na ang tile"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Idinagdag ang tile"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Inalis ang tile"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor ng Mga mabilisang setting."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"itago ang kontrol sa media na ito para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Hindi maitatago ang kasalukuyang session ng media."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Itago"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Ituloy"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Mga Setting"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Nagpe-play ang <xliff:g id="SONG_NAME">%1$s</xliff:g> ni/ng <xliff:g id="ARTIST_NAME">%2$s</xliff:g> mula sa <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> sa <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
index 03cca6d..92a122d 100644
--- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Naka-off"</item>
     <item msgid="4875147066469902392">"Naka-on"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Hindi available"</item>
     <item msgid="5044688398303285224">"Naka-off"</item>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 13d6cd7..a68bc74 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> ile bağlı."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> bağlantısı kuruldu."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Grubu genişlet."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Cihazı gruba ekle."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Cihazı gruptan kaldır."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Uygulama aç."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Bağlanmadı."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Dolaşım"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Giriş"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"İşitme cihazları"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Açılıyor…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Parlaklık ayarlanamıyor, çünkü bu özellik en üstteki uygulama tarafından kontrol ediliyor"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Otomatik döndür"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranı otomatik döndür"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Konum"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Çevredeki sesler"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sol"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Sağ"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Çevredeki sesler"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Sol çevredeki sesler"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Sağ çevredeki sesler"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Sol ve sağ kontrolleri ayırarak genişlet"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Kontrolleri birleştirerek daralt"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Çevredeki sesleri kapat"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Çevredeki sesleri aç"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Araçlar"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Canlı Altyazı"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Ayarlar"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Not"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonunun engellemesi kaldırılsın mı?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerasının engellemesi kaldırılsın mı?"</string>
@@ -518,7 +513,7 @@
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ortak eğitimi başlatmak için sola kaydırın"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Özelleştir"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Kapat"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Burada widget\'larınızı ekleyin, kaldırın ve düzenleyin"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Widget ekleyin, kaldırın ve düzenleyin"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Daha fazla widget ekle"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Widget\'ları özelleştirmek için uzun basın"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Widget\'ları özelleştir"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Açık - Yüze göre"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Bitti"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Uygula"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Bildirimleri kapat"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Sessiz"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Varsayılan"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Otomatik"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Sağdaki uygulamayla birlikte bölünmüş ekranı kullan"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Soldaki uygulamayla birlikte bölünmüş ekranı kullan"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Tam ekran kullanın"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Masaüstü görünümünü kullanın"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Masaüstü pencere sistemini kullan"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna ekle"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Konum geçersiz."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Konum: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Hızlı ayar kutusu daha önce eklendi"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kutu eklendi"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kutu kaldırıldı"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Hızlı ayar düzenleyicisi."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> için bu medya kontrolü gizlensin mi?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Geçerli medya oturumu gizlenemez."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Gizle"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Devam ettir"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> uygulamasından <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, <xliff:g id="SONG_NAME">%1$s</xliff:g> şarkısı çalıyor"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>/<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Bilinmiyor"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Tüm ayar kutuları sıfırlansın mı?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tüm Hızlı Ayarlar kutuları cihazın özgün ayarlarına sıfırlanır"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
index 7c17ace..986981f 100644
--- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Kapalı"</item>
     <item msgid="4875147066469902392">"Açık"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Kullanılamıyor"</item>
     <item msgid="5044688398303285224">"Kapalı"</item>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index f0f4e38..2e02293 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -247,15 +247,13 @@
     <string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"Натисніть, щоб змінити налаштування пристрою"</string>
     <string name="accessibility_bluetooth_device_settings_gear_with_name" msgid="114373701123165491">"<xliff:g id="DEVICE_NAME">%s</xliff:g>. Змінити налаштування пристрою"</string>
     <string name="accessibility_bluetooth_device_settings_see_all" msgid="5260390270128256620">"Переглянути всі пристрої"</string>
-    <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="7988547106800504256">"Підключити новий пристрій"</string>
+    <string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="7988547106800504256">"Зв’язати новий пристрій"</string>
     <string name="accessibility_battery_unknown" msgid="1807789554617976440">"Відсоток заряду акумулятора невідомий."</string>
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Підключено до <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Під’єднано до пристрою <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Розгорнути групу"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Додати пристрій у групу."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Вилучити пристрій із групи."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Відкрити додаток"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Не з’єднано."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Роумінг"</string>
@@ -308,7 +306,7 @@
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Немає спарених пристроїв"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Натисніть, щоб під’єднати або від’єднати пристрій"</string>
-    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Підключити новий пристрій"</string>
+    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Зв’язати новий пристрій"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Показати всі"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Увімкнути Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Підключено"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Джерело сигналу"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слухові апарати"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Увімкнення…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Не вдається змінити яскравість, оскільки нею керує основний додаток"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автообертання"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично обертати екран"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Геодані"</string>
@@ -418,7 +415,7 @@
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Під’єднано"</string>
     <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Від’єднано"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухові апарати"</string>
-    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Підключити новий пристрій"</string>
+    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Зв’язати новий пристрій"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Натисніть, щоб підключити новий пристрій"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не вдалось оновити набір налаштувань"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набір налаштувань"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Звуки оточення"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ліворуч"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Праворуч"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Звуки оточення"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Звуки оточення ліворуч"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Звуки оточення праворуч"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Розгорнути в окремі елементи керування ліворуч і праворуч"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Згорнути в єдиний елемент керування"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Вимкнути звуки оточення"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Увімкнути звуки оточення"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Інструменти"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Живі субтитри"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Налаштування"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Нотатка"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Надати доступ до мікрофона?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Надати доступ до камери пристрою?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Увімкнути (за обличчям)"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Застосувати"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Вимкнути сповіщення"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Без звуку"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"За умовчанням"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматично"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Розділити екран і показувати додаток праворуч"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Розділити екран і показувати додаток ліворуч"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Використовувати повноекранний режим"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Використовувати версію для комп’ютера"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Використовувати режим вікон для комп’ютера"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Перейти до додатка праворуч або внизу на розділеному екрані"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Під час розділення екрана перемикатися на додаток ліворуч або вгорі"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"Під час розділення екрана: замінити додаток іншим"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додати на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Позиція недійсна."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Плитку вже додано"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Опцію додано"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Опцію вилучено"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор швидких налаштувань."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Приховати цей елемент керування для <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Поточний медіасеанс не можна приховати."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Приховати"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Відновити"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Налаштування"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Пісня \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\", яку виконує <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, грає в додатку <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> з <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Невідомо"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Скинути всі панелі?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Усі панелі швидких налаштувань буде скинуто до стандартних налаштувань пристрою"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index 8b50c085..a31e858 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Вимкнено"</item>
     <item msgid="4875147066469902392">"Увімкнено"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Недоступно"</item>
     <item msgid="5044688398303285224">"Вимкнено"</item>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index ef10b87..c5a9609 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> سے منسلک ہیں۔"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> سے منسلک ہے۔"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"گروپ کو پھیلائیں۔"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"آلہ کو گروپ میں شامل کریں۔"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"آلہ کو گروپ سے ہٹائیں۔"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"ایپلیکیشن کھولیں۔"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"مربوط نہیں ہے۔"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"رومنگ"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ان پٹ"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سماعتی آلات"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"آن ہو رہا ہے…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"چمک کو ایڈجسٹ نہیں کیا جا سکتا کیونکہ اسے سرفہرست ایپ کے ذریعے کنٹرول کیا جا رہا ہے"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"خود کار طور پر گھمائیں"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"اسکرین کو خود کار طور پر گھمائیں"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"مقام"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"اطراف"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"دائیں"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"بائیں"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"سراؤنڈنگ ساؤنڈ"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"بایاں سراؤنڈنگ ساؤنڈ"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"دایاں سراؤنڈنگ ساؤنڈ"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"بائیں اور دائیں علیحدہ کردہ کنٹرولز کو پھیلائیں"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"یونیفائیڈ کنٹرول کیلئے سکیڑیں"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"اطراف کو خاموش کریں"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"اطراف کی آواز چالو کریں"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ٹولز"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"لائیو کیپشن"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"ترتیبات"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"نوٹ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"آلے کا مائیکروفون غیر مسدود کریں؟"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"آلے کا کیمرا غیر مسدود کریں؟"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"آن - چہرے پر مبنی"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"ہو گیا"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"لاگو کریں"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"اطلاعات کو آف کریں"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"خاموش"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"ڈیفالٹ"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"خودکار"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"بائیں جانب ایپ کے ساتھ اسپلٹ اسکرین کا استعمال کریں"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"دائیں جانب ایپ کے ساتھ اسپلٹ اسکرین کا استعمال کریں"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"فُل اسکرین استعمال کریں"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"ڈیسک ٹاپ منظر استعمال کریں"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"ڈیسک ٹاپ ونڈو موڈ استعمال کریں"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"اسپلٹ اسکرین کا استعمال کرتے ہوئے دائیں یا نیچے ایپ پر سوئچ کریں"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"اسپلٹ اسکرین کا استعمال کرتے ہوئے بائیں یا اوپر ایپ پر سوئچ کریں"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"اسپلٹ اسکرین کے دوران: ایک ایپ کو دوسرے سے تبدیل کریں"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g> میں شامل کریں"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"پوزیشن غلط ہے۔"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"ٹائل پہلے ہی شامل کر دیا گیا ہے"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ٹائل کو شامل کیا گیا"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ٹائل کو ہٹا دیا گیا"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"فوری ترتیبات کا ایڈیٹر۔"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے اس میڈیا کنٹرول کو چھپائیں؟"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"میڈیا کے موجودہ سیشن کو چھپایا نہیں جا سکتا۔"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"چھپائیں"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"دوبارہ شروع کریں"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"ترتیبات"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> سے <xliff:g id="ARTIST_NAME">%2$s</xliff:g> کا <xliff:g id="SONG_NAME">%1$s</xliff:g> چل رہا ہے"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> از <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
index fea0533..ea032f1 100644
--- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"آف ہے"</item>
     <item msgid="4875147066469902392">"آن ہے"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"دستیاب نہیں ہے"</item>
     <item msgid="5044688398303285224">"آف ہے"</item>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index facc51f..fee0eb9 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Ulangan: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Bunga ulangan: <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Guruhni yoying."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Qurilmani guruhga kiritish."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Qurilmani guruhdan olib tashlash."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Ilovani oching."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Ulanmagan."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Rouming"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Kirish"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Eshitish moslamalari"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Yoqilmoqda…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Yorqinlikni sozlash imkonsiz, chunki tepadago ilova tomonidan boshqariladi"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avto-burilish"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranning avtomatik burilishi"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Atrof-muhit"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Chap"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Oʻng"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Atrof tovushlari"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Chap tomondan tovushlar"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Oʻng tomondan tovushlar"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Chap va oʻngga ajratilgan boshqaruv elementlariga yoyish"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Yagona boshqaruvga yigʻish"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Atrof-muhitni ovozsiz qilish"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Atrof-muhitni sukutdan chiqarish"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Vositalar"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Jonli izoh"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Sozlamalar"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Qayd"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Qurilma mikrofoni blokdan chiqarilsinmi?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Qurilma kamerasi blokdan chiqarilsinmi?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Yoqish - Yuz asosida"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Tayyor"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Tatbiq etish"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Bildirishnoma kelmasin"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Sokin"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Standart"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Ekranni ajratib, joriy ilovani oʻngga joylash"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Ekranni ajratib, joriy ilovani chapga joylash"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Butun ekrandan foydalanish"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Desktop versiyadan foydalanish"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Oynalarni desktop rejimda chiqarish"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bu joyga kiritish: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozitsiya yaroqsiz."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Joylashuv: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Katakcha allaqachon kiritilgan"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Katakcha kiritildi"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Katakcha olib tashlandi"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Tezkor sozlamalar muharriri"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun media boshqaruvi berkitilsinmi?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Joriy media seansi berkitilmadi."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Berkitish"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Davom etish"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Sozlamalar"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g> ilovasida ijro etilmoqda: <xliff:g id="SONG_NAME">%1$s</xliff:g> – <xliff:g id="ARTIST_NAME">%2$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> / <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Noaniq"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Barcha katakchalar asliga qaytarilsinmi?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Barcha Tezkor sozlamalar katakchalari qurilmaning asl sozlamalariga qaytariladi"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
index 9b8ca80..1433a9b 100644
--- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Oʻchiq"</item>
     <item msgid="4875147066469902392">"Yoniq"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Ishlamaydi"</item>
     <item msgid="5044688398303285224">"Oʻchiq"</item>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 80db132..c79eef76 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Đã kết nối với <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Đã kết nối với <xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Mở rộng nhóm."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Thêm thiết bị vào nhóm."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Xoá thiết bị khỏi nhóm."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Mở ứng dụng."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Chưa được kết nối."</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Chuyển vùng"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Thiết bị đầu vào"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Thiết bị trợ thính"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Đang bật…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Không điều chỉnh được độ sáng do ứng dụng ở trên cùng đang điều khiển độ sáng"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Tự động xoay"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Tự động xoay màn hình"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Vị trí"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Âm lượng xung quanh"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Trái"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Phải"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Môi trường xung quanh"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Môi trường xung quanh bên trái"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Môi trường xung quanh bên phải"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Mở rộng thành các nút điều khiển tách biệt bên trái và bên phải"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Thu gọn thành nút điều khiển hợp nhất"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Tắt tiếng xung quanh"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Bật tiếng xung quanh"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Công cụ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Phụ đề trực tiếp"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Cài đặt"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Ghi chú"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Bỏ chặn micrô của thiết bị?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Bỏ chặn camera của thiết bị?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Đang bật – Dựa trên khuôn mặt"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Xong"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Áp dụng"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Tắt thông báo"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Im lặng"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Mặc định"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Tự động"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Dùng tính năng chia đôi màn hình với ứng dụng ở bên phải"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Dùng tính năng chia đôi màn hình với ứng dụng ở bên trái"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Dùng chế độ toàn màn hình"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Dùng chế độ xem trên máy tính để bàn"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Sử dụng chế độ cửa sổ trên máy tính"</string>
     <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>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Thêm vào vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Vị trí không hợp lệ."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Đã thêm ô"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Đã thêm thẻ thông tin"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Đã xóa thẻ thông tin"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Trình chỉnh sửa cài đặt nhanh."</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Ẩn tính năng điều khiển này cho <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Không thể ẩn phiên phát nội dung nghe nhìn hiện tại."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Ẩn"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Tiếp tục"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Cài đặt"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"Đang phát <xliff:g id="SONG_NAME">%1$s</xliff:g> của <xliff:g id="ARTIST_NAME">%2$s</xliff:g> trên <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>/<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Không xác định"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Đặt lại mọi ô?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Mọi ô Cài đặt nhanh sẽ được đặt lại về chế độ cài đặt ban đầu của thiết bị"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
index afbf477..02d2bb6 100644
--- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Đang tắt"</item>
     <item msgid="4875147066469902392">"Đang bật"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Không hoạt động"</item>
     <item msgid="5044688398303285224">"Đang tắt"</item>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index feb3989..f0f55a8 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -150,7 +150,7 @@
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"停止投屏吗?"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"您正在将整个屏幕投放到“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”上"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"您正在将整个屏幕投放到附近的设备上"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"您正在将“<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>”投放到“<xliff:g id="DEVICE_NAME">%2$s</xliff:g>”上"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"目前正在将“<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>”投放到“<xliff:g id="DEVICE_NAME">%2$s</xliff:g>”"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"您正在将“<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>”投放到附近的设备上"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"您正在向“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”上投放内容"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"您正在向附近的设备投放内容"</string>
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已连接到<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"已连接到 <xliff:g id="CAST">%s</xliff:g>。"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"展开群组。"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"将设备添加到群组。"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"从群组中移除设备。"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"打开应用。"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"未连接。"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"漫游"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"输入"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助听器"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"正在开启…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"亮度无法调整,因为它正在被顶层应用控制"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自动屏幕旋转"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自动旋转屏幕"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"位置信息"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"周围声音"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左侧"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"右侧"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"环境音"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"左侧环境音"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"右侧环境音"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"展开为左侧和右侧的单独控件"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"收起为统一控件"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"将周围声音静音"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"取消周围声音静音"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"工具"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"实时字幕"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"设置"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"记事"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解锁设备麦克风吗?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解锁设备摄像头吗?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已开启 - 基于人脸"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"应用"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"关闭通知"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"静音"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"默认"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"自动"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"使用分屏模式,并将应用置于右侧"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"使用分屏模式,并将应用置于左侧"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"使用全屏"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"使用桌面版视图"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"使用桌面设备窗口化模式"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分屏模式时,切换到右侧或下方的应用"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分屏模式时,切换到左侧或上方的应用"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"在分屏期间:将一个应用替换为另一个应用"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"添加到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"位置无效。"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"功能块已添加"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"已添加功能块"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"已移除功能块"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快捷设置编辑器。"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"要针对“<xliff:g id="APP_NAME">%1$s</xliff:g>”隐藏此媒体控件吗?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"无法隐藏当前的媒体会话。"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"隐藏"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"继续播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"设置"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"正在通过<xliff:g id="APP_LABEL">%3$s</xliff:g>播放<xliff:g id="ARTIST_NAME">%2$s</xliff:g>的《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> / <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"未知"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"要重置所有功能块吗?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有“快捷设置”功能块都将重置为设备的原始设置"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>,<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index a3922e5..5980c31 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"已关闭"</item>
     <item msgid="4875147066469902392">"已开启"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"不可用"</item>
     <item msgid="5044688398303285224">"已关闭"</item>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 031df00..caac743 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已連線至<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"已連接至 <xliff:g id="CAST">%s</xliff:g>。"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"展開群組。"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"將裝置新增至群組。"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"從群組移除裝置。"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"開啟應用程式。"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"未連線。"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"漫遊"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"輸入"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助聽器"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"正在開啟…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"無法調整亮度,因為目前是由上層應用程式控制亮度"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"位置"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"環境聲音"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"右"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"環境"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"左邊環境"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"右邊環境"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"打開就可以分開左右控制"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"收埋就可以統一控制"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"環境聲音靜音"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"取消環境聲音靜音"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"工具"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"即時字幕"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"設定"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"筆記"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已開啟 - 根據面孔偵測"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"套用"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"關閉通知"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"靜音"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"預設"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"使用分割螢幕,並在右側顯示應用程式"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"使用分割螢幕,並在左側顯示應用程式"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"使用全螢幕"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"使用桌面電腦檢視模式"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"使用桌面電腦視窗模式"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分割螢幕時,切換至右邊或下方的應用程式"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割螢幕時,切換至左邊或上方的應用程式"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"使用分割螢幕期間:更換應用程式"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"加去位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"位置冇效。"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"已新增資訊方塊"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"加咗圖塊"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"移除咗圖塊"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯工具。"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"要隱藏此「<xliff:g id="APP_NAME">%1$s</xliff:g>」媒體控制嗎?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"無法隱藏目前的媒體工作階段。"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"隱藏"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"正在透過 <xliff:g id="APP_LABEL">%3$s</xliff:g> 播放 <xliff:g id="ARTIST_NAME">%2$s</xliff:g> 的《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>/<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"要重設所有圖塊嗎?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有「快速設定」圖塊將重設為裝置的原始設定"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>,<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index 26bec2c..7369d95 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"已關閉"</item>
     <item msgid="4875147066469902392">"已開啟"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"無法使用"</item>
     <item msgid="5044688398303285224">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index df05577..d3a5544 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -252,10 +252,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"已連線至<xliff:g id="BLUETOOTH">%s</xliff:g>。"</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"已連線至 <xliff:g id="CAST">%s</xliff:g>。"</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"展開群組。"</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"將裝置加入群組。"</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"從群組中移除裝置。"</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"開啟應用程式。"</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"尚未連線。"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"漫遊"</string>
@@ -333,8 +331,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"輸入"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助聽器"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"開啟中…"</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"無法調整亮度,因為目前是由上層應用程式控制亮度"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"定位"</string>
@@ -426,18 +423,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"環境"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"右"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"環境"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"左側環境"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"右側環境"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"展開為左右獨立控制選項"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"收合為統合控制選項"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"將環境靜音"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"取消環境靜音"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"工具"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"即時字幕"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"設定"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"筆記"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"解除封鎖裝置相機?"</string>
@@ -799,7 +794,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已開啟 - 依臉部方向旋轉"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"套用"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"關閉通知"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"靜音"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"預設"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
@@ -910,7 +906,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"使用分割畫面,並在右側顯示應用程式"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"使用分割畫面,並在左側顯示應用程式"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"使用全螢幕模式"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"使用電腦檢視畫面"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"使用電腦視窗化模式"</string>
     <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分割畫面時,切換到右邊或上方的應用程式"</string>
     <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割畫面時,切換到左邊或上方的應用程式"</string>
     <string name="system_multitasking_replace" msgid="7410071959803642125">"使用分割畫面期間:更換應用程式"</string>
@@ -1006,6 +1002,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"新增到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"位置無效。"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"已新增過該設定方塊"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"已新增設定方塊"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"已移除設定方塊"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯器。"</string>
@@ -1206,7 +1203,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"要隱藏「<xliff:g id="APP_NAME">%1$s</xliff:g>」的媒體控制選項嗎?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"無法隱藏目前的媒體工作階段。"</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"隱藏"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"系統正透過「<xliff:g id="APP_LABEL">%3$s</xliff:g>」播放<xliff:g id="ARTIST_NAME">%2$s</xliff:g>的〈<xliff:g id="SONG_NAME">%1$s</xliff:g>〉"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g>,共 <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1574,6 +1570,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"要重設所有設定方塊嗎?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有快速設定方塊都會恢復裝置的原始設定"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>、<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index 6e272f2..18a6d12 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"已關閉"</item>
     <item msgid="4875147066469902392">"已開啟"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"無法使用"</item>
     <item msgid="5044688398303285224">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index d571f0e..08657d1 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -254,10 +254,8 @@
     <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"Xhuma ku-<xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="7344437925388773685">"Ixhumeke ku-<xliff:g id="CAST">%s</xliff:g>."</string>
     <string name="accessibility_expand_group" msgid="521237935987978624">"Nweba iqembu."</string>
-    <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
-    <skip />
-    <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
-    <skip />
+    <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"Faka idivayisi eqenjini."</string>
+    <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"Susa idivayisi eqenjini."</string>
     <string name="accessibility_open_application" msgid="1749126077501259712">"Vula i-application."</string>
     <string name="accessibility_not_connected" msgid="4061305616351042142">"Akuxhunyiwe"</string>
     <string name="data_connection_roaming" msgid="375650836665414797">"Iyazulazula"</string>
@@ -335,8 +333,7 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Okokufaka"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Imishini yendlebe"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Iyavula..."</string>
-    <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
-    <skip />
+    <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"Ayikwazi ukulungisa ukukhanya ngoba ilawulwa yi-app ephezulu"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ukuphenduka okuzenzakalelayo"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Phendula iskrini ngokuzenzakalela"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Indawo"</string>
@@ -428,18 +425,16 @@
     <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Izindawo ezizungezile"</string>
     <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kwesokunxele"</string>
     <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Kwesokudla"</string>
-    <!-- no translation found for hearing_devices_ambient_control_description (3663947879732939509) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left_description (4440988622896213511) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right_description (2230461103493378003) -->
-    <skip />
+    <string name="hearing_devices_ambient_control_description" msgid="3663947879732939509">"Izindawo ezizungezile"</string>
+    <string name="hearing_devices_ambient_control_left_description" msgid="4440988622896213511">"Indawo ezungezile engakwesokunxele"</string>
+    <string name="hearing_devices_ambient_control_right_description" msgid="2230461103493378003">"Indawo ezungezile engakwesokudla"</string>
     <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Nwebela ezilawulini ezihlukanisiwe zakwesokunxele nakwesokudla"</string>
     <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Goqa ezilawulini ezihlanganisiwe"</string>
     <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Thulisa izindawo ezizungezile"</string>
     <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Susa ukuthula ezindaweni ezizungezile"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Amathuluzi"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Okushuthwe Bukhoma"</string>
+    <string name="hearing_devices_settings_button" msgid="999474385481812222">"Amasethingi"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Inothi"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vulela imakrofoni yedivayisi?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vulela ikhamera yedivayisi?"</string>
@@ -801,7 +796,8 @@
     <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Vuliwe - Kususelwe kubuso"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Kwenziwe"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Faka"</string>
-    <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Vala izaziso"</string>
+    <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
+    <skip />
     <string name="notification_silence_title" msgid="8608090968400832335">"Kuthulile"</string>
     <string name="notification_alert_title" msgid="3656229781017543655">"Okuzenzekelayo"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Okuzenzekelayo"</string>
@@ -912,7 +908,7 @@
     <string name="system_multitasking_rhs" msgid="8779289852395243004">"Sebenzisa ukuhlukanisa isikrini nge-app kwesokudla"</string>
     <string name="system_multitasking_lhs" msgid="7348595296208696452">"Sebenzisa ukuhlukanisa isikrini nge-app kwesokunxele"</string>
     <string name="system_multitasking_full_screen" msgid="4221409316059910349">"Sebenzisa isikrini esigcwele"</string>
-    <string name="system_multitasking_desktop_view" msgid="8829838918507805921">"Sebenzisa ukubuka kwedeskithophu"</string>
+    <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Sebenzisa iwindi ledeskithophu"</string>
     <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>
@@ -1008,6 +1004,7 @@
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engeza kusikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Indawo ayivumelekile."</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Isikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
+    <string name="accessibility_qs_edit_tile_already_added" msgid="5900071201690226752">"Ithayela lifakiwe kakade"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Ithayela lingeziwe"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Ithayela likhishiwe"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Isihleli sezilungiselelo ezisheshayo."</string>
@@ -1208,7 +1205,6 @@
     <string name="controls_media_close_session" msgid="4780485355795635052">"Fihlela i-<xliff:g id="APP_NAME">%1$s</xliff:g> lesi silawuli semidiya?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Iseshini yamanje yemidiya ayikwazi ukufihlwa."</string>
     <string name="controls_media_dismiss_button" msgid="4485675693008031646">"Fihla"</string>
-    <string name="controls_media_resume" msgid="1933520684481586053">"Qalisa kabusha"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Izilungiselelo"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"I-<xliff:g id="SONG_NAME">%1$s</xliff:g> ka-<xliff:g id="ARTIST_NAME">%2$s</xliff:g> idlala kusuka ku-<xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
     <string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> ku-<xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
@@ -1576,6 +1572,5 @@
     <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Akwaziwa"</string>
     <string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Qala kabusha onke amathayela?"</string>
     <string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Ithayela Lamasethingi Asheshayo lizosetha kabusha libuyele kumasethingi okuqala edivayisi"</string>
-    <!-- no translation found for volume_slider_disabled_message_template (1305088816797803460) -->
-    <skip />
+    <string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
index 13ffe04..674aeaa 100644
--- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
@@ -56,6 +56,9 @@
     <item msgid="5376619709702103243">"Valiwe"</item>
     <item msgid="4875147066469902392">"Vuliwe"</item>
   </string-array>
+    <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
+    <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
+    <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Akutholakali"</item>
     <item msgid="5044688398303285224">"Valiwe"</item>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f4c6904..15519ff 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -34,7 +34,7 @@
 
     <!-- Base colors for notification shade/scrim, the alpha component is adjusted programmatically
     to match the spec -->
-    <color name="shade_panel_base">@android:color/system_accent1_900</color>
+    <color name="shade_panel_base">@android:color/system_accent1_100</color>
     <color name="notification_scrim_base">@android:color/system_accent1_100</color>
 
     <!-- Fallback colors for notification shade/scrim -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 78e719f..549fdef 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -115,7 +115,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices,notes,desktopeffects
+        internet,bt,flashlight,dnd,modes_dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices,notes,desktopeffects
     </string>
 
     <!-- The tiles to display in QuickSettings -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8342a9c..d0ae307 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -283,7 +283,7 @@
     <dimen name="notification_max_height">358dp</dimen>
 
     <!-- Height of a large promoted ongoing notification in the status bar -->
-    <dimen name="notification_max_height_for_promoted_ongoing">272dp</dimen>
+    <dimen name="notification_max_height_for_promoted_ongoing">218dp</dimen>
 
     <!-- Height of a heads up notification in the status bar for legacy custom views -->
     <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
@@ -1157,6 +1157,8 @@
     <dimen name="smart_action_button_icon_size">18dp</dimen>
     <dimen name="smart_action_button_icon_padding">8dp</dimen>
     <dimen name="smart_action_button_outline_stroke_width">2dp</dimen>
+    <dimen name="notification_2025_smart_reply_button_corner_radius">18dp</dimen>
+    <dimen name="notification_2025_smart_reply_button_min_height">48dp</dimen>
 
     <!-- Magic Action params. -->
     <!-- Corner radius = half of min_height to create rounded sides. -->
@@ -2166,7 +2168,7 @@
     <dimen name="volume_dialog_background_top_margin">-28dp</dimen>
 
     <dimen name="volume_dialog_window_margin">14dp</dimen>
-    <dimen name="volume_dialog_components_spacing">8dp</dimen>
+    <dimen name="volume_dialog_components_spacing">10dp</dimen>
     <dimen name="volume_dialog_floating_sliders_spacing">8dp</dimen>
     <dimen name="volume_dialog_floating_sliders_vertical_padding">10dp</dimen>
     <dimen name="volume_dialog_floating_sliders_vertical_padding_negative">
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c06c17a..924eaa4 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1016,6 +1016,13 @@
     <string name="hearing_devices_presets_error">Couldn\'t update preset</string>
     <!-- QuickSettings: Title for hearing aids presets. Preset is a set of hearing aid settings. User can apply different settings in different environments (e.g. Outdoor, Restaurant, Home) [CHAR LIMIT=40]-->
     <string name="hearing_devices_preset_label">Preset</string>
+    <!-- QuickSettings: Title for hearing aids input routing control in hearing device dialog. [CHAR LIMIT=40]-->
+    <string name="hearing_devices_input_routing_label">Default microphone for calls</string>
+    <!-- QuickSettings: Option for hearing aids input routing control in hearing device dialog. It will alter input routing for calls for hearing aid. [CHAR LIMIT=40]-->
+    <string-array name="hearing_device_input_routing_options">
+        <item>Hearing aid microphone</item>
+        <item>This phone\'s microphone</item>
+    </string-array>
     <!-- QuickSettings: Content description for the icon that indicates the item is selected [CHAR LIMIT=NONE]-->
     <string name="hearing_devices_spinner_item_selected">Selected</string>
     <!-- QuickSettings: Title for ambient controls. [CHAR LIMIT=40]-->
@@ -1476,6 +1483,8 @@
     <string name="media_projection_entry_app_permission_dialog_continue_entire_screen">Share screen</string>
     <!-- 1P/3P apps disabled the single app projection option. [CHAR LIMIT=NONE] -->
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled"><xliff:g id="app_name" example="Meet">%1$s</xliff:g> has disabled this option</string>
+    <!-- Explanation that the app requesting the projection does not support single app sharing[CHAR LIMIT=70] -->
+    <string name="media_projection_entry_app_permission_dialog_single_app_not_supported">Not supported by the app</string>
     <!-- Title of the activity that allows users to select an app to share to a 1P/3P app [CHAR LIMIT=70] -->
     <string name="media_projection_entry_share_app_selector_title">Choose app to share</string>
 
@@ -2056,7 +2065,7 @@
     <string name="inline_ok_button">Apply</string>
 
     <!-- Notification inline controls: button to show block screen [CHAR_LIMIT=35] -->
-    <string name="inline_turn_off_notifications">Turn off notifications</string>
+    <string name="inline_turn_off_notifications">Turn off</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance title -->
     <string name="notification_silence_title">Silent</string>
@@ -2357,8 +2366,8 @@
     <string name="system_multitasking_lhs">Use split screen with app on the left</string>
     <!-- User visible title for the keyboard shortcut that switches from split screen to full screen [CHAR LIMIT=70] -->
     <string name="system_multitasking_full_screen">Use full screen</string>
-    <!-- User visible title for the keyboard shortcut that switches to desktop view [CHAR LIMIT=70] -->
-    <string name="system_multitasking_desktop_view">Use desktop view</string>
+    <!-- User visible title for the keyboard shortcut that switches to desktop windowing [CHAR LIMIT=70] -->
+    <string name="system_multitasking_desktop_view">Use desktop windowing</string>
     <!-- User visible title for the keyboard shortcut that switches to app on right or below while using split screen [CHAR LIMIT=70] -->
     <string name="system_multitasking_splitscreen_focus_rhs">Switch to app on right or below while using split screen</string>
     <!-- User visible title for the keyboard shortcut that switches to app on left or above while using split screen [CHAR LIMIT=70] -->
@@ -3181,8 +3190,6 @@
     <string name="controls_media_active_session">The current media session cannot be hidden.</string>
     <!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
     <string name="controls_media_dismiss_button">Hide</string>
-    <!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
-    <string name="controls_media_resume">Resume</string>
     <!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] -->
     <string name="controls_media_settings_button">Settings</string>
     <!-- Description for media control's playing media item, including information for the media's title, the artist, and source app [CHAR LIMIT=NONE]-->
diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml
index faf06f3..bcd49b9 100644
--- a/packages/SystemUI/res/values/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values/tiles_states_strings.xml
@@ -85,6 +85,16 @@
         <item>On</item>
     </string-array>
 
+    <!-- State names for dnd (Do not disturb) mode tile: unavailable, off, on.
+         This subtitle is shown when the tile is in that particular state but does not set its own
+         subtitle, so some of these may never appear on screen. They should still be translated as
+         if they could appear. [CHAR LIMIT=32] -->
+    <string-array name="tile_states_modes_dnd">
+        <item>Unavailable</item>
+        <item>Off</item>
+        <item>On</item>
+    </string-array>
+
     <!-- State names for flashlight tile: unavailable, off, on.
          This subtitle is shown when the tile is in that particular state but does not set its own
          subtitle, so some of these may never appear on screen. They should still be translated as
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt
index f276a82..c30580f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt
@@ -37,51 +37,43 @@
  * @param operand The [Evaluator.ConditionOperand] to apply to the conditions.
  */
 @OptIn(ExperimentalCoroutinesApi::class)
-class CombinedCondition
-constructor(
+class CombinedCondition(
     private val scope: CoroutineScope,
     private val conditions: Collection<Condition>,
     @Evaluator.ConditionOperand private val operand: Int,
 ) : Condition(scope, null, false) {
 
-    private var job: Job? = null
     private val _startStrategy by lazy { calculateStartStrategy() }
 
-    override fun start() {
-        job =
-            scope.launch {
-                val groupedConditions = conditions.groupBy { it.isOverridingCondition }
+    override suspend fun start() {
+        val groupedConditions = conditions.groupBy { it.isOverridingCondition }
 
-                lazilyEvaluate(
-                        conditions = groupedConditions.getOrDefault(true, emptyList()),
-                        filterUnknown = true,
+        lazilyEvaluate(
+                conditions = groupedConditions.getOrDefault(true, emptyList()),
+                filterUnknown = true,
+            )
+            .distinctUntilChanged()
+            .flatMapLatest { overriddenValue ->
+                // If there are overriding conditions with values set, they take precedence.
+                if (overriddenValue == null) {
+                    lazilyEvaluate(
+                        conditions = groupedConditions.getOrDefault(false, emptyList()),
+                        filterUnknown = false,
                     )
-                    .distinctUntilChanged()
-                    .flatMapLatest { overriddenValue ->
-                        // If there are overriding conditions with values set, they take precedence.
-                        if (overriddenValue == null) {
-                            lazilyEvaluate(
-                                conditions = groupedConditions.getOrDefault(false, emptyList()),
-                                filterUnknown = false,
-                            )
-                        } else {
-                            flowOf(overriddenValue)
-                        }
-                    }
-                    .collect { conditionMet ->
-                        if (conditionMet == null) {
-                            clearCondition()
-                        } else {
-                            updateCondition(conditionMet)
-                        }
-                    }
+                } else {
+                    flowOf(overriddenValue)
+                }
+            }
+            .collect { conditionMet ->
+                if (conditionMet == null) {
+                    clearCondition()
+                } else {
+                    updateCondition(conditionMet)
+                }
             }
     }
 
-    override fun stop() {
-        job?.cancel()
-        job = null
-    }
+    override fun stop() {}
 
     /**
      * Evaluates a list of conditions lazily with support for short-circuiting. Conditions are
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt
index 69236b4..44803bd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.kt
@@ -22,6 +22,8 @@
 import androidx.lifecycle.LifecycleOwner
 import java.lang.ref.WeakReference
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
 
 /**
  * Base class for a condition that needs to be fulfilled in order for [Monitor] to inform its
@@ -43,11 +45,12 @@
 ) {
     private val mTag: String = javaClass.simpleName
 
-    private val _callbacks = mutableListOf<WeakReference<Callback>>()
-    private var _started = false
+    private val callbacks = mutableListOf<WeakReference<Callback>>()
+    private var started = false
+    private var currentJob: Job? = null
 
     /** Starts monitoring the condition. */
-    protected abstract fun start()
+    protected abstract suspend fun start()
 
     /** Stops monitoring the condition. */
     protected abstract fun stop()
@@ -64,21 +67,21 @@
      */
     fun addCallback(callback: Callback) {
         if (shouldLog()) Log.d(mTag, "adding callback")
-        _callbacks.add(WeakReference(callback))
+        callbacks.add(WeakReference(callback))
 
-        if (_started) {
+        if (started) {
             callback.onConditionChanged(this)
             return
         }
 
-        start()
-        _started = true
+        currentJob = _scope.launch { start() }
+        started = true
     }
 
     /** Removes the provided callback from further receiving updates. */
     fun removeCallback(callback: Callback) {
         if (shouldLog()) Log.d(mTag, "removing callback")
-        val iterator = _callbacks.iterator()
+        val iterator = callbacks.iterator()
         while (iterator.hasNext()) {
             val cb = iterator.next().get()
             if (cb == null || cb === callback) {
@@ -86,12 +89,15 @@
             }
         }
 
-        if (_callbacks.isNotEmpty() || !_started) {
+        if (callbacks.isNotEmpty() || !started) {
             return
         }
 
         stop()
-        _started = false
+        currentJob?.cancel()
+        currentJob = null
+
+        started = false
     }
 
     /**
@@ -151,7 +157,7 @@
     }
 
     private fun sendUpdate() {
-        val iterator = _callbacks.iterator()
+        val iterator = callbacks.iterator()
         while (iterator.hasNext()) {
             val cb = iterator.next().get()
             if (cb == null) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt
index 0f535cd..849b3d6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt
@@ -19,7 +19,7 @@
     return object : Condition(scope, initialValue, false) {
         var job: Job? = null
 
-        override fun start() {
+        override suspend fun start() {
             job = scope.launch { collect { updateCondition(it) } }
         }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 1549b69..f2f1773 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -21,7 +21,6 @@
 import android.content.Intent
 import android.content.IntentFilter
 import android.content.res.Resources
-import android.graphics.RectF
 import android.os.Trace
 import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
 import android.provider.Settings.Global.ZEN_MODE_OFF
@@ -60,6 +59,7 @@
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockTickRate
+import com.android.systemui.plugins.clocks.VRectF
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
 import com.android.systemui.plugins.clocks.ZenData.ZenMode
@@ -250,7 +250,7 @@
     private var largeClockOnSecondaryDisplay = false
 
     val dozeAmount = MutableStateFlow(0f)
-    val onClockBoundsChanged = MutableStateFlow<RectF?>(null)
+    val onClockBoundsChanged = MutableStateFlow<VRectF>(VRectF.ZERO)
 
     private fun isDarkTheme(): Boolean {
         val isLightTheme = TypedValue()
@@ -315,7 +315,7 @@
 
     private val clockListener =
         object : ClockEventListener {
-            override fun onBoundsChanged(bounds: RectF) {
+            override fun onBoundsChanged(bounds: VRectF) {
                 onClockBoundsChanged.value = bounds
             }
         }
@@ -572,10 +572,6 @@
     }
 
     fun handleFidgetTap(x: Float, y: Float) {
-        if (!com.android.systemui.Flags.clockFidgetAnimation()) {
-            return
-        }
-
         clock?.run {
             smallClock.animations.onFidgetTap(x, y)
             largeClock.animations.onFidgetTap(x, y)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index ec97b8a..b872610 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
+import android.hardware.input.InputManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -219,6 +220,7 @@
         private final KeyguardKeyboardInteractor mKeyguardKeyboardInteractor;
         private final BouncerHapticPlayer mBouncerHapticPlayer;
         private final UserActivityNotifier mUserActivityNotifier;
+        private final InputManager mInputManager;
 
         @Inject
         public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -235,7 +237,8 @@
                 UiEventLogger uiEventLogger,
                 KeyguardKeyboardInteractor keyguardKeyboardInteractor,
                 BouncerHapticPlayer bouncerHapticPlayer,
-                UserActivityNotifier userActivityNotifier) {
+                UserActivityNotifier userActivityNotifier,
+                InputManager inputManager) {
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
             mLockPatternUtils = lockPatternUtils;
             mLatencyTracker = latencyTracker;
@@ -254,6 +257,7 @@
             mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
             mBouncerHapticPlayer = bouncerHapticPlayer;
             mUserActivityNotifier = userActivityNotifier;
+            mInputManager = inputManager;
         }
 
         /** Create a new {@link KeyguardInputViewController}. */
@@ -285,22 +289,23 @@
                         emergencyButtonController, mFalsingCollector,
                         mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
                         mUiEventLogger, mKeyguardKeyboardInteractor, mBouncerHapticPlayer,
-                        mUserActivityNotifier);
+                        mUserActivityNotifier, mInputManager);
             } else if (keyguardInputView instanceof KeyguardSimPinView) {
                 return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mTelephonyManager, mFalsingCollector,
                         emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
-                        mKeyguardKeyboardInteractor, mBouncerHapticPlayer, mUserActivityNotifier);
+                        mKeyguardKeyboardInteractor, mBouncerHapticPlayer, mUserActivityNotifier,
+                        mInputManager);
             } else if (keyguardInputView instanceof KeyguardSimPukView) {
                 return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mTelephonyManager, mFalsingCollector,
                         emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
-                        mKeyguardKeyboardInteractor, mBouncerHapticPlayer, mUserActivityNotifier
-                );
+                        mKeyguardKeyboardInteractor, mBouncerHapticPlayer, mUserActivityNotifier,
+                        mInputManager);
             }
 
             throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 0e9d8fe..ec9aedf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -18,12 +18,14 @@
 
 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
 
+import static com.android.internal.widget.flags.Flags.hideLastCharWithPhysicalInput;
 import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.StateListDrawable;
+import android.hardware.input.InputManager;
 import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -43,11 +45,13 @@
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
 public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
-        extends KeyguardAbsKeyInputViewController<T> {
+        extends KeyguardAbsKeyInputViewController<T> implements InputManager.InputDeviceListener {
 
     private final FalsingCollector mFalsingCollector;
     private final KeyguardKeyboardInteractor mKeyguardKeyboardInteractor;
     protected PasswordTextView mPasswordEntry;
+    private Boolean mShowAnimations;
+    private InputManager mInputManager;
 
     private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -79,7 +83,8 @@
             SelectedUserInteractor selectedUserInteractor,
             KeyguardKeyboardInteractor keyguardKeyboardInteractor,
             BouncerHapticPlayer bouncerHapticPlayer,
-            UserActivityNotifier userActivityNotifier) {
+            UserActivityNotifier userActivityNotifier,
+            InputManager inputManager) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, falsingCollector,
                 emergencyButtonController, featureFlags, selectedUserInteractor,
@@ -87,6 +92,51 @@
         mFalsingCollector = falsingCollector;
         mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
         mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
+        mInputManager = inputManager;
+        mShowAnimations = null;
+    }
+
+    private void updateAnimations(Boolean showAnimations) {
+        if (!hideLastCharWithPhysicalInput()) return;
+
+        if (showAnimations == null) {
+            showAnimations = !mLockPatternUtils
+                .isPinEnhancedPrivacyEnabled(mSelectedUserInteractor.getSelectedUserId());
+        }
+        if (mShowAnimations != null && showAnimations.equals(mShowAnimations)) return;
+        mShowAnimations = showAnimations;
+
+        for (NumPadKey button : mView.getButtons()) {
+            button.setAnimationEnabled(mShowAnimations);
+        }
+        mPasswordEntry.setShowPassword(mShowAnimations);
+    }
+
+    @Override
+    public void onInputDeviceAdded(int deviceId) {
+        if (!hideLastCharWithPhysicalInput()) return;
+
+        // If we were showing animations before maybe the new device is a keyboard.
+        if (mShowAnimations) {
+            updateAnimations(null);
+        }
+    }
+
+    @Override
+    public void onInputDeviceRemoved(int deviceId) {
+        if (!hideLastCharWithPhysicalInput()) return;
+
+        // If we were hiding animations because of a keyboard the keyboard may have been unplugged.
+        if (!mShowAnimations) {
+            updateAnimations(null);
+        }
+    }
+
+    @Override
+    public void onInputDeviceChanged(int deviceId) {
+        if (!hideLastCharWithPhysicalInput()) return;
+
+        updateAnimations(null);
     }
 
     @Override
@@ -95,7 +145,13 @@
 
         boolean showAnimations = !mLockPatternUtils
                 .isPinEnhancedPrivacyEnabled(mSelectedUserInteractor.getSelectedUserId());
-        mPasswordEntry.setShowPassword(showAnimations);
+        if (hideLastCharWithPhysicalInput()) {
+            mInputManager.registerInputDeviceListener(this, null);
+            updateAnimations(showAnimations);
+        } else {
+            mPasswordEntry.setShowPassword(showAnimations);
+        }
+
         for (NumPadKey button : mView.getButtons()) {
             button.setOnTouchListener((v, event) -> {
                 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
@@ -103,7 +159,9 @@
                 }
                 return false;
             });
-            button.setAnimationEnabled(showAnimations);
+            if (!hideLastCharWithPhysicalInput()) {
+                button.setAnimationEnabled(showAnimations);
+            }
             button.setBouncerHapticHelper(mBouncerHapticPlayer);
         }
         mPasswordEntry.setOnKeyListener(mOnKeyListener);
@@ -191,6 +249,10 @@
     protected void onViewDetached() {
         super.onViewDetached();
 
+        if (hideLastCharWithPhysicalInput()) {
+            mInputManager.unregisterInputDeviceListener(this);
+        }
+
         for (NumPadKey button : mView.getButtons()) {
             button.setOnTouchListener(null);
             button.setBouncerHapticHelper(null);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 9ae4cc6..eefcab3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
 
+import android.hardware.input.InputManager;
 import android.view.View;
 
 import com.android.internal.logging.UiEvent;
@@ -63,11 +64,13 @@
             SelectedUserInteractor selectedUserInteractor, UiEventLogger uiEventLogger,
             KeyguardKeyboardInteractor keyguardKeyboardInteractor,
             BouncerHapticPlayer bouncerHapticPlayer,
-            UserActivityNotifier userActivityNotifier) {
+            UserActivityNotifier userActivityNotifier,
+            InputManager inputManager) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker,
                 emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
-                keyguardKeyboardInteractor, bouncerHapticPlayer, userActivityNotifier);
+                keyguardKeyboardInteractor, bouncerHapticPlayer, userActivityNotifier, inputManager
+        );
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mPostureController = postureController;
         mLockPatternUtils = lockPatternUtils;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 24f77d7..a5bb62c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -29,6 +29,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
+import android.hardware.input.InputManager;
 import android.telephony.PinResult;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -97,11 +98,13 @@
             SelectedUserInteractor selectedUserInteractor,
             KeyguardKeyboardInteractor keyguardKeyboardInteractor,
             BouncerHapticPlayer bouncerHapticPlayer,
-            UserActivityNotifier userActivityNotifier) {
+            UserActivityNotifier userActivityNotifier,
+            InputManager inputManager) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker,
                 emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
-                keyguardKeyboardInteractor, bouncerHapticPlayer, userActivityNotifier);
+                keyguardKeyboardInteractor, bouncerHapticPlayer, userActivityNotifier, inputManager
+        );
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index e17e8cc..adede3d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -25,6 +25,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
+import android.hardware.input.InputManager;
 import android.telephony.PinResult;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -95,11 +96,13 @@
             SelectedUserInteractor selectedUserInteractor,
             KeyguardKeyboardInteractor keyguardKeyboardInteractor,
             BouncerHapticPlayer bouncerHapticPlayer,
-            UserActivityNotifier userActivityNotifier) {
+            UserActivityNotifier userActivityNotifier,
+            InputManager inputManager) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker,
                 emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
-                keyguardKeyboardInteractor, bouncerHapticPlayer, userActivityNotifier);
+                keyguardKeyboardInteractor, bouncerHapticPlayer, userActivityNotifier, inputManager
+        );
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d84b034..60ec051 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -124,7 +124,6 @@
 import com.android.settingslib.WirelessUtils;
 import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.Dumpable;
 import com.android.systemui.Flags;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
@@ -301,7 +300,8 @@
     private final Provider<SceneInteractor> mSceneInteractor;
     private final Provider<AlternateBouncerInteractor> mAlternateBouncerInteractor;
     private final Provider<CommunalSceneInteractor> mCommunalSceneInteractor;
-    private final KeyguardServiceShowLockscreenInteractor mKeyguardServiceShowLockscreenInteractor;
+    private final Provider<KeyguardServiceShowLockscreenInteractor>
+            mKeyguardServiceShowLockscreenInteractor;
     private final AuthController mAuthController;
     private final UiEventLogger mUiEventLogger;
     private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
@@ -2219,7 +2219,8 @@
             Provider<JavaAdapter> javaAdapter,
             Provider<SceneInteractor> sceneInteractor,
             Provider<CommunalSceneInteractor> communalSceneInteractor,
-            KeyguardServiceShowLockscreenInteractor keyguardServiceShowLockscreenInteractor) {
+            Provider<KeyguardServiceShowLockscreenInteractor>
+                    keyguardServiceShowLockscreenInteractor) {
         mContext = context;
         mSubscriptionManager = subscriptionManager;
         mUserTracker = userTracker;
@@ -2553,7 +2554,7 @@
 
         if (KeyguardWmStateRefactor.isEnabled()) {
             mJavaAdapter.get().alwaysCollectFlow(
-                    mKeyguardServiceShowLockscreenInteractor.getShowNowEvents(),
+                    mKeyguardServiceShowLockscreenInteractor.get().getShowNowEvents(),
                     this::onKeyguardServiceShowLockscreenNowEvents
             );
         }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 4a8e4ed..f72087e 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -32,6 +32,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationModeController;
@@ -42,7 +43,6 @@
 import com.android.systemui.recents.LauncherProxyService;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
diff --git a/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt b/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt
index 5e29ba9..442d4ab 100644
--- a/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt
+++ b/packages/SystemUI/src/com/android/systemui/KairosActivatable.kt
@@ -19,23 +19,28 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.BuildSpec
 import com.android.systemui.kairos.Events
 import com.android.systemui.kairos.EventsLoop
 import com.android.systemui.kairos.ExperimentalKairosApi
 import com.android.systemui.kairos.Incremental
 import com.android.systemui.kairos.IncrementalLoop
 import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.kairos.RootKairosNetwork
 import com.android.systemui.kairos.State
 import com.android.systemui.kairos.StateLoop
+import com.android.systemui.kairos.TransactionScope
+import com.android.systemui.kairos.activateSpec
+import com.android.systemui.kairos.effect
 import com.android.systemui.kairos.launchKairosNetwork
 import com.android.systemui.kairos.launchScope
 import dagger.Binds
 import dagger.Module
-import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
 import dagger.multibindings.Multibinds
 import javax.inject.Inject
+import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
@@ -176,21 +181,40 @@
 @SysUISingleton
 @ExperimentalKairosApi
 class KairosCoreStartable
-@Inject
-constructor(
-    @Application private val appScope: CoroutineScope,
-    private val kairosNetwork: KairosNetwork,
+private constructor(
+    private val appScope: CoroutineScope,
     private val activatables: dagger.Lazy<Set<@JvmSuppressWildcards KairosActivatable>>,
-) : CoreStartable {
+    private val unwrappedNetwork: RootKairosNetwork,
+) : CoreStartable, KairosNetwork by unwrappedNetwork {
+
+    @Inject
+    constructor(
+        @Application appScope: CoroutineScope,
+        activatables: dagger.Lazy<Set<@JvmSuppressWildcards KairosActivatable>>,
+    ) : this(appScope, activatables, appScope.launchKairosNetwork())
+
+    private val started = CompletableDeferred<Unit>()
+
     override fun start() {
         appScope.launch {
-            kairosNetwork.activateSpec {
+            unwrappedNetwork.activateSpec {
                 for (activatable in activatables.get()) {
                     launchScope { activatable.run { activate() } }
                 }
+                effect { started.complete(Unit) }
             }
         }
     }
+
+    override suspend fun activateSpec(spec: BuildSpec<*>) {
+        started.await()
+        unwrappedNetwork.activateSpec(spec)
+    }
+
+    override suspend fun <R> transact(block: TransactionScope.() -> R): R {
+        started.await()
+        return unwrappedNetwork.transact(block)
+    }
 }
 
 @Module
@@ -203,10 +227,5 @@
 
     @Multibinds fun kairosActivatables(): Set<@JvmSuppressWildcards KairosActivatable>
 
-    companion object {
-        @Provides
-        @SysUISingleton
-        fun provideKairosNetwork(@Application scope: CoroutineScope): KairosNetwork =
-            scope.launchKairosNetwork()
-    }
+    @Binds fun bindKairosNetwork(impl: KairosCoreStartable): KairosNetwork
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index c78f75a..0894667 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -894,8 +894,8 @@
                 if (NotificationBundleUi.isEnabled()) {
                     return enr.getEntryAdapter().canDragAndDrop();
                 } else {
-                    boolean canBubble = enr.getEntry().canBubble();
-                    Notification notif = enr.getEntry().getSbn().getNotification();
+                    boolean canBubble = enr.getEntryLegacy().canBubble();
+                    Notification notif = enr.getEntryLegacy().getSbn().getNotification();
                     PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
                             : notif.fullScreenIntent;
                     if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java
index 2d1cd03..20290f7 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java
@@ -99,10 +99,7 @@
 
     private boolean isExclusivelyManagedBluetoothDevice(
             @NonNull CachedBluetoothDevice cachedDevice) {
-        if (com.android.settingslib.flags.Flags.enableHideExclusivelyManagedBluetoothDevice()) {
-            return BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
-                    cachedDevice.getDevice());
-        }
-        return false;
+        return BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
+                cachedDevice.getDevice());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 08559f2..14b13d1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -63,7 +63,7 @@
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.bluetooth.qsdialog.ActiveHearingDeviceItemFactory;
 import com.android.systemui.bluetooth.qsdialog.AvailableHearingDeviceItemFactory;
-import com.android.systemui.bluetooth.qsdialog.ConnectedDeviceItemFactory;
+import com.android.systemui.bluetooth.qsdialog.ConnectedHearingDeviceItemFactory;
 import com.android.systemui.bluetooth.qsdialog.DeviceItem;
 import com.android.systemui.bluetooth.qsdialog.DeviceItemFactory;
 import com.android.systemui.bluetooth.qsdialog.DeviceItemType;
@@ -71,6 +71,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.shared.QSSettingsPackageRepository;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
@@ -111,6 +112,7 @@
     private final HearingDevicesUiEventLogger mUiEventLogger;
     private final boolean mShowPairNewDevice;
     private final int mLaunchSourceId;
+    private final QSSettingsPackageRepository mQSSettingsPackageRepository;
 
     private SystemUIDialog mDialog;
     private HearingDevicesListAdapter mDeviceListAdapter;
@@ -119,6 +121,13 @@
     private Spinner mPresetSpinner;
     private HearingDevicesPresetsController mPresetController;
     private HearingDevicesSpinnerAdapter mPresetInfoAdapter;
+
+    private View mInputRoutingLayout;
+    private Spinner mInputRoutingSpinner;
+    private HearingDevicesInputRoutingController.Factory mInputRoutingControllerFactory;
+    private HearingDevicesInputRoutingController mInputRoutingController;
+    private HearingDevicesSpinnerAdapter mInputRoutingAdapter;
+
     private final HearingDevicesPresetsController.PresetCallback mPresetCallback =
             new HearingDevicesPresetsController.PresetCallback() {
                 @Override
@@ -142,12 +151,7 @@
     private final List<DeviceItemFactory> mHearingDeviceItemFactoryList = List.of(
             new ActiveHearingDeviceItemFactory(),
             new AvailableHearingDeviceItemFactory(),
-            // TODO(b/331305850): setHearingAidInfo() for connected but not connect to profile
-            // hearing device only called from
-            // settings/bluetooth/DeviceListPreferenceFragment#handleLeScanResult, so we don't know
-            // it is connected but not yet connect to profile hearing device in systemui.
-            // Show all connected but not connect to profile bluetooth device for now.
-            new ConnectedDeviceItemFactory(),
+            new ConnectedHearingDeviceItemFactory(),
             new SavedHearingDeviceItemFactory()
     );
 
@@ -171,7 +175,9 @@
             @Main Executor mainExecutor,
             @Background Executor bgExecutor,
             AudioManager audioManager,
-            HearingDevicesUiEventLogger uiEventLogger) {
+            HearingDevicesUiEventLogger uiEventLogger,
+            QSSettingsPackageRepository qsSettingsPackageRepository,
+            HearingDevicesInputRoutingController.Factory inputRoutingControllerFactory) {
         mShowPairNewDevice = showPairNewDevice;
         mSystemUIDialogFactory = systemUIDialogFactory;
         mActivityStarter = activityStarter;
@@ -183,6 +189,8 @@
         mProfileManager = localBluetoothManager.getProfileManager();
         mUiEventLogger = uiEventLogger;
         mLaunchSourceId = launchSourceId;
+        mQSSettingsPackageRepository = qsSettingsPackageRepository;
+        mInputRoutingControllerFactory = inputRoutingControllerFactory;
     }
 
     @Override
@@ -198,11 +206,11 @@
     public void onDeviceItemGearClicked(@NonNull DeviceItem deviceItem, @NonNull View view) {
         mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK, mLaunchSourceId);
         dismissDialogIfExists();
-        Intent intent = new Intent(ACTION_BLUETOOTH_DEVICE_DETAILS);
         Bundle bundle = new Bundle();
         bundle.putString(KEY_BLUETOOTH_ADDRESS, deviceItem.getCachedBluetoothDevice().getAddress());
-        intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        Intent intent = new Intent(ACTION_BLUETOOTH_DEVICE_DETAILS)
+                .setPackage(mQSSettingsPackageRepository.getSettingsPackageName())
+                .putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
         mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
                 mDialogTransitionAnimator.createActivityTransitionController(view));
     }
@@ -240,6 +248,12 @@
                 mPresetLayout.setVisibility(
                         mPresetController.isPresetControlAvailable() ? VISIBLE : GONE);
             }
+            if (mInputRoutingController != null) {
+                mInputRoutingController.setDevice(device);
+                mInputRoutingController.isInputRoutingControlAvailable(
+                        available -> mMainExecutor.execute(() -> mInputRoutingLayout.setVisibility(
+                                available ? VISIBLE : GONE)));
+            }
             if (mAmbientController != null) {
                 mAmbientController.loadDevice(device);
             }
@@ -311,6 +325,11 @@
                 setupDeviceListView(dialog, hearingDeviceItemList);
                 setupPairNewDeviceButton(dialog);
                 setupPresetSpinner(dialog, activeHearingDevice);
+                if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()
+                        && com.android.systemui.Flags
+                        .hearingDevicesInputRoutingUiImprovement()) {
+                    setupInputRoutingSpinner(dialog, activeHearingDevice);
+                }
                 if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) {
                     setupAmbientControls(activeHearingDevice);
                 }
@@ -384,6 +403,52 @@
         mBgExecutor.execute(() -> mPresetController.registerHapCallback());
     }
 
+    private void setupInputRoutingSpinner(SystemUIDialog dialog,
+            CachedBluetoothDevice activeHearingDevice) {
+        mInputRoutingController = mInputRoutingControllerFactory.create(dialog.getContext());
+        mInputRoutingController.setDevice(activeHearingDevice);
+
+        mInputRoutingSpinner = dialog.requireViewById(R.id.input_routing_spinner);
+        mInputRoutingAdapter = new HearingDevicesSpinnerAdapter(dialog.getContext());
+        mInputRoutingAdapter.addAll(
+                HearingDevicesInputRoutingController.getInputRoutingOptions(dialog.getContext()));
+        mInputRoutingSpinner.setAdapter(mInputRoutingAdapter);
+        // Disable redundant Touch & Hold accessibility action for Switch Access
+        mInputRoutingSpinner.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+                    @NonNull AccessibilityNodeInfo info) {
+                info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+                super.onInitializeAccessibilityNodeInfo(host, info);
+            }
+        });
+        // Should call setSelection(index, false) for the spinner before setOnItemSelectedListener()
+        // to avoid extra onItemSelected() get called when first register the listener.
+        final int initialPosition =
+                mInputRoutingController.getUserPreferredInputRoutingValue();
+        mInputRoutingSpinner.setSelection(initialPosition, false);
+        mInputRoutingAdapter.setSelected(initialPosition);
+        mInputRoutingSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                mInputRoutingAdapter.setSelected(position);
+                mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_INPUT_ROUTING_SELECT,
+                        mLaunchSourceId);
+                mInputRoutingController.selectInputRouting(position);
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+                // Do nothing
+            }
+        });
+
+        mInputRoutingLayout = dialog.requireViewById(R.id.input_routing_layout);
+        mInputRoutingController.isInputRoutingControlAvailable(
+                available -> mMainExecutor.execute(() -> mInputRoutingLayout.setVisibility(
+                        available ? VISIBLE : GONE)));
+    }
+
     private void setupAmbientControls(CachedBluetoothDevice activeHearingDevice) {
         final AmbientVolumeLayout ambientLayout = mDialog.requireViewById(R.id.ambient_layout);
         ambientLayout.setUiEventLogger(mUiEventLogger, mLaunchSourceId);
@@ -401,8 +466,8 @@
             pairButton.setOnClickListener(v -> {
                 mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR, mLaunchSourceId);
                 dismissDialogIfExists();
-                final Intent intent = new Intent(Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS);
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                final Intent intent = new Intent(Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS)
+                        .setPackage(mQSSettingsPackageRepository.getSettingsPackageName());
                 mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
                         mDialogTransitionAnimator.createActivityTransitionController(dialog));
             });
@@ -523,8 +588,9 @@
                     com.android.internal.R.color.materialColorOnPrimaryContainer));
         }
         text.setText(item.getToolName());
-        Intent intent = item.getToolIntent();
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        Intent intent = item.getToolIntent()
+                .setPackage(mQSSettingsPackageRepository.getSettingsPackageName());
+
         view.setOnClickListener(v -> {
             final String name = intent.getComponent() != null
                     ? intent.getComponent().flattenToString()
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt
new file mode 100644
index 0000000..e8fa7ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesInputRoutingController.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility.hearingaid
+
+import android.content.Context
+import android.media.AudioManager
+import android.util.Log
+import androidx.collection.ArraySet
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.HapClientProfile
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants
+import com.android.settingslib.bluetooth.HearingAidAudioRoutingHelper
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.res.R
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * The controller of the hearing device input routing.
+ *
+ * <p> It manages and update the input routing according to the value.
+ */
+open class HearingDevicesInputRoutingController
+@AssistedInject
+constructor(
+    @Assisted context: Context,
+    private val audioManager: AudioManager,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+    private val audioRoutingHelper = HearingAidAudioRoutingHelper(context)
+    private var cachedDevice: CachedBluetoothDevice? = null
+    private val bgCoroutineScope = CoroutineScope(backgroundDispatcher)
+
+    /** Factory to create a [HearingDevicesInputRoutingController] instance. */
+    @AssistedFactory
+    interface Factory {
+        fun create(context: Context): HearingDevicesInputRoutingController
+    }
+
+    /** Possible input routing UI. Need to align with [getInputRoutingOptions] */
+    enum class InputRoutingValue {
+        HEARING_DEVICE,
+        BUILTIN_MIC,
+    }
+
+    companion object {
+        private const val TAG = "HearingDevicesInputRoutingController"
+
+        /** Gets input routing options as strings. */
+        @JvmStatic
+        fun getInputRoutingOptions(context: Context): Array<String> {
+            return context.resources.getStringArray(R.array.hearing_device_input_routing_options)
+        }
+    }
+
+    fun interface InputRoutingControlAvailableCallback {
+        fun onResult(available: Boolean)
+    }
+
+    /**
+     * Sets the device for this controller to control the input routing.
+     *
+     * @param device the [CachedBluetoothDevice] set to the controller
+     */
+    fun setDevice(device: CachedBluetoothDevice?) {
+        this@HearingDevicesInputRoutingController.cachedDevice = device
+    }
+
+    fun isInputRoutingControlAvailable(callback: InputRoutingControlAvailableCallback) {
+        bgCoroutineScope.launch {
+            val result = isInputRoutingControlAvailableInternal()
+            callback.onResult(result)
+        }
+    }
+
+    /**
+     * Checks if input routing control is available for the currently set device.
+     *
+     * @return `true` if input routing control is available.
+     */
+    private suspend fun isInputRoutingControlAvailableInternal(): Boolean {
+        val device = cachedDevice ?: return false
+
+        val memberDevices = device.memberDevice
+
+        val inputInfos =
+            withContext(backgroundDispatcher) {
+                audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)
+            }
+        val supportedInputDeviceAddresses = ArraySet<String>()
+        supportedInputDeviceAddresses.add(device.address)
+        if (memberDevices.isNotEmpty()) {
+            memberDevices.forEach { supportedInputDeviceAddresses.add(it.address) }
+        }
+
+        val isValidInputDevice =
+            inputInfos.any { supportedInputDeviceAddresses.contains(it.address) }
+        // Not support ASHA hearing device for input routing feature
+        val isHapHearingDevice = device.profiles.any { profile -> profile is HapClientProfile }
+
+        if (isHapHearingDevice && !isValidInputDevice) {
+            Log.d(TAG, "Not supported input type hearing device.")
+        }
+        return isHapHearingDevice && isValidInputDevice
+    }
+
+    /** Gets the user's preferred [InputRoutingValue]. */
+    fun getUserPreferredInputRoutingValue(): Int {
+        val device = cachedDevice ?: return InputRoutingValue.HEARING_DEVICE.ordinal
+
+        return if (device.device.isMicrophonePreferredForCalls) {
+            InputRoutingValue.HEARING_DEVICE.ordinal
+        } else {
+            InputRoutingValue.BUILTIN_MIC.ordinal
+        }
+    }
+
+    /**
+     * Sets the input routing to [android.bluetooth.BluetoothDevice.setMicrophonePreferredForCalls]
+     * based on the input routing index.
+     *
+     * @param inputRoutingIndex The desired input routing index.
+     */
+    fun selectInputRouting(inputRoutingIndex: Int) {
+        val device = cachedDevice ?: return
+
+        val useBuiltinMic = (inputRoutingIndex == InputRoutingValue.BUILTIN_MIC.ordinal)
+        val status =
+            audioRoutingHelper.setPreferredInputDeviceForCalls(
+                device,
+                if (useBuiltinMic) HearingAidAudioRoutingConstants.RoutingValue.BUILTIN_DEVICE
+                else HearingAidAudioRoutingConstants.RoutingValue.AUTO,
+            )
+        if (!status) {
+            Log.d(TAG, "Fail to configure setPreferredInputDeviceForCalls")
+        }
+        setMicrophonePreferredForCallsForDeviceSet(device, !useBuiltinMic)
+    }
+
+    private fun setMicrophonePreferredForCallsForDeviceSet(
+        device: CachedBluetoothDevice?,
+        enabled: Boolean,
+    ) {
+        device ?: return
+        device.device.isMicrophonePreferredForCalls = enabled
+        val memberDevices = device.memberDevice
+        if (memberDevices.isNotEmpty()) {
+            memberDevices.forEach { d -> d.device.isMicrophonePreferredForCalls = enabled }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
index 4a695d6..82ac10d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
@@ -40,6 +40,8 @@
     HEARING_DEVICES_AMBIENT_EXPAND_CONTROLS(2153),
     @UiEvent(doc = "Collapse the ambient volume controls")
     HEARING_DEVICES_AMBIENT_COLLAPSE_CONTROLS(2154),
+    @UiEvent(doc = "Select a input routing option from input routing spinner")
+    HEARING_DEVICES_INPUT_ROUTING_SELECT(2155),
     @UiEvent(doc = "Click on the device settings to enter hearing devices page")
     HEARING_DEVICES_SETTINGS_CLICK(2172);
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index fb47d42..e25d54b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -29,40 +29,40 @@
 import com.android.systemui.qs.tiles.NightDisplayTile
 import com.android.systemui.qs.tiles.OneHandedModeTile
 import com.android.systemui.qs.tiles.ReduceBrightColorsTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.impl.colorcorrection.domain.ColorCorrectionTileMapper
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionUserActionInteractor
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
-import com.android.systemui.qs.tiles.impl.fontscaling.domain.FontScalingTileMapper
+import com.android.systemui.qs.tiles.impl.colorcorrection.ui.mapper.ColorCorrectionTileMapper
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileDataInteractor
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
-import com.android.systemui.qs.tiles.impl.hearingdevices.domain.HearingDevicesTileMapper
+import com.android.systemui.qs.tiles.impl.fontscaling.ui.mapper.FontScalingTileMapper
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileDataInteractor
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
-import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper
+import com.android.systemui.qs.tiles.impl.hearingdevices.ui.mapper.HearingDevicesTileMapper
 import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor
 import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor
 import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
+import com.android.systemui.qs.tiles.impl.inversion.ui.mapper.ColorInversionTileMapper
 import com.android.systemui.qs.tiles.impl.night.domain.interactor.NightDisplayTileDataInteractor
 import com.android.systemui.qs.tiles.impl.night.domain.interactor.NightDisplayTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
-import com.android.systemui.qs.tiles.impl.night.ui.NightDisplayTileMapper
+import com.android.systemui.qs.tiles.impl.night.ui.mapper.NightDisplayTileMapper
 import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileDataInteractor
 import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
-import com.android.systemui.qs.tiles.impl.onehanded.ui.OneHandedModeTileMapper
+import com.android.systemui.qs.tiles.impl.onehanded.ui.mapper.OneHandedModeTileMapper
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor.ReduceBrightColorsTileDataInteractor
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor.ReduceBrightColorsTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
-import com.android.systemui.qs.tiles.impl.reducebrightness.ui.ReduceBrightColorsTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.impl.reducebrightness.ui.mapper.ReduceBrightColorsTileMapper
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
index 831bc1d..26d5562 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
@@ -5,15 +5,15 @@
 import com.android.systemui.qs.shared.model.TileCategory
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.BatterySaverTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileDataInteractor
 import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
-import com.android.systemui.qs.tiles.impl.battery.ui.BatterySaverTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.impl.battery.ui.mapper.BatterySaverTileMapper
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
@@ -63,7 +63,7 @@
             factory: QSTileViewModelFactory.Static<BatterySaverTileModel>,
             mapper: BatterySaverTileMapper,
             stateInteractor: BatterySaverTileDataInteractor,
-            userActionInteractor: BatterySaverTileUserActionInteractor
+            userActionInteractor: BatterySaverTileUserActionInteractor,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(BATTERY_SAVER_TILE_SPEC),
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
index 39f5580..c4e1ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.biometrics.shared.model.toSensorStrength
 import com.android.systemui.biometrics.shared.model.toSensorType
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
index 230b30b..cce33fd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.shared.model.PromptKind
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
index 40313e3..6484116 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.biometrics.data.repository.DisplayStateRepository
 import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.display.data.repository.DisplayRepository
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index 30b98a6..ceb2b10 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -99,11 +99,6 @@
                                     show()
                                 } else if (showIndicatorForDeviceEntry) {
                                     show()
-                                    overlayView?.announceForAccessibility(
-                                        applicationContext.resources.getString(
-                                            R.string.accessibility_side_fingerprint_indicator_label
-                                        )
-                                    )
                                 } else {
                                     hide()
                                 }
@@ -182,6 +177,11 @@
 
                 overlayShowAnimator.start()
 
+                /**
+                 * Intercepts TYPE_WINDOW_STATE_CHANGED accessibility event, preventing Talkback
+                 * from speaking @string/accessibility_fingerprint_label twice when sensor location
+                 * indicator is in focus
+                 */
                 it.setAccessibilityDelegate(
                     object : View.AccessibilityDelegate() {
                         override fun dispatchPopulateAccessibilityEvent(
@@ -190,8 +190,7 @@
                         ): Boolean {
                             return if (
                                 event.getEventType() ===
-                                    android.view.accessibility.AccessibilityEvent
-                                        .TYPE_WINDOW_STATE_CHANGED
+                                    AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
                             ) {
                                 true
                             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
index 7f1cb5d..dea3c47 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
@@ -21,7 +21,7 @@
 import com.android.settingslib.bluetooth.BluetoothCallback
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt
index 5a5a51e..8d066bb 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt
@@ -419,6 +419,8 @@
                 }
                 nameView.text = item.deviceName
                 summaryView.text = item.connectionSummary
+                // needed for marquee
+                summaryView.isSelected = true
 
                 actionIconView.setOnClickListener {
                     mutableDeviceItemClick.value =
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsViewModel.kt
index 5863a93..7d8752e 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsViewModel.kt
@@ -21,18 +21,14 @@
 class BluetoothDetailsViewModel(
     private val onSettingsClick: () -> Unit,
     val detailsContentViewModel: BluetoothDetailsContentViewModel,
-) : TileDetailsViewModel() {
+) : TileDetailsViewModel {
     override fun clickOnSettingsButton() {
         onSettingsClick()
     }
 
-    override fun getTitle(): String {
-        // TODO: b/378513956 Update the placeholder text
-        return "Bluetooth"
-    }
+    // TODO: b/378513956 Update the placeholder text
+    override val title = "Bluetooth"
 
-    override fun getSubTitle(): String {
-        // TODO: b/378513956 Update the placeholder text
-        return "Tap to connect or disconnect a device"
-    }
+    // TODO: b/378513956 Update the placeholder text
+    override val subTitle = "Tap to connect or disconnect a device"
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
index 55d4d3e..9e0f1027 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractor.kt
@@ -22,7 +22,7 @@
 import com.android.settingslib.bluetooth.BluetoothCallback
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
index bfbc27d..208e498 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
@@ -21,7 +21,6 @@
 import com.android.settingslib.bluetooth.BluetoothUtils
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.LocalBluetoothManager
-import com.android.settingslib.flags.Flags
 import com.android.systemui.res.R
 
 private val backgroundOn = R.drawable.settingslib_switch_bar_bg_on
@@ -215,19 +214,15 @@
     }
 }
 
-internal class ConnectedDeviceItemFactory : DeviceItemFactory() {
+internal open class ConnectedDeviceItemFactory : DeviceItemFactory() {
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
         isOngoingCall: Boolean,
         audioSharingAvailable: Boolean,
     ): Boolean {
-        return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
-            !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) &&
-                BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, isOngoingCall)
-        } else {
+        return !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) &&
             BluetoothUtils.isConnectedBluetoothDevice(cachedDevice, isOngoingCall)
-        }
     }
 
     override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
@@ -243,6 +238,19 @@
     }
 }
 
+internal class ConnectedHearingDeviceItemFactory : ConnectedDeviceItemFactory() {
+    override fun isFilterMatched(
+        context: Context,
+        cachedDevice: CachedBluetoothDevice,
+        isOngoingCall: Boolean,
+        audioSharingAvailable: Boolean,
+    ): Boolean {
+        return cachedDevice.isHearingDevice &&
+            cachedDevice.bondState == BluetoothDevice.BOND_BONDED &&
+            cachedDevice.device.isConnected
+    }
+}
+
 internal open class SavedDeviceItemFactory : DeviceItemFactory() {
     override fun isFilterMatched(
         context: Context,
@@ -250,13 +258,9 @@
         isOngoingCall: Boolean,
         audioSharingAvailable: Boolean,
     ): Boolean {
-        return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
-            !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) &&
-                cachedDevice.bondState == BluetoothDevice.BOND_BONDED &&
-                !cachedDevice.isConnected
-        } else {
-            cachedDevice.bondState == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected
-        }
+        return !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) &&
+            cachedDevice.bondState == BluetoothDevice.BOND_BONDED &&
+            !cachedDevice.isConnected
     }
 
     override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
@@ -279,18 +283,12 @@
         isOngoingCall: Boolean,
         audioSharingAvailable: Boolean,
     ): Boolean {
-        return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) {
-            !BluetoothUtils.isExclusivelyManagedBluetoothDevice(
-                context,
-                cachedDevice.getDevice(),
-            ) &&
-                cachedDevice.isHearingAidDevice &&
-                cachedDevice.bondState == BluetoothDevice.BOND_BONDED &&
-                !cachedDevice.isConnected
-        } else {
-            cachedDevice.isHearingAidDevice &&
-                cachedDevice.bondState == BluetoothDevice.BOND_BONDED &&
-                !cachedDevice.isConnected
-        }
+        return !BluetoothUtils.isExclusivelyManagedBluetoothDevice(
+            context,
+            cachedDevice.getDevice(),
+        ) &&
+            cachedDevice.isHearingDevice &&
+            cachedDevice.bondState == BluetoothDevice.BOND_BONDED &&
+            !cachedDevice.isConnected
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
index b606c19..e458b80 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
@@ -24,7 +24,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt
index b9e1c55..8920836 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/SimBouncerRepository.kt
@@ -28,7 +28,7 @@
 import com.android.systemui.bouncer.data.model.SimBouncerModel
 import com.android.systemui.bouncer.data.model.SimPukInputModel
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt
index e6d6293..636b3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.brightness.shared.model.LinearBrightness
 import com.android.systemui.brightness.shared.model.formatBrightness
 import com.android.systemui.brightness.shared.model.logDiffForTable
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
index 52204b8..6aeb35b 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.view.MotionEvent
 import androidx.annotation.VisibleForTesting
+import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationVector1D
 import androidx.compose.animation.core.VectorConverter
@@ -40,6 +41,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
@@ -323,7 +325,7 @@
 fun BrightnessSliderContainer(
     viewModel: BrightnessSliderViewModel,
     modifier: Modifier = Modifier,
-    containerColor: Color = colorResource(R.color.shade_scrim_background_dark),
+    containerColors: ContainerColors,
 ) {
     val gamma = viewModel.currentBrightness.value
     if (gamma == BrightnessSliderViewModel.initialValue.value) { // Ignore initial negative value.
@@ -344,6 +346,16 @@
 
     DisposableEffect(Unit) { onDispose { viewModel.setIsDragging(false) } }
 
+    var dragging by remember { mutableStateOf(false) }
+
+    // Use dragging instead of viewModel.showMirror so the color starts changing as soon as the
+    // dragging state changes. If not, we may be waiting for the background to finish fading in
+    // when stopping dragging
+    val containerColor by
+        animateColorAsState(
+            if (dragging) containerColors.mirrorColor else containerColors.idleColor
+        )
+
     Box(
         modifier =
             modifier
@@ -360,10 +372,12 @@
             onRestrictedClick = viewModel::showPolicyRestrictionDialog,
             onDrag = {
                 viewModel.setIsDragging(true)
+                dragging = true
                 coroutineScope.launch { viewModel.onDrag(Drag.Dragging(GammaBrightness(it))) }
             },
             onStop = {
                 viewModel.setIsDragging(false)
+                dragging = false
                 coroutineScope.launch { viewModel.onDrag(Drag.Stopped(GammaBrightness(it))) }
             },
             modifier =
@@ -392,6 +406,15 @@
     }
 }
 
+data class ContainerColors(val idleColor: Color, val mirrorColor: Color) {
+    companion object {
+        fun singleColor(color: Color) = ContainerColors(color, color)
+
+        val defaultContainerColor: Color
+            @Composable @ReadOnlyComposable get() = colorResource(R.color.shade_panel_fallback)
+    }
+}
+
 private object Dimensions {
     val SliderBackgroundFrameSize = DpSize(10.dp, 6.dp)
     val SliderBackgroundRoundedCorner = 24.dp
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 183a3cc..724670d 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -32,7 +32,7 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.BroadcastRunning
 import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt b/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt
index 7816a14..dac5b7e 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/data/repository/CameraSensorPrivacyRepository.kt
@@ -19,7 +19,7 @@
 import android.hardware.SensorPrivacyManager
 import android.hardware.SensorPrivacyManager.Sensors.CAMERA
 import android.os.UserHandle
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
index df6c1b1..8cebe04 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
@@ -24,11 +24,17 @@
 import android.net.Uri
 import android.text.TextUtils
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.res.R
+import java.util.function.Consumer
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
 @SysUISingleton
-class ActionIntentCreator @Inject constructor() : IntentCreator {
+class ActionIntentCreator
+@Inject
+constructor(@Application private val applicationScope: CoroutineScope) : IntentCreator {
     override fun getTextEditorIntent(context: Context?) =
         Intent(context, EditTextActivity::class.java).apply {
             addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
@@ -65,7 +71,7 @@
             .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
     }
 
-    override fun getImageEditIntent(uri: Uri?, context: Context): Intent {
+    suspend fun getImageEditIntent(uri: Uri?, context: Context): Intent {
         val editorPackage = context.getString(R.string.config_screenshotEditor)
         return Intent(Intent.ACTION_EDIT).apply {
             if (!TextUtils.isEmpty(editorPackage)) {
@@ -78,6 +84,14 @@
         }
     }
 
+    override fun getImageEditIntentAsync(
+        uri: Uri?,
+        context: Context,
+        outputConsumer: Consumer<Intent>,
+    ) {
+        applicationScope.launch { outputConsumer.accept(getImageEditIntent(uri, context)) }
+    }
+
     override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent {
         val remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage)
         return Intent(REMOTE_COPY_ACTION).apply {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 314b6e7..984d247 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -558,8 +558,10 @@
 
     private void editImage(Uri uri) {
         mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
-        mContext.startActivity(mIntentCreator.getImageEditIntent(uri, mContext));
-        animateOut();
+        mIntentCreator.getImageEditIntentAsync(uri, mContext, intent -> {
+            mContext.startActivity(intent);
+            animateOut();
+        });
     }
 
     private void editText() {
@@ -747,8 +749,10 @@
                             mIntentCreator.getTextEditorIntent(mContext));
                     break;
                 case IMAGE:
-                    finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED,
-                            mIntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
+                    mIntentCreator.getImageEditIntentAsync(mClipboardModel.getUri(), mContext,
+                            intent -> {
+                                finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED, intent);
+                            });
                     break;
                 default:
                     Log.w(TAG, "Got preview tapped callback for non-editable type "
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
index 4b24536..e9a9cbf 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
@@ -27,6 +27,8 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.res.R;
 
+import java.util.function.Consumer;
+
 import javax.inject.Inject;
 
 @SysUISingleton
@@ -73,7 +75,7 @@
         return chooserIntent;
     }
 
-    public Intent getImageEditIntent(Uri uri, Context context) {
+    public void getImageEditIntentAsync(Uri uri, Context context, Consumer<Intent> outputConsumer) {
         String editorPackage = context.getString(R.string.config_screenshotEditor);
         Intent editIntent = new Intent(Intent.ACTION_EDIT);
         if (!TextUtils.isEmpty(editorPackage)) {
@@ -83,7 +85,7 @@
         editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
         editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD);
-        return editIntent;
+        outputConsumer.accept(editIntent);
     }
 
     public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
index c8a6b05..283596f 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -21,9 +21,11 @@
 import android.content.Intent;
 import android.net.Uri;
 
+import java.util.function.Consumer;
+
 public interface IntentCreator {
     Intent getTextEditorIntent(Context context);
     Intent getShareIntent(ClipData clipData, Context context);
-    Intent getImageEditIntent(Uri uri, Context context);
+    void getImageEditIntentAsync(Uri uri, Context context, Consumer<Intent> outputConsumer);
     Intent getRemoteCopyIntent(ClipData clipData, Context context);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
index 9db7b50..1301fb8 100644
--- a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.common.domain.interactor
 
 import android.util.Log
+import com.android.app.displaylib.PerDisplayRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.display.data.repository.PerDisplayRepository
 import com.android.systemui.model.StateChange
 import com.android.systemui.model.SysUiState
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/TouchHandlingView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/TouchHandlingView.kt
index 42f1b73..6c3535a 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/TouchHandlingView.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/TouchHandlingView.kt
@@ -27,17 +27,17 @@
 import android.view.accessibility.AccessibilityNodeInfo
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import com.android.systemui.Flags.doubleTapToSleep
 import com.android.systemui.log.TouchHandlingViewLogger
 import com.android.systemui.shade.TouchLogger
-import kotlin.math.pow
-import kotlin.math.sqrt
 import kotlinx.coroutines.DisposableHandle
 
 /**
- * View designed to handle long-presses.
+ * View designed to handle long-presses and double taps.
  *
- * The view will not handle any long pressed by default. To set it up, set up a listener and, when
- * ready to start consuming long-presses, set [setLongPressHandlingEnabled] to `true`.
+ * The view will not handle any gestures by default. To set it up, set up a listener and, when ready
+ * to start consuming gestures, set the gesture's enable function ([setLongPressHandlingEnabled],
+ * [setDoublePressHandlingEnabled]) to `true`.
  */
 class TouchHandlingView(
     context: Context,
@@ -62,6 +62,9 @@
 
         /** Notifies that the gesture was too short for a long press, it is actually a click. */
         fun onSingleTapDetected(view: View, x: Int, y: Int) = Unit
+
+        /** Notifies that a double tap has been detected by the given view. */
+        fun onDoubleTapDetected(view: View) = Unit
     }
 
     var listener: Listener? = null
@@ -70,6 +73,7 @@
 
     private val interactionHandler: TouchHandlingViewInteractionHandler by lazy {
         TouchHandlingViewInteractionHandler(
+            context = context,
             postDelayed = { block, timeoutMs ->
                 val dispatchToken = Any()
 
@@ -84,6 +88,9 @@
             onSingleTapDetected = { x, y ->
                 listener?.onSingleTapDetected(this@TouchHandlingView, x = x, y = y)
             },
+            onDoubleTapDetected = {
+                if (doubleTapToSleep()) listener?.onDoubleTapDetected(this@TouchHandlingView)
+            },
             longPressDuration = longPressDuration,
             allowedTouchSlop = allowedTouchSlop,
             logger = logger,
@@ -100,13 +107,17 @@
         interactionHandler.isLongPressHandlingEnabled = isEnabled
     }
 
+    fun setDoublePressHandlingEnabled(isEnabled: Boolean) {
+        interactionHandler.isDoubleTapHandlingEnabled = isEnabled
+    }
+
     override fun dispatchTouchEvent(event: MotionEvent): Boolean {
         return TouchLogger.logDispatchTouch("long_press", event, super.dispatchTouchEvent(event))
     }
 
     @SuppressLint("ClickableViewAccessibility")
-    override fun onTouchEvent(event: MotionEvent?): Boolean {
-        return interactionHandler.onTouchEvent(event?.toModel())
+    override fun onTouchEvent(event: MotionEvent): Boolean {
+        return interactionHandler.onTouchEvent(event)
     }
 
     private fun setupAccessibilityDelegate() {
@@ -154,33 +165,3 @@
             }
     }
 }
-
-private fun MotionEvent.toModel(): TouchHandlingViewInteractionHandler.MotionEventModel {
-    return when (actionMasked) {
-        MotionEvent.ACTION_DOWN ->
-            TouchHandlingViewInteractionHandler.MotionEventModel.Down(x = x.toInt(), y = y.toInt())
-        MotionEvent.ACTION_MOVE ->
-            TouchHandlingViewInteractionHandler.MotionEventModel.Move(
-                distanceMoved = distanceMoved()
-            )
-        MotionEvent.ACTION_UP ->
-            TouchHandlingViewInteractionHandler.MotionEventModel.Up(
-                distanceMoved = distanceMoved(),
-                gestureDuration = gestureDuration(),
-            )
-        MotionEvent.ACTION_CANCEL -> TouchHandlingViewInteractionHandler.MotionEventModel.Cancel
-        else -> TouchHandlingViewInteractionHandler.MotionEventModel.Other
-    }
-}
-
-private fun MotionEvent.distanceMoved(): Float {
-    return if (historySize > 0) {
-        sqrt((x - getHistoricalX(0)).pow(2) + (y - getHistoricalY(0)).pow(2))
-    } else {
-        0f
-    }
-}
-
-private fun MotionEvent.gestureDuration(): Long {
-    return eventTime - downTime
-}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/TouchHandlingViewInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/TouchHandlingViewInteractionHandler.kt
index 5863fc6..fe509d7 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/TouchHandlingViewInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/TouchHandlingViewInteractionHandler.kt
@@ -17,12 +17,20 @@
 
 package com.android.systemui.common.ui.view
 
+import android.content.Context
 import android.graphics.Point
+import android.view.GestureDetector
+import android.view.MotionEvent
+import android.view.ViewConfiguration
 import com.android.systemui.log.TouchHandlingViewLogger
+import kotlin.math.pow
+import kotlin.math.sqrt
+import kotlin.properties.Delegates
 import kotlinx.coroutines.DisposableHandle
 
 /** Encapsulates logic to handle complex touch interactions with a [TouchHandlingView]. */
 class TouchHandlingViewInteractionHandler(
+    context: Context,
     /**
      * Callback to run the given [Runnable] with the given delay, returning a [DisposableHandle]
      * allowing the delayed runnable to be canceled before it is run.
@@ -34,6 +42,8 @@
     private val onLongPressDetected: (x: Int, y: Int) -> Unit,
     /** Callback reporting the a single tap gesture was detected at the given coordinates. */
     private val onSingleTapDetected: (x: Int, y: Int) -> Unit,
+    /** Callback reporting that a double tap gesture was detected. */
+    private val onDoubleTapDetected: () -> Unit,
     /** Time for the touch to be considered a long-press in ms */
     var longPressDuration: () -> Long,
     /**
@@ -58,48 +68,98 @@
     }
 
     var isLongPressHandlingEnabled: Boolean = false
+    var isDoubleTapHandlingEnabled: Boolean = false
     var scheduledLongPressHandle: DisposableHandle? = null
 
+    private var doubleTapAwaitingUp: Boolean = false
+    private var lastDoubleTapDownEventTime: Long? = null
+
     /** Record coordinate for last DOWN event for single tap */
     val lastEventDownCoordinate = Point(-1, -1)
 
-    fun onTouchEvent(event: MotionEventModel?): Boolean {
-        if (!isLongPressHandlingEnabled) {
-            return false
-        }
-        return when (event) {
-            is MotionEventModel.Down -> {
-                scheduleLongPress(event.x, event.y)
-                lastEventDownCoordinate.x = event.x
-                lastEventDownCoordinate.y = event.y
-                true
+    private val gestureDetector =
+        GestureDetector(
+            context,
+            object : GestureDetector.SimpleOnGestureListener() {
+                override fun onDoubleTap(event: MotionEvent): Boolean {
+                    if (isDoubleTapHandlingEnabled) {
+                        doubleTapAwaitingUp = true
+                        lastDoubleTapDownEventTime = event.eventTime
+                        return true
+                    }
+                    return false
+                }
+            },
+        )
+
+    fun onTouchEvent(event: MotionEvent): Boolean {
+        if (isDoubleTapHandlingEnabled) {
+            gestureDetector.onTouchEvent(event)
+            if (event.actionMasked == MotionEvent.ACTION_UP && doubleTapAwaitingUp) {
+                lastDoubleTapDownEventTime?.let { time ->
+                    if (
+                        event.eventTime - time < ViewConfiguration.getDoubleTapTimeout()
+                    ) {
+                        cancelScheduledLongPress()
+                        onDoubleTapDetected()
+                    }
+                }
+                doubleTapAwaitingUp = false
+            } else if (event.actionMasked == MotionEvent.ACTION_CANCEL && doubleTapAwaitingUp) {
+                doubleTapAwaitingUp = false
             }
-            is MotionEventModel.Move -> {
-                if (event.distanceMoved > allowedTouchSlop) {
-                    logger?.cancelingLongPressDueToTouchSlop(event.distanceMoved, allowedTouchSlop)
+        }
+
+        if (isLongPressHandlingEnabled) {
+            val motionEventModel = event.toModel()
+
+            return when (motionEventModel) {
+                is MotionEventModel.Down -> {
+                    scheduleLongPress(motionEventModel.x, motionEventModel.y)
+                    lastEventDownCoordinate.x = motionEventModel.x
+                    lastEventDownCoordinate.y = motionEventModel.y
+                    true
+                }
+
+                is MotionEventModel.Move -> {
+                    if (motionEventModel.distanceMoved > allowedTouchSlop) {
+                        logger?.cancelingLongPressDueToTouchSlop(
+                            motionEventModel.distanceMoved,
+                            allowedTouchSlop,
+                        )
+                        cancelScheduledLongPress()
+                    }
+                    false
+                }
+
+                is MotionEventModel.Up -> {
+                    logger?.onUpEvent(
+                        motionEventModel.distanceMoved,
+                        allowedTouchSlop,
+                        motionEventModel.gestureDuration,
+                    )
                     cancelScheduledLongPress()
+                    if (
+                        motionEventModel.distanceMoved <= allowedTouchSlop &&
+                            motionEventModel.gestureDuration < longPressDuration()
+                    ) {
+                        logger?.dispatchingSingleTap()
+                        dispatchSingleTap(lastEventDownCoordinate.x, lastEventDownCoordinate.y)
+                    }
+                    false
                 }
-                false
-            }
-            is MotionEventModel.Up -> {
-                logger?.onUpEvent(event.distanceMoved, allowedTouchSlop, event.gestureDuration)
-                cancelScheduledLongPress()
-                if (
-                    event.distanceMoved <= allowedTouchSlop &&
-                        event.gestureDuration < longPressDuration()
-                ) {
-                    logger?.dispatchingSingleTap()
-                    dispatchSingleTap(lastEventDownCoordinate.x, lastEventDownCoordinate.y)
+
+                is MotionEventModel.Cancel -> {
+                    logger?.motionEventCancelled()
+                    cancelScheduledLongPress()
+                    false
                 }
-                false
+
+                else -> false
             }
-            is MotionEventModel.Cancel -> {
-                logger?.motionEventCancelled()
-                cancelScheduledLongPress()
-                false
-            }
-            else -> false
         }
+
+        return false
     }
 
     private fun scheduleLongPress(x: Int, y: Int) {
@@ -134,4 +194,30 @@
 
         onSingleTapDetected(x, y)
     }
+
+    private fun MotionEvent.toModel(): MotionEventModel {
+        return when (actionMasked) {
+            MotionEvent.ACTION_DOWN -> MotionEventModel.Down(x = x.toInt(), y = y.toInt())
+            MotionEvent.ACTION_MOVE -> MotionEventModel.Move(distanceMoved = distanceMoved())
+            MotionEvent.ACTION_UP ->
+                MotionEventModel.Up(
+                    distanceMoved = distanceMoved(),
+                    gestureDuration = gestureDuration(),
+                )
+            MotionEvent.ACTION_CANCEL -> MotionEventModel.Cancel
+            else -> MotionEventModel.Other
+        }
+    }
+
+    private fun MotionEvent.distanceMoved(): Float {
+        return if (historySize > 0) {
+            sqrt((x - getHistoricalX(0)).pow(2) + (y - getHistoricalY(0)).pow(2))
+        } else {
+            0f
+        }
+    }
+
+    private fun MotionEvent.gestureDuration(): Long {
+        return eventTime - downTime
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSuppressionStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSuppressionStartable.kt
new file mode 100644
index 0000000..6a611ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSuppressionStartable.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.communal.data.model.SuppressionReason
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.dagger.CommunalTableLog
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+@SysUISingleton
+class CommunalSuppressionStartable
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    private val suppressionFlows: Set<@JvmSuppressWildcards Flow<SuppressionReason?>>,
+    private val communalSettingsInteractor: CommunalSettingsInteractor,
+    @CommunalTableLog private val tableLogBuffer: TableLogBuffer,
+) : CoreStartable {
+    override fun start() {
+        getSuppressionReasons()
+            .onEach { reasons -> communalSettingsInteractor.setSuppressionReasons(reasons) }
+            .logDiffsForTable(
+                tableLogBuffer = tableLogBuffer,
+                columnName = "suppressionReasons",
+                initialValue = emptyList(),
+            )
+            .flowOn(bgDispatcher)
+            .launchIn(applicationScope)
+    }
+
+    private fun getSuppressionReasons(): Flow<List<SuppressionReason>> {
+        if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
+            return flowOf(listOf(SuppressionReason.ReasonFlagDisabled))
+        }
+        return combine(suppressionFlows) { reasons -> reasons.filterNotNull() }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java
deleted file mode 100644
index 4be9601..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.java
+++ /dev/null
@@ -1,116 +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;
-
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.keyguard.shared.model.DozeStateModel;
-import com.android.systemui.shared.condition.Condition;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.kotlin.JavaAdapter;
-
-import kotlinx.coroutines.CoroutineScope;
-import kotlinx.coroutines.Job;
-
-import javax.inject.Inject;
-
-/**
- * Condition which estimates device inactivity in order to avoid launching a full-screen activity
- * while the user is actively using the device.
- */
-public class DeviceInactiveCondition extends Condition {
-    private final KeyguardStateController mKeyguardStateController;
-    private final WakefulnessLifecycle mWakefulnessLifecycle;
-    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private final KeyguardInteractor mKeyguardInteractor;
-    private final JavaAdapter mJavaAdapter;
-    private Job mAnyDozeListenerJob;
-    private boolean mAnyDoze;
-    private final KeyguardStateController.Callback mKeyguardStateCallback =
-            new KeyguardStateController.Callback() {
-                @Override
-                public void onKeyguardShowingChanged() {
-                    updateState();
-                }
-            };
-    private final WakefulnessLifecycle.Observer mWakefulnessObserver =
-            new WakefulnessLifecycle.Observer() {
-                @Override
-                public void onStartedGoingToSleep() {
-                    updateState();
-                }
-            };
-    private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
-            new KeyguardUpdateMonitorCallback() {
-                @Override
-                public void onDreamingStateChanged(boolean dreaming) {
-                    updateState();
-                }
-            };
-
-    @Inject
-    public DeviceInactiveCondition(@Application CoroutineScope scope,
-            KeyguardStateController keyguardStateController,
-            WakefulnessLifecycle wakefulnessLifecycle, KeyguardUpdateMonitor keyguardUpdateMonitor,
-            KeyguardInteractor keyguardInteractor, JavaAdapter javaAdapter) {
-        super(scope);
-        mKeyguardStateController = keyguardStateController;
-        mWakefulnessLifecycle = wakefulnessLifecycle;
-        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mKeyguardInteractor = keyguardInteractor;
-        mJavaAdapter = javaAdapter;
-    }
-
-    @Override
-    protected void start() {
-        updateState();
-        mKeyguardStateController.addCallback(mKeyguardStateCallback);
-        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
-        mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
-        mAnyDozeListenerJob = mJavaAdapter.alwaysCollectFlow(
-                mKeyguardInteractor.getDozeTransitionModel(), dozeModel -> {
-                    mAnyDoze = !DozeStateModel.Companion.isDozeOff(dozeModel.getTo());
-                    updateState();
-                });
-    }
-
-    @Override
-    protected void stop() {
-        mKeyguardStateController.removeCallback(mKeyguardStateCallback);
-        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
-        mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
-        mAnyDozeListenerJob.cancel(null);
-    }
-
-    @Override
-    public int getStartStrategy() {
-        return START_EAGERLY;
-    }
-
-    private void updateState() {
-        final boolean asleep = mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP;
-        // Doze/AoD is also a dream, but we should never override it with low light as to the user
-        // it's totally unrelated.
-        updateCondition(!mAnyDoze && (asleep || mKeyguardStateController.isShowing()
-                || mKeyguardUpdateMonitor.isDreaming()));
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt
new file mode 100644
index 0000000..70dce2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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
+
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.shared.condition.Condition
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.kotlin.JavaAdapter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+/**
+ * Condition which estimates device inactivity in order to avoid launching a full-screen activity
+ * while the user is actively using the device.
+ */
+class DeviceInactiveCondition
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    @Background backgroundScope: CoroutineScope,
+    private val keyguardStateController: KeyguardStateController,
+    private val wakefulnessLifecycle: WakefulnessLifecycle,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+    private val keyguardInteractor: KeyguardInteractor,
+    private val javaAdapter: JavaAdapter,
+) : Condition(backgroundScope) {
+    private var anyDozeListenerJob: Job? = null
+    private var anyDoze = false
+    private val keyguardStateCallback: KeyguardStateController.Callback =
+        object : KeyguardStateController.Callback {
+            override fun onKeyguardShowingChanged() {
+                updateState()
+            }
+        }
+    private val wakefulnessObserver: WakefulnessLifecycle.Observer =
+        object : WakefulnessLifecycle.Observer {
+            override fun onStartedGoingToSleep() {
+                updateState()
+            }
+        }
+    private val keyguardUpdateCallback: KeyguardUpdateMonitorCallback =
+        object : KeyguardUpdateMonitorCallback() {
+            override fun onDreamingStateChanged(dreaming: Boolean) {
+                updateState()
+            }
+        }
+
+    override suspend fun start() {
+        updateState()
+        keyguardStateController.addCallback(keyguardStateCallback)
+
+        // Keyguard update monitor callbacks must be registered on the main thread
+        applicationScope.launch { keyguardUpdateMonitor.registerCallback(keyguardUpdateCallback) }
+        wakefulnessLifecycle.addObserver(wakefulnessObserver)
+        anyDozeListenerJob =
+            javaAdapter.alwaysCollectFlow(keyguardInteractor.dozeTransitionModel) {
+                dozeModel: DozeTransitionModel ->
+                anyDoze = !isDozeOff(dozeModel.to)
+                updateState()
+            }
+    }
+
+    override fun stop() {
+        keyguardStateController.removeCallback(keyguardStateCallback)
+        keyguardUpdateMonitor.removeCallback(keyguardUpdateCallback)
+        wakefulnessLifecycle.removeObserver(wakefulnessObserver)
+        anyDozeListenerJob?.cancel(null)
+    }
+
+    override val startStrategy: Int
+        get() = START_EAGERLY
+
+    private fun updateState() {
+        val asleep = wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP
+        // Doze/AoD is also a dream, but we should never override it with low light as to the user
+        // it's totally unrelated.
+        updateCondition(
+            !anyDoze &&
+                (asleep || keyguardStateController.isShowing || keyguardUpdateMonitor.isDreaming)
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index bb3be53..a31c0bd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
 import com.android.systemui.communal.domain.interactor.CommunalSceneTransitionInteractor
+import com.android.systemui.communal.domain.suppression.dagger.CommunalSuppressionModule
 import com.android.systemui.communal.shared.log.CommunalMetricsLogger
 import com.android.systemui.communal.shared.log.CommunalStatsLogProxyImpl
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -70,6 +71,7 @@
             CommunalSmartspaceRepositoryModule::class,
             CommunalStartableModule::class,
             GlanceableHubWidgetManagerModule::class,
+            CommunalSuppressionModule::class,
         ]
 )
 interface CommunalModule {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
index 7358aa7..a4f75e8 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.communal.CommunalMetricsStartable
 import com.android.systemui.communal.CommunalOngoingContentStartable
 import com.android.systemui.communal.CommunalSceneStartable
+import com.android.systemui.communal.CommunalSuppressionStartable
 import com.android.systemui.communal.DevicePosturingListener
 import com.android.systemui.communal.log.CommunalLoggerStartable
 import com.android.systemui.communal.widgets.CommunalAppWidgetHostStartable
@@ -73,4 +74,9 @@
     @IntoMap
     @ClassKey(DevicePosturingListener::class)
     fun bindDevicePosturingistener(impl: DevicePosturingListener): CoreStartable
+
+    @Binds
+    @IntoMap
+    @ClassKey(CommunalSuppressionStartable::class)
+    fun bindCommunalSuppressionStartable(impl: CommunalSuppressionStartable): CoreStartable
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt
deleted file mode 100644
index 83a5bdb..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.data.model
-
-import com.android.systemui.log.table.Diffable
-import com.android.systemui.log.table.TableRowLogger
-import java.util.EnumSet
-
-/** Reasons that communal is disabled, primarily for logging. */
-enum class DisabledReason(val loggingString: String) {
-    /** Communal should be disabled due to invalid current user */
-    DISABLED_REASON_INVALID_USER("invalidUser"),
-    /** Communal should be disabled due to the flag being off */
-    DISABLED_REASON_FLAG("flag"),
-    /** Communal should be disabled because the user has turned off the setting */
-    DISABLED_REASON_USER_SETTING("userSetting"),
-    /** Communal is disabled by the device policy app */
-    DISABLED_REASON_DEVICE_POLICY("devicePolicy"),
-}
-
-/**
- * Model representing the reasons communal hub should be disabled. Allows logging reasons separately
- * for debugging.
- */
-@JvmInline
-value class CommunalEnabledState(
-    private val disabledReasons: EnumSet<DisabledReason> =
-        EnumSet.noneOf(DisabledReason::class.java)
-) : Diffable<CommunalEnabledState>, Set<DisabledReason> by disabledReasons {
-
-    /** Creates [CommunalEnabledState] with a single reason for being disabled */
-    constructor(reason: DisabledReason) : this(EnumSet.of(reason))
-
-    /** Checks if there are any reasons communal should be disabled. If none, returns true. */
-    val enabled: Boolean
-        get() = isEmpty()
-
-    override fun logDiffs(prevVal: CommunalEnabledState, row: TableRowLogger) {
-        for (reason in DisabledReason.entries) {
-            val newVal = contains(reason)
-            if (newVal != prevVal.contains(reason)) {
-                row.logChange(
-                    columnName = reason.loggingString,
-                    value = newVal,
-                )
-            }
-        }
-    }
-
-    override fun logFull(row: TableRowLogger) {
-        for (reason in DisabledReason.entries) {
-            row.logChange(columnName = reason.loggingString, value = contains(reason))
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalFeature.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalFeature.kt
new file mode 100644
index 0000000..5fb1c4e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalFeature.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.model
+
+import android.annotation.IntDef
+
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(
+    flag = true,
+    prefix = ["FEATURE_"],
+    value = [FEATURE_AUTO_OPEN, FEATURE_MANUAL_OPEN, FEATURE_ENABLED, FEATURE_ALL],
+)
+annotation class CommunalFeature
+
+/** If we should automatically open the hub */
+const val FEATURE_AUTO_OPEN: Int = 1
+
+/** If the user is allowed to manually open the hub */
+const val FEATURE_MANUAL_OPEN: Int = 1 shl 1
+
+/**
+ * If the hub should be considered enabled. If not, it may be cleaned up entirely to reduce memory
+ * footprint.
+ */
+const val FEATURE_ENABLED: Int = 1 shl 2
+
+const val FEATURE_ALL: Int = FEATURE_ENABLED or FEATURE_MANUAL_OPEN or FEATURE_AUTO_OPEN
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/SuppressionReason.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/SuppressionReason.kt
new file mode 100644
index 0000000..de05bed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/SuppressionReason.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.model
+
+sealed interface SuppressionReason {
+    @CommunalFeature val suppressedFeatures: Int
+
+    /** Whether this reason suppresses a particular feature. */
+    fun isSuppressed(@CommunalFeature feature: Int): Boolean {
+        return (suppressedFeatures and feature) != 0
+    }
+
+    /** Suppress hub automatically opening due to Android Auto projection */
+    data object ReasonCarProjection : SuppressionReason {
+        override val suppressedFeatures: Int = FEATURE_AUTO_OPEN
+    }
+
+    /** Suppress hub due to the "When to dream" conditions not being met */
+    data class ReasonWhenToAutoShow(override val suppressedFeatures: Int) : SuppressionReason
+
+    /** Suppress hub due to device policy */
+    data object ReasonDevicePolicy : SuppressionReason {
+        override val suppressedFeatures: Int = FEATURE_ALL
+    }
+
+    /** Suppress hub due to the user disabling the setting */
+    data object ReasonSettingDisabled : SuppressionReason {
+        override val suppressedFeatures: Int = FEATURE_ALL
+    }
+
+    /** Suppress hub due to the user being locked */
+    data object ReasonUserLocked : SuppressionReason {
+        override val suppressedFeatures: Int = FEATURE_ALL
+    }
+
+    /** Suppress hub due the a secondary user being active */
+    data object ReasonSecondaryUser : SuppressionReason {
+        override val suppressedFeatures: Int = FEATURE_ALL
+    }
+
+    /** Suppress hub due to the flag being disabled */
+    data object ReasonFlagDisabled : SuppressionReason {
+        override val suppressedFeatures: Int = FEATURE_ALL
+    }
+
+    /** Suppress hub due to an unknown reason, used as initial state and in tests */
+    data class ReasonUnknown(override val suppressedFeatures: Int = FEATURE_ALL) :
+        SuppressionReason
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CarProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CarProjectionRepository.kt
new file mode 100644
index 0000000..4fe641a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CarProjectionRepository.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import android.app.UiModeManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
+
+interface CarProjectionRepository {
+    /** Whether car projection is active. */
+    val projectionActive: Flow<Boolean>
+
+    /**
+     * Checks the system for the current car projection state.
+     *
+     * @return True if projection is active, false otherwise.
+     */
+    suspend fun isProjectionActive(): Boolean
+}
+
+@SysUISingleton
+class CarProjectionRepositoryImpl
+@Inject
+constructor(
+    private val uiModeManager: UiModeManager,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+) : CarProjectionRepository {
+    override val projectionActive: Flow<Boolean> =
+        conflatedCallbackFlow {
+                val listener =
+                    UiModeManager.OnProjectionStateChangedListener { _, _ -> trySend(Unit) }
+                uiModeManager.addOnProjectionStateChangedListener(
+                    UiModeManager.PROJECTION_TYPE_AUTOMOTIVE,
+                    bgDispatcher.asExecutor(),
+                    listener,
+                )
+                awaitClose { uiModeManager.removeOnProjectionStateChangedListener(listener) }
+            }
+            .emitOnStart()
+            .map { isProjectionActive() }
+            .flowOn(bgDispatcher)
+
+    override suspend fun isProjectionActive(): Boolean =
+        withContext(bgDispatcher) {
+            (uiModeManager.activeProjectionTypes and UiModeManager.PROJECTION_TYPE_AUTOMOTIVE) != 0
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
index 7f137f3..0d590db 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
@@ -22,4 +22,6 @@
 @Module
 interface CommunalRepositoryModule {
     @Binds fun communalRepository(impl: CommunalSceneRepositoryImpl): CommunalSceneRepository
+
+    @Binds fun carProjectionRepository(impl: CarProjectionRepositoryImpl): CarProjectionRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 4c291a0..42a345b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -26,15 +26,13 @@
 import com.android.systemui.Flags.communalHub
 import com.android.systemui.Flags.glanceableHubV2
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.communal.data.model.CommunalEnabledState
-import com.android.systemui.communal.data.model.DisabledReason
-import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_DEVICE_POLICY
-import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
-import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_INVALID_USER
-import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING
+import com.android.systemui.communal.data.model.CommunalFeature
+import com.android.systemui.communal.data.model.FEATURE_ALL
+import com.android.systemui.communal.data.model.SuppressionReason
 import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule.Companion.DEFAULT_BACKGROUND_TYPE
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.WhenToDream
+import com.android.systemui.communal.shared.model.WhenToStartHub
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -43,22 +41,23 @@
 import com.android.systemui.util.kotlin.emitOnStart
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
-import java.util.EnumSet
 import javax.inject.Inject
 import javax.inject.Named
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
 
 interface CommunalSettingsRepository {
-    /** A [CommunalEnabledState] for the specified user. */
-    fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
+    /** Whether a particular feature is enabled */
+    fun isEnabled(@CommunalFeature feature: Int): Flow<Boolean>
 
-    fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean>
+    /**
+     * Suppresses the hub with the given reasons. If there are no reasons, the hub will not be
+     * suppressed.
+     */
+    fun setSuppressionReasons(reasons: List<SuppressionReason>)
 
     /**
      * Returns a [WhenToDream] for the specified user, indicating what state the device should be in
@@ -67,6 +66,15 @@
     fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream>
 
     /**
+     * Returns a[WhenToStartHub] for the specified user, indicating what state the device should be
+     * in to automatically display the hub.
+     */
+    fun getWhenToStartHubState(user: UserInfo): Flow<WhenToStartHub>
+
+    /** Returns whether glanceable hub is enabled by the current user. */
+    fun getSettingEnabledByUser(user: UserInfo): Flow<Boolean>
+
+    /**
      * Returns true if any glanceable hub functionality should be enabled via configs and flags.
      *
      * This should be used for preventing basic glanceable hub functionality from running on devices
@@ -123,6 +131,23 @@
         resources.getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault)
     }
 
+    private val whenToStartHubByDefault by lazy {
+        resources.getInteger(com.android.internal.R.integer.config_whenToStartHubModeDefault)
+    }
+
+    private val _suppressionReasons =
+        MutableStateFlow<List<SuppressionReason>>(
+            // Suppress hub by default until we get an initial update.
+            listOf(SuppressionReason.ReasonUnknown(FEATURE_ALL))
+        )
+
+    override fun isEnabled(@CommunalFeature feature: Int): Flow<Boolean> =
+        _suppressionReasons.map { reasons -> reasons.none { it.isSuppressed(feature) } }
+
+    override fun setSuppressionReasons(reasons: List<SuppressionReason>) {
+        _suppressionReasons.value = reasons
+    }
+
     override fun getFlagEnabled(): Boolean {
         return if (getV2FlagEnabled()) {
             true
@@ -138,44 +163,6 @@
             glanceableHubV2()
     }
 
-    override fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> {
-        if (!user.isMain) {
-            return flowOf(CommunalEnabledState(DISABLED_REASON_INVALID_USER))
-        }
-        if (!getFlagEnabled()) {
-            return flowOf(CommunalEnabledState(DISABLED_REASON_FLAG))
-        }
-        return combine(
-                getEnabledByUser(user).mapToReason(DISABLED_REASON_USER_SETTING),
-                getAllowedByDevicePolicy(user).mapToReason(DISABLED_REASON_DEVICE_POLICY),
-            ) { reasons ->
-                reasons.filterNotNull()
-            }
-            .map { reasons ->
-                if (reasons.isEmpty()) {
-                    EnumSet.noneOf(DisabledReason::class.java)
-                } else {
-                    EnumSet.copyOf(reasons)
-                }
-            }
-            .map { reasons -> CommunalEnabledState(reasons) }
-            .flowOn(bgDispatcher)
-    }
-
-    override fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean> =
-        secureSettings
-            .observerFlow(userId = user.id, names = arrayOf(Settings.Secure.SCREENSAVER_ENABLED))
-            // Force an update
-            .onStart { emit(Unit) }
-            .map {
-                secureSettings.getIntForUser(
-                    Settings.Secure.SCREENSAVER_ENABLED,
-                    SCREENSAVER_ENABLED_SETTING_DEFAULT,
-                    user.id,
-                ) == 1
-            }
-            .flowOn(bgDispatcher)
-
     override fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream> =
         secureSettings
             .observerFlow(
@@ -219,6 +206,31 @@
             }
             .flowOn(bgDispatcher)
 
+    override fun getWhenToStartHubState(user: UserInfo): Flow<WhenToStartHub> =
+        secureSettings
+            .observerFlow(
+                userId = user.id,
+                names = arrayOf(Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB),
+            )
+            .emitOnStart()
+            .map {
+                when (
+                    secureSettings.getIntForUser(
+                        Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+                        whenToStartHubByDefault,
+                        user.id,
+                    )
+                ) {
+                    Settings.Secure.GLANCEABLE_HUB_START_NEVER -> WhenToStartHub.NEVER
+                    Settings.Secure.GLANCEABLE_HUB_START_CHARGING -> WhenToStartHub.WHILE_CHARGING
+                    Settings.Secure.GLANCEABLE_HUB_START_CHARGING_UPRIGHT ->
+                        WhenToStartHub.WHILE_CHARGING_AND_POSTURED
+                    Settings.Secure.GLANCEABLE_HUB_START_DOCKED -> WhenToStartHub.WHILE_DOCKED
+                    else -> WhenToStartHub.NEVER
+                }
+            }
+            .flowOn(bgDispatcher)
+
     override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
         broadcastDispatcher
             .broadcastFlow(
@@ -247,11 +259,11 @@
                     ?: defaultBackgroundType
             }
 
-    private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
+    override fun getSettingEnabledByUser(user: UserInfo): Flow<Boolean> =
         secureSettings
             .observerFlow(userId = user.id, names = arrayOf(Settings.Secure.GLANCEABLE_HUB_ENABLED))
             // Force an update
-            .onStart { emit(Unit) }
+            .emitOnStart()
             .map {
                 secureSettings.getIntForUser(
                     Settings.Secure.GLANCEABLE_HUB_ENABLED,
@@ -259,17 +271,13 @@
                     user.id,
                 ) == 1
             }
+            .flowOn(bgDispatcher)
 
     companion object {
         const val GLANCEABLE_HUB_BACKGROUND_SETTING = "glanceable_hub_background"
         private const val ENABLED_SETTING_DEFAULT = 1
-        private const val SCREENSAVER_ENABLED_SETTING_DEFAULT = 0
     }
 }
 
 private fun DevicePolicyManager.areKeyguardWidgetsAllowed(userId: Int): Boolean =
     (getKeyguardDisabledFeatures(null, userId) and KEYGUARD_DISABLE_WIDGETS_ALL) == 0
-
-private fun Flow<Boolean>.mapToReason(reason: DisabledReason) = map { enabled ->
-    if (enabled) null else reason
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CarProjectionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CarProjectionInteractor.kt
new file mode 100644
index 0000000..17b61e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CarProjectionInteractor.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import com.android.systemui.communal.data.repository.CarProjectionRepository
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+class CarProjectionInteractor @Inject constructor(repository: CarProjectionRepository) {
+    /** Whether car projection is active. */
+    val projectionActive = repository.projectionActive
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt
new file mode 100644
index 0000000..20bfabd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import com.android.systemui.common.domain.interactor.BatteryInteractor
+import com.android.systemui.communal.dagger.CommunalModule.Companion.SWIPE_TO_HUB
+import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN
+import com.android.systemui.communal.data.model.FEATURE_MANUAL_OPEN
+import com.android.systemui.communal.data.model.SuppressionReason
+import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor
+import com.android.systemui.communal.shared.model.WhenToStartHub
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.retrieveIsDocked
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
+import javax.inject.Inject
+import javax.inject.Named
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class CommunalAutoOpenInteractor
+@Inject
+constructor(
+    communalSettingsInteractor: CommunalSettingsInteractor,
+    @Background private val backgroundContext: CoroutineContext,
+    private val batteryInteractor: BatteryInteractor,
+    private val posturingInteractor: PosturingInteractor,
+    private val dockManager: DockManager,
+    @Named(SWIPE_TO_HUB) private val allowSwipeAlways: Boolean,
+) {
+    val shouldAutoOpen: Flow<Boolean> =
+        communalSettingsInteractor.whenToStartHub
+            .flatMapLatestConflated { whenToStartHub ->
+                when (whenToStartHub) {
+                    WhenToStartHub.WHILE_CHARGING -> batteryInteractor.isDevicePluggedIn
+                    WhenToStartHub.WHILE_DOCKED -> {
+                        allOf(batteryInteractor.isDevicePluggedIn, dockManager.retrieveIsDocked())
+                    }
+                    WhenToStartHub.WHILE_CHARGING_AND_POSTURED -> {
+                        allOf(batteryInteractor.isDevicePluggedIn, posturingInteractor.postured)
+                    }
+                    WhenToStartHub.NEVER -> flowOf(false)
+                }
+            }
+            .flowOn(backgroundContext)
+
+    val suppressionReason: Flow<SuppressionReason?> =
+        shouldAutoOpen.map { conditionMet ->
+            if (conditionMet) {
+                null
+            } else {
+                var suppressedFeatures = FEATURE_AUTO_OPEN
+                if (!allowSwipeAlways) {
+                    suppressedFeatures = suppressedFeatures or FEATURE_MANUAL_OPEN
+                }
+                SuppressionReason.ReasonWhenToAutoShow(suppressedFeatures)
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 564628d..684c52a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -30,13 +30,11 @@
 import com.android.systemui.Flags.communalResponsiveGrid
 import com.android.systemui.Flags.glanceableHubBlurredBackground
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.domain.interactor.BatteryInteractor
 import com.android.systemui.communal.data.repository.CommunalMediaRepository
 import com.android.systemui.communal.data.repository.CommunalSmartspaceRepository
 import com.android.systemui.communal.data.repository.CommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.domain.model.CommunalContentModel.WidgetContent
-import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.FULL
@@ -45,14 +43,11 @@
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.shared.model.EditModeState
-import com.android.systemui.communal.shared.model.WhenToDream
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dock.DockManager
-import com.android.systemui.dock.retrieveIsDocked
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -69,11 +64,8 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.ManagedProfileController
-import com.android.systemui.user.domain.interactor.UserLockedInteractor
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
-import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import com.android.systemui.util.kotlin.emitOnStart
-import com.android.systemui.util.kotlin.isDevicePluggedIn
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.minutes
 import kotlinx.coroutines.CoroutineDispatcher
@@ -125,10 +117,6 @@
     @CommunalLog logBuffer: LogBuffer,
     @CommunalTableLog tableLogBuffer: TableLogBuffer,
     private val managedProfileController: ManagedProfileController,
-    private val batteryInteractor: BatteryInteractor,
-    private val dockManager: DockManager,
-    private val posturingInteractor: PosturingInteractor,
-    private val userLockedInteractor: UserLockedInteractor,
 ) {
     private val logger = Logger(logBuffer, "CommunalInteractor")
 
@@ -162,11 +150,7 @@
 
     /** Whether communal features are enabled and available. */
     val isCommunalAvailable: Flow<Boolean> =
-        allOf(
-                communalSettingsInteractor.isCommunalEnabled,
-                userLockedInteractor.isUserUnlocked(userManager.mainUser),
-                keyguardInteractor.isKeyguardShowing,
-            )
+        allOf(communalSettingsInteractor.isCommunalEnabled, keyguardInteractor.isKeyguardShowing)
             .distinctUntilChanged()
             .onEach { available ->
                 logger.i({ "Communal is ${if (bool1) "" else "un"}available" }) {
@@ -184,37 +168,6 @@
                 replay = 1,
             )
 
-    /**
-     * Whether communal hub should be shown automatically, depending on the user's [WhenToDream]
-     * state.
-     */
-    val shouldShowCommunal: StateFlow<Boolean> =
-        allOf(
-                isCommunalAvailable,
-                communalSettingsInteractor.whenToDream
-                    .flatMapLatest { whenToDream ->
-                        when (whenToDream) {
-                            WhenToDream.NEVER -> flowOf(false)
-
-                            WhenToDream.WHILE_CHARGING -> batteryInteractor.isDevicePluggedIn
-
-                            WhenToDream.WHILE_DOCKED ->
-                                allOf(
-                                    batteryInteractor.isDevicePluggedIn,
-                                    dockManager.retrieveIsDocked(),
-                                )
-
-                            WhenToDream.WHILE_POSTURED ->
-                                allOf(
-                                    batteryInteractor.isDevicePluggedIn,
-                                    posturingInteractor.postured,
-                                )
-                        }
-                    }
-                    .flowOn(bgDispatcher),
-            )
-            .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
-
     private val _isDisclaimerDismissed = MutableStateFlow(false)
     val isDisclaimerDismissed: Flow<Boolean> = _isDisclaimerDismissed.asStateFlow()
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index a0b1261..cf51fa1 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -17,18 +17,20 @@
 package com.android.systemui.communal.domain.interactor
 
 import android.content.pm.UserInfo
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN
+import com.android.systemui.communal.data.model.FEATURE_ENABLED
+import com.android.systemui.communal.data.model.FEATURE_MANUAL_OPEN
+import com.android.systemui.communal.data.model.SuppressionReason
 import com.android.systemui.communal.data.repository.CommunalSettingsRepository
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.WhenToDream
+import com.android.systemui.communal.shared.model.WhenToStartHub
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.log.dagger.CommunalTableLog
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
@@ -53,33 +55,49 @@
     private val repository: CommunalSettingsRepository,
     userInteractor: SelectedUserInteractor,
     private val userTracker: UserTracker,
-    @CommunalTableLog tableLogBuffer: TableLogBuffer,
 ) {
-    /** Whether or not communal is enabled for the currently selected user. */
+    /** Whether communal is enabled at all. */
     val isCommunalEnabled: StateFlow<Boolean> =
-        userInteractor.selectedUserInfo
-            .flatMapLatest { user -> repository.getEnabledState(user) }
-            .logDiffsForTable(
-                tableLogBuffer = tableLogBuffer,
-                columnPrefix = "disabledReason",
-                initialValue = CommunalEnabledState(),
-            )
-            .map { model -> model.enabled }
-            // Start this eagerly since the value is accessed synchronously in many places.
+        repository
+            .isEnabled(FEATURE_ENABLED)
             .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
 
-    /** Whether or not screensaver (dreams) is enabled for the currently selected user. */
-    val isScreensaverEnabled: Flow<Boolean> =
-        userInteractor.selectedUserInfo.flatMapLatest { user ->
-            repository.getScreensaverEnabledState(user)
-        }
+    /** Whether manually opening the hub is enabled */
+    val manualOpenEnabled: StateFlow<Boolean> =
+        repository
+            .isEnabled(FEATURE_MANUAL_OPEN)
+            .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+
+    /** Whether auto-opening the hub is enabled */
+    val autoOpenEnabled: StateFlow<Boolean> =
+        repository
+            .isEnabled(FEATURE_AUTO_OPEN)
+            .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
 
     /** When to dream for the currently selected user. */
     val whenToDream: Flow<WhenToDream> =
-        userInteractor.selectedUserInfo.flatMapLatest { user ->
+        userInteractor.selectedUserInfo.flatMapLatestConflated { user ->
             repository.getWhenToDreamState(user)
         }
 
+    /** When to automatically start hub for the currently selected user. */
+    val whenToStartHub: Flow<WhenToStartHub> =
+        userInteractor.selectedUserInfo.flatMapLatest { user ->
+            repository.getWhenToStartHubState(user)
+        }
+
+    /** Whether communal hub is allowed by device policy for the current user */
+    val allowedForCurrentUserByDevicePolicy: Flow<Boolean> =
+        userInteractor.selectedUserInfo.flatMapLatestConflated { user ->
+            repository.getAllowedByDevicePolicy(user)
+        }
+
+    /** Whether the hub is enabled for the current user */
+    val settingEnabledForCurrentUser: Flow<Boolean> =
+        userInteractor.selectedUserInfo.flatMapLatestConflated { user ->
+            repository.getSettingEnabledByUser(user)
+        }
+
     /**
      * Returns true if any glanceable hub functionality should be enabled via configs and flags.
      *
@@ -109,6 +127,14 @@
      */
     fun isV2FlagEnabled(): Boolean = repository.getV2FlagEnabled()
 
+    /**
+     * Suppresses the hub with the given reasons. If there are no reasons, the hub will not be
+     * suppressed.
+     */
+    fun setSuppressionReasons(reasons: List<SuppressionReason>) {
+        repository.setSuppressionReasons(reasons)
+    }
+
     /** The type of background to use for the hub. Used to experiment with different backgrounds */
     val communalBackground: Flow<CommunalBackgroundType> =
         userInteractor.selectedUserInfo
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/suppression/FlowExt.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/suppression/FlowExt.kt
new file mode 100644
index 0000000..a10e90f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/suppression/FlowExt.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.suppression
+
+import com.android.systemui.communal.data.model.SuppressionReason
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+fun Flow<Boolean>.mapToReasonIfNotAllowed(reason: SuppressionReason): Flow<SuppressionReason?> =
+    this.map { allowed -> if (allowed) null else reason }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/suppression/dagger/CommunalSuppressionModule.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/suppression/dagger/CommunalSuppressionModule.kt
new file mode 100644
index 0000000..c62d77e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/suppression/dagger/CommunalSuppressionModule.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.suppression.dagger
+
+import com.android.systemui.Flags.glanceableHubV2
+import com.android.systemui.communal.data.model.SuppressionReason
+import com.android.systemui.communal.domain.interactor.CarProjectionInteractor
+import com.android.systemui.communal.domain.interactor.CommunalAutoOpenInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.domain.suppression.mapToReasonIfNotAllowed
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.user.domain.interactor.UserLockedInteractor
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoSet
+import dagger.multibindings.Multibinds
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+@Module
+interface CommunalSuppressionModule {
+    /**
+     * A set of reasons why communal may be suppressed. Ensures that this can be injected even if
+     * it's empty.
+     */
+    @Multibinds fun suppressorSet(): Set<Flow<SuppressionReason?>>
+
+    companion object {
+        @Provides
+        @IntoSet
+        fun provideCarProjectionSuppressor(
+            interactor: CarProjectionInteractor
+        ): Flow<SuppressionReason?> {
+            if (!glanceableHubV2()) {
+                return flowOf(null)
+            }
+            return not(interactor.projectionActive)
+                .mapToReasonIfNotAllowed(SuppressionReason.ReasonCarProjection)
+        }
+
+        @Provides
+        @IntoSet
+        fun provideDevicePolicySuppressor(
+            interactor: CommunalSettingsInteractor
+        ): Flow<SuppressionReason?> {
+            return interactor.allowedForCurrentUserByDevicePolicy.mapToReasonIfNotAllowed(
+                SuppressionReason.ReasonDevicePolicy
+            )
+        }
+
+        @Provides
+        @IntoSet
+        fun provideSettingDisabledSuppressor(
+            interactor: CommunalSettingsInteractor
+        ): Flow<SuppressionReason?> {
+            return interactor.settingEnabledForCurrentUser.mapToReasonIfNotAllowed(
+                SuppressionReason.ReasonSettingDisabled
+            )
+        }
+
+        @Provides
+        @IntoSet
+        fun bindUserLockedSuppressor(interactor: UserLockedInteractor): Flow<SuppressionReason?> {
+            return interactor.currentUserUnlocked.mapToReasonIfNotAllowed(
+                SuppressionReason.ReasonUserLocked
+            )
+        }
+
+        @Provides
+        @IntoSet
+        fun provideAutoOpenSuppressor(
+            interactor: CommunalAutoOpenInteractor
+        ): Flow<SuppressionReason?> {
+            return interactor.suppressionReason
+        }
+
+        @Provides
+        @IntoSet
+        fun provideMainUserSuppressor(
+            interactor: SelectedUserInteractor
+        ): Flow<SuppressionReason?> {
+            return interactor.selectedUserInfo
+                .map { it.isMain }
+                .mapToReasonIfNotAllowed(SuppressionReason.ReasonSecondaryUser)
+        }
+    }
+}
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToStartHub.kt
similarity index 71%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToStartHub.kt
index 5d14631..be89fda 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToStartHub.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,6 +14,11 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package com.android.systemui.communal.shared.model
 
-parcelable PictureProfileHandle;
+enum class WhenToStartHub {
+    NEVER,
+    WHILE_CHARGING,
+    WHILE_CHARGING_AND_POSTURED,
+    WHILE_DOCKED,
+}
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 d7859c9..756edb3 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
@@ -247,7 +247,7 @@
                 showsOnlyActiveMedia = false
             }
             falsingProtectionNeeded = false
-            disablePagination = true
+            disableScrolling = true
             init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
         }
     }
@@ -367,11 +367,22 @@
     /** See [CommunalSettingsInteractor.isV2FlagEnabled] */
     fun v2FlagEnabled(): Boolean = communalSettingsInteractor.isV2FlagEnabled()
 
-    val swipeToHubEnabled: StateFlow<Boolean> by lazy {
+    val swipeToHubEnabled: Flow<Boolean> by lazy {
+        val inAllowedDeviceState =
+            if (v2FlagEnabled()) {
+                communalSettingsInteractor.manualOpenEnabled
+            } else {
+                MutableStateFlow(swipeToHub)
+            }
+
         if (v2FlagEnabled()) {
-            communalInteractor.shouldShowCommunal
+            val inAllowedKeyguardState =
+                keyguardTransitionInteractor.startedKeyguardTransitionStep.map {
+                    it.to == KeyguardState.LOCKSCREEN || it.to == KeyguardState.GLANCEABLE_HUB
+                }
+            allOf(inAllowedDeviceState, inAllowedKeyguardState)
         } else {
-            MutableStateFlow(swipeToHub)
+            inAllowedDeviceState
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
index 06a14ea..440c300 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal.widgets
 
+import android.appwidget.AppWidgetProviderInfo
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -101,6 +102,7 @@
                     val (_, isActive) = withPrev
                     // The validation is performed once the hub becomes active.
                     if (isActive) {
+                        removeNotLockscreenWidgets(widgets)
                         validateWidgetsAndDeleteOrphaned(widgets)
                     }
                 }
@@ -144,6 +146,19 @@
             }
         }
 
+    private fun removeNotLockscreenWidgets(widgets: List<CommunalWidgetContentModel>) {
+        widgets
+            .filter { widget ->
+                when (widget) {
+                    is CommunalWidgetContentModel.Available ->
+                        widget.providerInfo.widgetCategory and
+                            AppWidgetProviderInfo.WIDGET_CATEGORY_NOT_KEYGUARD != 0
+                    else -> false
+                }
+            }
+            .onEach { widget -> communalInteractor.deleteWidget(id = widget.appWidgetId) }
+    }
+
     /**
      * Ensure the existence of all associated users for widgets, and remove widgets belonging to
      * users who have been deleted.
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index 037b6fa..ba9f52b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -51,8 +51,9 @@
 import com.android.systemui.qs.shared.model.TileCategory
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.DeviceControlsTile
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.res.R
 import dagger.Binds
 import dagger.BindsOptionalOf
 import dagger.Module
@@ -91,8 +92,8 @@
                 tileSpec = TileSpec.create(DEVICE_CONTROLS_SPEC),
                 uiConfig =
                     QSTileUIConfig.Resource(
-                        iconRes = com.android.systemui.res.R.drawable.controls_icon,
-                        labelRes = com.android.systemui.res.R.string.quick_controls_title
+                        iconRes = R.drawable.controls_icon,
+                        labelRes = R.string.quick_controls_title,
                     ),
                 instanceId = uiEventLogger.getNewInstanceId(),
                 category = TileCategory.UTILITIES,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt
index 6f579a3..d7ffbb2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt
@@ -18,7 +18,7 @@
 package com.android.systemui.controls.settings
 
 import android.provider.Settings
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
index 39708a7..3520439 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
@@ -16,9 +16,9 @@
 
 package com.android.systemui.dagger
 
-import com.android.systemui.display.data.repository.DefaultDisplayOnlyInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayRepository
+import com.android.app.displaylib.DefaultDisplayOnlyInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayRepository
 import com.android.systemui.model.SysUIStateInstanceProvider
 import com.android.systemui.model.SysUiState
 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index a25faa3..11b42a8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -36,6 +36,8 @@
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.education.dagger.ContextualEducationModule;
+import com.android.systemui.topwindoweffects.dagger.SqueezeEffectRepositoryModule;
+import com.android.systemui.topwindoweffects.dagger.TopLevelWindowEffectsModule;
 import com.android.systemui.emergency.EmergencyGestureModule;
 import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialModule;
 import com.android.systemui.keyboard.shortcut.ShortcutHelperModule;
@@ -160,12 +162,14 @@
         StatusBarPhoneModule.class,
         SystemActionsModule.class,
         ShadeModule.class,
+        SqueezeEffectRepositoryModule.class,
         StartCentralSurfacesModule.class,
         SceneContainerFrameworkModule.class,
         SysUICoroutinesModule.class,
         SysUIUnfoldStartableModule.class,
         UnfoldTransitionModule.Startables.class,
         ToastModule.class,
+        TopLevelWindowEffectsModule.class,
         TouchpadTutorialModule.class,
         VolumeModule.class,
         WallpaperModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f08126a..edee64e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -65,7 +65,7 @@
 import com.android.systemui.demomode.dagger.DemoModeModule;
 import com.android.systemui.deviceentry.DeviceEntryModule;
 import com.android.systemui.display.DisplayModule;
-import com.android.systemui.display.data.repository.PerDisplayRepository;
+import com.android.app.displaylib.PerDisplayRepository;
 import com.android.systemui.doze.dagger.DozeComponent;
 import com.android.systemui.dreams.dagger.DreamModule;
 import com.android.systemui.flags.FeatureFlags;
@@ -97,11 +97,11 @@
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.privacy.PrivacyModule;
 import com.android.systemui.process.condition.SystemProcessCondition;
-import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule;
 import com.android.systemui.qs.FgsManagerController;
 import com.android.systemui.qs.FgsManagerControllerImpl;
 import com.android.systemui.qs.QSFragmentStartableModule;
 import com.android.systemui.qs.footer.dagger.FooterActionsModule;
+import com.android.systemui.qs.tiles.impl.qr.ui.model.QRCodeScannerModule;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recordissue.RecordIssueModule;
 import com.android.systemui.retail.RetailModeModule;
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 69378b4..449a995 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
index 675f00a..b7315cc 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
@@ -1,7 +1,7 @@
 package com.android.systemui.deviceentry.data.repository
 
 import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
index 69da67e..1e7bec2 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
@@ -68,10 +68,4 @@
                 emptyFlow()
             }
         }
-
-    /** Triggered if a face failure occurs regardless of the mode. */
-    val faceFailure: Flow<FailedFaceAuthenticationStatus> =
-        deviceEntryFaceAuthInteractor.authenticationStatus.filterIsInstance<
-            FailedFaceAuthenticationStatus
-        >()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
index 38e0503..0993683 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardBypassInteractor
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.util.kotlin.FlowDumperImpl
@@ -49,8 +48,6 @@
 constructor(
     biometricSettingsRepository: BiometricSettingsRepository,
     deviceEntryBiometricAuthInteractor: DeviceEntryBiometricAuthInteractor,
-    deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
-    keyguardBypassInteractor: KeyguardBypassInteractor,
     deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
     deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
     fingerprintPropertyRepository: FingerprintPropertyRepository,
@@ -83,7 +80,12 @@
                 emit(recentPowerButtonPressThresholdMs * -1L - 1L)
             }
 
-    private val playHapticsOnDeviceEntry: Flow<Boolean> =
+    /**
+     * Indicates when success haptics should play when the device is entered. This always occurs on
+     * successful fingerprint authentications. It also occurs on successful face authentication but
+     * only if the lockscreen is bypassed.
+     */
+    val playSuccessHapticOnDeviceEntry: Flow<Unit> =
         deviceEntrySourceInteractor.deviceEntryFromBiometricSource
             .sample(
                 combine(
@@ -93,29 +95,17 @@
                     ::Triple,
                 )
             )
-            .map { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) ->
+            .filter { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) ->
                 val sideFpsAllowsHaptic =
                     !powerButtonDown &&
                         systemClock.uptimeMillis() - lastPowerButtonWakeup >
                             recentPowerButtonPressThresholdMs
                 val allowHaptic = !sideFpsEnrolled || sideFpsAllowsHaptic
                 if (!allowHaptic) {
-                    logger.d(
-                        "Skip success entry haptic from power button. Recent power button press or button is down."
-                    )
+                    logger.d("Skip success haptic. Recent power button press or button is down.")
                 }
                 allowHaptic
             }
-
-    private val playHapticsOnFaceAuthSuccessAndBypassDisabled: Flow<Boolean> =
-        deviceEntryFaceAuthInteractor.isAuthenticated
-            .filter { it }
-            .sample(keyguardBypassInteractor.isBypassAvailable)
-            .map { !it }
-
-    val playSuccessHaptic: Flow<Unit> =
-        merge(playHapticsOnDeviceEntry, playHapticsOnFaceAuthSuccessAndBypassDisabled)
-            .filter { it }
             // map to Unit
             .map {}
             .dumpWhileCollecting("playSuccessHaptic")
@@ -123,7 +113,7 @@
     private val playErrorHapticForBiometricFailure: Flow<Unit> =
         merge(
                 deviceEntryFingerprintAuthInteractor.fingerprintFailure,
-                deviceEntryBiometricAuthInteractor.faceFailure,
+                deviceEntryBiometricAuthInteractor.faceOnlyFaceFailure,
             )
             // map to Unit
             .map {}
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
index 9b181be..908d0aa 100644
--- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
@@ -16,8 +16,15 @@
 
 package com.android.systemui.display
 
+import android.hardware.display.DisplayManager
+import android.os.Handler
+import com.android.app.displaylib.DisplayLibBackground
+import com.android.app.displaylib.DisplayLibComponent
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.app.displaylib.createDisplayLibComponent
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.data.repository.DeviceStateRepository
 import com.android.systemui.display.data.repository.DeviceStateRepositoryImpl
 import com.android.systemui.display.data.repository.DisplayRepository
@@ -28,6 +35,7 @@
 import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepositoryImpl
 import com.android.systemui.display.data.repository.FocusedDisplayRepository
 import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl
+import com.android.systemui.display.data.repository.PerDisplayRepoDumpHelper
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractorImpl
 import com.android.systemui.display.domain.interactor.DisplayWindowPropertiesInteractorModule
@@ -40,9 +48,11 @@
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
 
 /** Module binding display related classes. */
-@Module(includes = [DisplayWindowPropertiesInteractorModule::class])
+@Module(includes = [DisplayWindowPropertiesInteractorModule::class, DisplayLibModule::class])
 interface DisplayModule {
     @Binds
     fun bindConnectedDisplayInteractor(
@@ -73,6 +83,13 @@
         impl: DisplayWindowPropertiesRepositoryImpl
     ): DisplayWindowPropertiesRepository
 
+    @Binds
+    fun dumpRegistrationLambda(helper: PerDisplayRepoDumpHelper): PerDisplayRepository.InitCallback
+
+    @Binds
+    @DisplayLibBackground
+    fun bindDisplayLibBackground(@Background bgScope: CoroutineScope): CoroutineScope
+
     companion object {
         @Provides
         @SysUISingleton
@@ -103,3 +120,31 @@
         }
     }
 }
+
+/** Module to bind the DisplayRepository from displaylib to the systemui dagger graph. */
+@Module
+object DisplayLibModule {
+    @Provides
+    @SysUISingleton
+    fun displayLibComponent(
+        displayManager: DisplayManager,
+        @Background backgroundHandler: Handler,
+        @Background bgApplicationScope: CoroutineScope,
+        @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
+    ): DisplayLibComponent {
+        return createDisplayLibComponent(
+            displayManager,
+            backgroundHandler,
+            bgApplicationScope,
+            backgroundCoroutineDispatcher,
+        )
+    }
+
+    @Provides
+    @SysUISingleton
+    fun providesDisplayRepositoryFromLib(
+        displayLibComponent: DisplayLibComponent
+    ): com.android.app.displaylib.DisplayRepository {
+        return displayLibComponent.displayRepository
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt b/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt
deleted file mode 100644
index 626a68f..0000000
--- a/packages/SystemUI/src/com/android/systemui/display/data/DisplayEvent.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.display.data
-
-sealed interface DisplayEvent {
-    val displayId: Int
-    data class Added(override val displayId: Int) : DisplayEvent
-    data class Removed(override val displayId: Int) : DisplayEvent
-    data class Changed(override val displayId: Int) : DisplayEvent
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
index 29044d0..f4db2cc 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
@@ -27,7 +27,7 @@
 import android.hardware.devicestate.DeviceStateManager
 import android.hardware.devicestate.feature.flags.Flags as DeviceStateManagerFlags
 import com.android.internal.R
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
 import java.util.concurrent.Executor
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt
index cef45dc..3c554b9 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt
@@ -19,7 +19,7 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.util.DisplayMetrics
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 721d116..051fe7e 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -17,112 +17,30 @@
 package com.android.systemui.display.data.repository
 
 import android.annotation.SuppressLint
-import android.hardware.display.DisplayManager
-import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED
-import android.hardware.display.DisplayManager.DisplayListener
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_ADDED
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
-import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_REMOVED
-import android.os.Handler
-import android.util.Log
-import android.view.Display
 import android.view.IWindowManager
-import com.android.app.tracing.FlowTracing.traceEach
-import com.android.app.tracing.traceSection
+import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.display.data.DisplayEvent
 import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.Compile
-import com.android.systemui.util.kotlin.pairwiseBy
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asFlow
 import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.scan
 import kotlinx.coroutines.flow.stateIn
 
 /** Repository for providing access to display related information and events. */
-interface DisplayRepository {
-    /** Display change event indicating a change to the given displayId has occurred. */
-    val displayChangeEvent: Flow<Int>
-
-    /** Display addition event indicating a new display has been added. */
-    val displayAdditionEvent: Flow<Display?>
-
-    /** Display removal event indicating a display has been removed. */
-    val displayRemovalEvent: Flow<Int>
+interface DisplayRepository : DisplayRepositoryFromLib {
 
     /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
     val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
-
-    /**
-     * Provides the current set of displays.
-     *
-     * Consider using [displayIds] if only the [Display.getDisplayId] is needed.
-     */
-    val displays: StateFlow<Set<Display>>
-
-    /**
-     * Provides the current set of display ids.
-     *
-     * Note that it is preferred to use this instead of [displays] if only the
-     * [Display.getDisplayId] is needed.
-     */
-    val displayIds: StateFlow<Set<Int>>
-
-    /**
-     * Pending display id that can be enabled/disabled.
-     *
-     * When `null`, it means there is no pending display waiting to be enabled.
-     */
-    val pendingDisplay: Flow<PendingDisplay?>
-
-    /** Whether the default display is currently off. */
-    val defaultDisplayOff: Flow<Boolean>
-
-    /**
-     * Given a display ID int, return the corresponding Display object, or null if none exist.
-     *
-     * This method is guaranteed to not result in any binder call.
-     */
-    fun getDisplay(displayId: Int): Display? =
-        displays.value.firstOrNull { it.displayId == displayId }
-
-    /** Represents a connected display that has not been enabled yet. */
-    interface PendingDisplay {
-        /** Id of the pending display. */
-        val id: Int
-
-        /** Enables the display, making it available to the system. */
-        suspend fun enable()
-
-        /**
-         * Ignores the pending display. When called, this specific display id doesn't appear as
-         * pending anymore until the display is disconnected and reconnected again.
-         */
-        suspend fun ignore()
-
-        /** Disables the display, making it unavailable to the system. */
-        suspend fun disable()
-    }
 }
 
 @SysUISingleton
@@ -130,310 +48,11 @@
 class DisplayRepositoryImpl
 @Inject
 constructor(
-    private val displayManager: DisplayManager,
     private val commandQueue: CommandQueue,
     private val windowManager: IWindowManager,
-    @Background backgroundHandler: Handler,
     @Background bgApplicationScope: CoroutineScope,
-    @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
-) : DisplayRepository {
-    private val allDisplayEvents: Flow<DisplayEvent> =
-        conflatedCallbackFlow {
-                val callback =
-                    object : DisplayListener {
-                        override fun onDisplayAdded(displayId: Int) {
-                            trySend(DisplayEvent.Added(displayId))
-                        }
-
-                        override fun onDisplayRemoved(displayId: Int) {
-                            trySend(DisplayEvent.Removed(displayId))
-                        }
-
-                        override fun onDisplayChanged(displayId: Int) {
-                            trySend(DisplayEvent.Changed(displayId))
-                        }
-                    }
-                displayManager.registerDisplayListener(
-                    callback,
-                    backgroundHandler,
-                    EVENT_TYPE_DISPLAY_ADDED or
-                        EVENT_TYPE_DISPLAY_CHANGED or
-                        EVENT_TYPE_DISPLAY_REMOVED,
-                )
-                awaitClose { displayManager.unregisterDisplayListener(callback) }
-            }
-            .onStart { emit(DisplayEvent.Changed(Display.DEFAULT_DISPLAY)) }
-            .debugLog("allDisplayEvents")
-            .flowOn(backgroundCoroutineDispatcher)
-
-    override val displayChangeEvent: Flow<Int> =
-        allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
-
-    override val displayRemovalEvent: Flow<Int> =
-        allDisplayEvents.filterIsInstance<DisplayEvent.Removed>().map { it.displayId }
-
-    // This is necessary because there might be multiple displays, and we could
-    // have missed events for those added before this process or flow started.
-    // Note it causes a binder call from the main thread (it's traced).
-    private val initialDisplays: Set<Display> =
-        traceSection("$TAG#initialDisplays") { displayManager.displays?.toSet() ?: emptySet() }
-    private val initialDisplayIds = initialDisplays.map { display -> display.displayId }.toSet()
-
-    /** Propagate to the listeners only enabled displays */
-    private val enabledDisplayIds: StateFlow<Set<Int>> =
-        allDisplayEvents
-            .scan(initial = initialDisplayIds) { previousIds: Set<Int>, event: DisplayEvent ->
-                val id = event.displayId
-                when (event) {
-                    is DisplayEvent.Removed -> previousIds - id
-                    is DisplayEvent.Added,
-                    is DisplayEvent.Changed -> previousIds + id
-                }
-            }
-            .distinctUntilChanged()
-            .debugLog("enabledDisplayIds")
-            .stateIn(bgApplicationScope, SharingStarted.WhileSubscribed(), initialDisplayIds)
-
-    private val defaultDisplay by lazy {
-        getDisplayFromDisplayManager(Display.DEFAULT_DISPLAY)
-            ?: error("Unable to get default display.")
-    }
-
-    /**
-     * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
-     *
-     * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
-     */
-    private val enabledDisplays: StateFlow<Set<Display>> =
-        enabledDisplayIds
-            .mapElementsLazily { displayId -> getDisplayFromDisplayManager(displayId) }
-            .onEach {
-                if (it.isEmpty()) Log.wtf(TAG, "No enabled displays. This should never happen.")
-            }
-            .flowOn(backgroundCoroutineDispatcher)
-            .debugLog("enabledDisplays")
-            .stateIn(
-                bgApplicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                // This triggers a single binder call on the UI thread per process. The
-                // alternative would be to use sharedFlows, but they are prohibited due to
-                // performance concerns.
-                // Ultimately, this is a trade-off between a one-time UI thread binder call and
-                // the constant overhead of sharedFlows.
-                initialValue = initialDisplays,
-            )
-
-    /**
-     * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
-     *
-     * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
-     */
-    override val displays: StateFlow<Set<Display>> = enabledDisplays
-
-    override val displayIds: StateFlow<Set<Int>> = enabledDisplayIds
-
-    /**
-     * Implementation that maps from [displays], instead of [allDisplayEvents] for 2 reasons:
-     * 1. Guarantee that it emits __after__ [displays] emitted. This way it is guaranteed that
-     *    calling [getDisplay] for the newly added display will be non-null.
-     * 2. Reuse the existing instance of [Display] without a new call to [DisplayManager].
-     */
-    override val displayAdditionEvent: Flow<Display?> =
-        displays
-            .pairwiseBy { previousDisplays, currentDisplays -> currentDisplays - previousDisplays }
-            .flatMapLatest { it.asFlow() }
-
-    val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
-    private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds")
-
-    private fun getInitialConnectedDisplays(): Set<Int> =
-        traceSection("$TAG#getInitialConnectedDisplays") {
-            displayManager
-                .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
-                .map { it.displayId }
-                .toSet()
-                .also {
-                    if (DEBUG) {
-                        Log.d(TAG, "getInitialConnectedDisplays: $it")
-                    }
-                }
-        }
-
-    /* keeps connected displays until they are disconnected. */
-    private val connectedDisplayIds: StateFlow<Set<Int>> =
-        conflatedCallbackFlow {
-                val connectedIds = getInitialConnectedDisplays().toMutableSet()
-                val callback =
-                    object : DisplayConnectionListener {
-                        override fun onDisplayConnected(id: Int) {
-                            if (DEBUG) {
-                                Log.d(TAG, "display with id=$id connected.")
-                            }
-                            connectedIds += id
-                            _ignoredDisplayIds.value -= id
-                            trySend(connectedIds.toSet())
-                        }
-
-                        override fun onDisplayDisconnected(id: Int) {
-                            connectedIds -= id
-                            if (DEBUG) {
-                                Log.d(TAG, "display with id=$id disconnected.")
-                            }
-                            _ignoredDisplayIds.value -= id
-                            trySend(connectedIds.toSet())
-                        }
-                    }
-                trySend(connectedIds.toSet())
-                displayManager.registerDisplayListener(
-                    callback,
-                    backgroundHandler,
-                    /* eventFlags */ 0,
-                    DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED,
-                )
-                awaitClose { displayManager.unregisterDisplayListener(callback) }
-            }
-            .distinctUntilChanged()
-            .debugLog("connectedDisplayIds")
-            .stateIn(
-                bgApplicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                // The initial value is set to empty, but connected displays are gathered as soon as
-                // the flow starts being collected. This is to ensure the call to get displays (an
-                // IPC) happens in the background instead of when this object
-                // is instantiated.
-                initialValue = emptySet(),
-            )
-
-    private val connectedExternalDisplayIds: Flow<Set<Int>> =
-        connectedDisplayIds
-            .map { connectedDisplayIds ->
-                traceSection("$TAG#filteringExternalDisplays") {
-                    connectedDisplayIds
-                        .filter { id -> getDisplayType(id) == Display.TYPE_EXTERNAL }
-                        .toSet()
-                }
-            }
-            .flowOn(backgroundCoroutineDispatcher)
-            .debugLog("connectedExternalDisplayIds")
-
-    private fun getDisplayType(displayId: Int): Int? =
-        traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type }
-
-    private fun getDisplayFromDisplayManager(displayId: Int): Display? =
-        traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) }
-
-    /**
-     * Pending displays are the ones connected, but not enabled and not ignored.
-     *
-     * A connected display is ignored after the user makes the decision to use it or not. For now,
-     * the initial decision from the user is final and not reversible.
-     */
-    private val pendingDisplayIds: Flow<Set<Int>> =
-        combine(enabledDisplayIds, connectedExternalDisplayIds, ignoredDisplayIds) {
-                enabledDisplaysIds,
-                connectedExternalDisplayIds,
-                ignoredDisplayIds ->
-                if (DEBUG) {
-                    Log.d(
-                        TAG,
-                        "combining enabled=$enabledDisplaysIds, " +
-                            "connectedExternalDisplayIds=$connectedExternalDisplayIds, " +
-                            "ignored=$ignoredDisplayIds",
-                    )
-                }
-                connectedExternalDisplayIds - enabledDisplaysIds - ignoredDisplayIds
-            }
-            .debugLog("allPendingDisplayIds")
-
-    /** Which display id should be enabled among the pending ones. */
-    private val pendingDisplayId: Flow<Int?> =
-        pendingDisplayIds.map { it.maxOrNull() }.distinctUntilChanged().debugLog("pendingDisplayId")
-
-    override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?> =
-        pendingDisplayId
-            .map { displayId ->
-                val id = displayId ?: return@map null
-                object : DisplayRepository.PendingDisplay {
-                    override val id = id
-
-                    override suspend fun enable() {
-                        traceSection("DisplayRepository#enable($id)") {
-                            if (DEBUG) {
-                                Log.d(TAG, "Enabling display with id=$id")
-                            }
-                            displayManager.enableConnectedDisplay(id)
-                        }
-                        // After the display has been enabled, it is automatically ignored.
-                        ignore()
-                    }
-
-                    override suspend fun ignore() {
-                        traceSection("DisplayRepository#ignore($id)") {
-                            _ignoredDisplayIds.value += id
-                        }
-                    }
-
-                    override suspend fun disable() {
-                        ignore()
-                        traceSection("DisplayRepository#disable($id)") {
-                            if (DEBUG) {
-                                Log.d(TAG, "Disabling display with id=$id")
-                            }
-                            displayManager.disableConnectedDisplay(id)
-                        }
-                    }
-                }
-            }
-            .debugLog("pendingDisplay")
-
-    override val defaultDisplayOff: Flow<Boolean> =
-        displayChangeEvent
-            .filter { it == Display.DEFAULT_DISPLAY }
-            .map { defaultDisplay.state == Display.STATE_OFF }
-            .distinctUntilChanged()
-
-    private fun <T> Flow<T>.debugLog(flowName: String): Flow<T> {
-        return if (DEBUG) {
-            traceEach(flowName, logcat = true, traceEmissionCount = true)
-        } else {
-            this
-        }
-    }
-
-    /**
-     * Maps a set of T to a set of V, minimizing the number of `createValue` calls taking into
-     * account the diff between each root flow emission.
-     *
-     * This is needed to minimize the number of [getDisplayFromDisplayManager] in this class. Note
-     * that if the [createValue] returns a null element, it will not be added in the output set.
-     */
-    private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> {
-        data class State<T, V>(
-            val previousSet: Set<T>,
-            // Caches T values from the previousSet that were already converted to V
-            val valueMap: Map<T, V>,
-            val resultSet: Set<V>,
-        )
-
-        val emptyInitialState = State(emptySet<T>(), emptyMap(), emptySet<V>())
-        return this.scan(emptyInitialState) { state, currentSet ->
-                if (currentSet == state.previousSet) {
-                    state
-                } else {
-                    val removed = state.previousSet - currentSet
-                    val added = currentSet - state.previousSet
-                    val newMap = state.valueMap.toMutableMap()
-
-                    added.forEach { key -> createValue(key)?.let { newMap[key] = it } }
-                    removed.forEach { key -> newMap.remove(key) }
-
-                    val resultSet = newMap.values.toSet()
-                    State(currentSet, newMap, resultSet)
-                }
-            }
-            .filter { it != emptyInitialState }
-            .map { it.resultSet }
-    }
+    private val displayRepositoryFromLib: com.android.app.displaylib.DisplayRepository,
+) : DisplayRepositoryFromLib by displayRepositoryFromLib, DisplayRepository {
 
     private val decorationEvents: Flow<Event> = callbackFlow {
         val callback =
@@ -487,20 +106,5 @@
 
     private companion object {
         const val TAG = "DisplayRepository"
-        val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG
     }
 }
-
-/** Used to provide default implementations for all methods. */
-private interface DisplayConnectionListener : DisplayListener {
-
-    override fun onDisplayConnected(id: Int) {}
-
-    override fun onDisplayDisconnected(id: Int) {}
-
-    override fun onDisplayAdded(id: Int) {}
-
-    override fun onDisplayRemoved(id: Int) {}
-
-    override fun onDisplayChanged(id: Int) {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
index 083191c..86c9d84c 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.display.data.repository
 
+import com.android.app.displaylib.PerDisplayRepository
+
+// TODO b/401305290 - move to displaylib
 class FakePerDisplayRepository<T> : PerDisplayRepository<T> {
 
     private val instances = mutableMapOf<Int, T>()
@@ -32,9 +35,6 @@
         return instances[displayId]
     }
 
-    override val displayIds: Set<Int>
-        get() = instances.keys
-
     override val debugName: String
         get() = "FakePerDisplayRepository"
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
new file mode 100644
index 0000000..efbae5d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.display.data.repository
+
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.DumpableFromToString
+import javax.inject.Inject
+
+/** Helper class to register PerDisplayRepository in the dump manager in SystemUI. */
+@SysUISingleton
+class PerDisplayRepoDumpHelper @Inject constructor(private val dumpManager: DumpManager) :
+    PerDisplayRepository.InitCallback {
+    /**
+     * Registers PerDisplayRepository in the dump manager.
+     *
+     * The repository will be identified by the given debug name.
+     */
+    override fun onInit(debugName: String, instance: Any) {
+        dumpManager.registerNormalDumpable(
+            "PerDisplayRepository-$debugName",
+            DumpableFromToString(instance),
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
deleted file mode 100644
index d27e33e..0000000
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.display.data.repository
-
-import android.util.Log
-import android.view.Display
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.app.tracing.traceSection
-import com.android.systemui.Dumpable
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dump.DumpManager
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import java.io.PrintWriter
-import java.util.concurrent.ConcurrentHashMap
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collectLatest
-
-/**
- * Used to create instances of type `T` for a specific display.
- *
- * This is useful for resources or objects that need to be managed independently for each connected
- * display (e.g., UI state, rendering contexts, or display-specific configurations).
- *
- * Note that in most cases this can be implemented by a simple `@AssistedFactory` with `displayId`
- * parameter
- *
- * ```kotlin
- * class SomeType @AssistedInject constructor(@Assisted displayId: Int,..)
- *      @AssistedFactory
- *      interface Factory {
- *         fun create(displayId: Int): SomeType
- *      }
- *  }
- * ```
- *
- * Then it can be used to create a [PerDisplayRepository] as follows:
- * ```kotlin
- * // Injected:
- * val repositoryFactory: PerDisplayRepositoryImpl.Factory
- * val instanceFactory: PerDisplayRepositoryImpl.Factory
- * // repository creation:
- * repositoryFactory.create(instanceFactory::create)
- * ```
- *
- * @see PerDisplayRepository For how to retrieve and manage instances created by this factory.
- */
-fun interface PerDisplayInstanceProvider<T> {
-    /** Creates an instance for a display. */
-    fun createInstance(displayId: Int): T?
-}
-
-/**
- * Extends [PerDisplayInstanceProvider], adding support for destroying the instance.
- *
- * This is useful for releasing resources associated with a display when it is disconnected or when
- * the per-display instance is no longer needed.
- */
-interface PerDisplayInstanceProviderWithTeardown<T> : PerDisplayInstanceProvider<T> {
-    /** Destroys a previously created instance of `T` forever. */
-    fun destroyInstance(instance: T)
-}
-
-/**
- * Provides access to per-display instances of type `T`.
- *
- * Acts as a repository, managing the caching and retrieval of instances created by a
- * [PerDisplayInstanceProvider]. It ensures that only one instance of `T` exists per display ID.
- */
-interface PerDisplayRepository<T> {
-    /** Gets the cached instance or create a new one for a given display. */
-    operator fun get(displayId: Int): T?
-
-    /** List of display ids for which this repository has an instance. */
-    val displayIds: Set<Int>
-
-    /** Debug name for this repository, mainly for tracing and logging. */
-    val debugName: String
-}
-
-/**
- * Default implementation of [PerDisplayRepository].
- *
- * This class manages a cache of per-display instances of type `T`, creating them using a provided
- * [PerDisplayInstanceProvider] and optionally tearing them down using a
- * [PerDisplayInstanceProviderWithTeardown] when displays are disconnected.
- *
- * It listens to the [DisplayRepository] to detect when displays are added or removed, and
- * automatically manages the lifecycle of the per-display instances.
- *
- * Note that this is a [PerDisplayStoreImpl] 2.0 that doesn't require [CoreStartable] bindings,
- * providing all args in the constructor.
- */
-class PerDisplayInstanceRepositoryImpl<T>
-@AssistedInject
-constructor(
-    @Assisted override val debugName: String,
-    @Assisted private val instanceProvider: PerDisplayInstanceProvider<T>,
-    @Background private val backgroundApplicationScope: CoroutineScope,
-    private val displayRepository: DisplayRepository,
-    private val dumpManager: DumpManager,
-) : PerDisplayRepository<T>, Dumpable {
-
-    private val perDisplayInstances = ConcurrentHashMap<Int, T?>()
-
-    init {
-        backgroundApplicationScope.launch("$debugName#start") { start() }
-    }
-
-    override val displayIds: Set<Int>
-        get() = perDisplayInstances.keys
-
-    private suspend fun start() {
-        dumpManager.registerNormalDumpable("PerDisplayRepository-${debugName}", this)
-        displayRepository.displayIds.collectLatest { displayIds ->
-            val toRemove = perDisplayInstances.keys - displayIds
-            toRemove.forEach { displayId ->
-                Log.d(TAG, "<$debugName> destroying instance for displayId=$displayId.")
-                perDisplayInstances.remove(displayId)?.let { instance ->
-                    (instanceProvider as? PerDisplayInstanceProviderWithTeardown)?.destroyInstance(
-                        instance
-                    )
-                }
-            }
-        }
-    }
-
-    override fun get(displayId: Int): T? {
-        if (displayRepository.getDisplay(displayId) == null) {
-            Log.e(TAG, "<$debugName: Display with id $displayId doesn't exist.")
-            return null
-        }
-
-        // If it doesn't exist, create it and put it in the map.
-        return perDisplayInstances.computeIfAbsent(displayId) { key ->
-            Log.d(TAG, "<$debugName> creating instance for displayId=$key, as it wasn't available.")
-            val instance =
-                traceSection({ "creating instance of $debugName for displayId=$key" }) {
-                    instanceProvider.createInstance(key)
-                }
-            if (instance == null) {
-                Log.e(
-                    TAG,
-                    "<$debugName> returning null because createInstance($key) returned null.",
-                )
-            }
-            instance
-        }
-    }
-
-    @AssistedFactory
-    interface Factory<T> {
-        fun create(
-            debugName: String,
-            instanceProvider: PerDisplayInstanceProvider<T>,
-        ): PerDisplayInstanceRepositoryImpl<T>
-    }
-
-    companion object {
-        private const val TAG = "PerDisplayInstanceRepo"
-    }
-
-    override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println(perDisplayInstances)
-    }
-}
-
-/**
- * Provides an instance of a given class **only** for the default display, even if asked for another
- * display.
- *
- * This is useful in case of **flag refactors**: it can be provided instead of an instance of
- * [PerDisplayInstanceRepositoryImpl] when a flag related to multi display refactoring is off.
- *
- * Note that this still requires all instances to be provided by a [PerDisplayInstanceProvider]. If
- * you want to provide an existing instance instead for the default display, either implement it in
- * a custom [PerDisplayInstanceProvider] (e.g. inject it in the constructor and return it if the
- * displayId is zero), or use [SingleInstanceRepositoryImpl].
- */
-class DefaultDisplayOnlyInstanceRepositoryImpl<T>(
-    override val debugName: String,
-    private val instanceProvider: PerDisplayInstanceProvider<T>,
-) : PerDisplayRepository<T> {
-    private val lazyDefaultDisplayInstance by lazy {
-        instanceProvider.createInstance(Display.DEFAULT_DISPLAY)
-    }
-    override val displayIds: Set<Int> = setOf(Display.DEFAULT_DISPLAY)
-
-    override fun get(displayId: Int): T? = lazyDefaultDisplayInstance
-}
-
-/**
- * Always returns [instance] for any display.
- *
- * This can be used to provide a single instance based on a flag value during a refactor. Similar to
- * [DefaultDisplayOnlyInstanceRepositoryImpl], but also avoids creating the
- * [PerDisplayInstanceProvider]. This is useful when you want to provide an existing instance only,
- * without even instantiating a [PerDisplayInstanceProvider].
- */
-class SingleInstanceRepositoryImpl<T>(override val debugName: String, private val instance: T) :
-    PerDisplayRepository<T> {
-    override val displayIds: Set<Int> = setOf(Display.DEFAULT_DISPLAY)
-
-    override fun get(displayId: Int): T? = instance
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index 15a3cbd..84f103e 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -18,6 +18,7 @@
 
 import android.companion.virtual.VirtualDeviceManager
 import android.view.Display
+import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.data.repository.DeviceStateRepository
@@ -138,7 +139,8 @@
             .distinctUntilChanged()
             .flowOn(backgroundCoroutineDispatcher)
 
-    private fun DisplayRepository.PendingDisplay.toInteractorPendingDisplay(): PendingDisplay =
+    private fun DisplayRepositoryFromLib.PendingDisplay.toInteractorPendingDisplay():
+        PendingDisplay =
         object : PendingDisplay {
             override suspend fun enable() = this@toInteractorPendingDisplay.enable()
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java
deleted file mode 100644
index c17094b..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.conditions;
-
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener;
-import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.shared.condition.Condition;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import javax.inject.Inject;
-
-/**
- * {@link AssistantAttentionCondition} provides a signal when assistant has the user's attention.
- */
-public class AssistantAttentionCondition extends Condition {
-    private final AssistManager mAssistManager;
-
-    private final VisualQueryAttentionListener mVisualQueryAttentionListener =
-            new VisualQueryAttentionListener() {
-        @Override
-        public void onAttentionGained() {
-            updateCondition(true);
-        }
-
-        @Override
-        public void onAttentionLost() {
-            updateCondition(false);
-        }
-    };
-
-    @Inject
-    public AssistantAttentionCondition(
-            @Application CoroutineScope scope,
-            AssistManager assistManager) {
-        super(scope);
-        mAssistManager = assistManager;
-    }
-
-    @Override
-    protected void start() {
-        mAssistManager.addVisualQueryAttentionListener(mVisualQueryAttentionListener);
-    }
-
-    @Override
-    protected void stop() {
-        mAssistManager.removeVisualQueryAttentionListener(mVisualQueryAttentionListener);
-    }
-
-    @Override
-    public int getStartStrategy() {
-        return START_EAGERLY;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.kt b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.kt
new file mode 100644
index 0000000..838163b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/AssistantAttentionCondition.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams.conditions
+
+import com.android.systemui.assist.AssistManager
+import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.shared.condition.Condition
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** [AssistantAttentionCondition] provides a signal when assistant has the user's attention. */
+class AssistantAttentionCondition
+@Inject
+constructor(@Application scope: CoroutineScope, private val assistManager: AssistManager) :
+    Condition(scope) {
+    private val visualQueryAttentionListener: VisualQueryAttentionListener =
+        object : VisualQueryAttentionListener {
+            override fun onAttentionGained() {
+                updateCondition(true)
+            }
+
+            override fun onAttentionLost() {
+                updateCondition(false)
+            }
+        }
+
+    override suspend fun start() {
+        assistManager.addVisualQueryAttentionListener(visualQueryAttentionListener)
+    }
+
+    public override fun stop() {
+        assistManager.removeVisualQueryAttentionListener(visualQueryAttentionListener)
+    }
+
+    override val startStrategy: Int
+        get() = START_EAGERLY
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
deleted file mode 100644
index fb4ed14..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.dreams.conditions;
-
-import android.app.DreamManager;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.shared.condition.Condition;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import javax.inject.Inject;
-
-/**
- * {@link DreamCondition} provides a signal when a dream begins and ends.
- */
-public class DreamCondition extends Condition {
-    private final DreamManager mDreamManager;
-    private final KeyguardUpdateMonitor mUpdateMonitor;
-
-    private final KeyguardUpdateMonitorCallback mUpdateCallback =
-            new KeyguardUpdateMonitorCallback() {
-                @Override
-                public void onDreamingStateChanged(boolean dreaming) {
-                    updateCondition(dreaming);
-                }
-            };
-
-    @Inject
-    public DreamCondition(
-            @Application CoroutineScope scope,
-            DreamManager dreamManager,
-            KeyguardUpdateMonitor monitor) {
-        super(scope);
-        mDreamManager = dreamManager;
-        mUpdateMonitor = monitor;
-    }
-
-    @Override
-    protected void start() {
-        mUpdateMonitor.registerCallback(mUpdateCallback);
-        updateCondition(mDreamManager.isDreaming());
-    }
-
-    @Override
-    protected void stop() {
-        mUpdateMonitor.removeCallback(mUpdateCallback);
-    }
-
-    @Override
-    public int getStartStrategy() {
-        return START_EAGERLY;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.kt b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.kt
new file mode 100644
index 0000000..6968132
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams.conditions
+
+import android.app.DreamManager
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.shared.condition.Condition
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** [DreamCondition] provides a signal when a dream begins and ends. */
+class DreamCondition
+@Inject
+constructor(
+    @Application scope: CoroutineScope,
+    private val _dreamManager: DreamManager,
+    private val _updateMonitor: KeyguardUpdateMonitor,
+) : Condition(scope) {
+    private val _updateCallback: KeyguardUpdateMonitorCallback =
+        object : KeyguardUpdateMonitorCallback() {
+            override fun onDreamingStateChanged(dreaming: Boolean) {
+                updateCondition(dreaming)
+            }
+        }
+
+    override suspend fun start() {
+        _updateMonitor.registerCallback(_updateCallback)
+        updateCondition(_dreamManager.isDreaming)
+    }
+
+    override fun stop() {
+        _updateMonitor.removeCallback(_updateCallback)
+    }
+
+    override val startStrategy: Int
+        get() = START_EAGERLY
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 15f73ee..a56f07e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -42,9 +42,9 @@
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.pipeline.shared.TileSpec;
 import com.android.systemui.qs.shared.model.TileCategory;
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig;
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy;
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig;
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig;
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy;
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig;
 import com.android.systemui.res.R;
 import com.android.systemui.touch.TouchInsetManager;
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index a7c078f..36b75c6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -61,7 +61,7 @@
     fun startTransitionFromDream() {
         val showGlanceableHub =
             if (communalSettingsInteractor.isV2FlagEnabled()) {
-                communalInteractor.shouldShowCommunal.value
+                communalSettingsInteractor.autoOpenEnabled.value
             } else {
                 communalInteractor.isCommunalEnabled.value &&
                     !keyguardUpdateMonitor.isEncryptedOrLockdown(userTracker.userId)
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt
new file mode 100644
index 0000000..438931a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpableFromToString.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dump
+
+import com.android.systemui.Dumpable
+import java.io.PrintWriter
+
+/** Dumpable implementation that just calls toString() on the instance. */
+class DumpableFromToString<T>(private val instance: T) : Dumpable {
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("$instance")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/PluggedInCondition.kt b/packages/SystemUI/src/com/android/systemui/flags/PluggedInCondition.kt
index dc08570..e5920924 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/PluggedInCondition.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/PluggedInCondition.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.flags
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.statusbar.policy.BatteryController
 import dagger.Lazy
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index cf5c340..f2a10cc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -147,14 +147,14 @@
 import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.settings.SecureSettings;
 
+import dagger.Lazy;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 
 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.
@@ -270,6 +270,16 @@
     private final UserLogoutInteractor mLogoutInteractor;
     private final GlobalActionsInteractor mInteractor;
     private final Lazy<DisplayWindowPropertiesRepository> mDisplayWindowPropertiesRepositoryLazy;
+    private final Handler mHandler;
+
+    private final UserTracker.Callback mOnUserSwitched = new UserTracker.Callback() {
+        @Override
+        public void onBeforeUserSwitching(int newUser) {
+            // Dismiss the dialog as soon as we start switching. This will schedule a message
+            // in a handler so it will be pretty quick.
+            dismissDialog();
+        }
+    };
 
     @VisibleForTesting
     public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -425,6 +435,29 @@
         mInteractor = interactor;
         mDisplayWindowPropertiesRepositoryLazy = displayWindowPropertiesRepository;
 
+        mHandler = new Handler(mMainHandler.getLooper()) {
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MESSAGE_DISMISS:
+                        if (mDialog != null) {
+                            if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
+                                // Hide instantly.
+                                mDialog.hide();
+                                mDialog.dismiss();
+                            } else {
+                                mDialog.dismiss();
+                            }
+                            mDialog = null;
+                        }
+                        break;
+                    case MESSAGE_REFRESH:
+                        refreshSilentMode();
+                        mAdapter.notifyDataSetChanged();
+                        break;
+                }
+            }
+        };
+
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -537,6 +570,7 @@
                 expandable != null ? expandable.dialogTransitionController(
                         new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
                                 INTERACTION_JANK_TAG)) : null;
+        mUserTracker.addCallback(mOnUserSwitched, mBackgroundExecutor);
         if (controller != null) {
             mDialogTransitionAnimator.show(mDialog, controller);
         } else {
@@ -1404,6 +1438,7 @@
         mWindowManagerFuncs.onGlobalActionsHidden();
         mLifecycle.setCurrentState(Lifecycle.State.CREATED);
         mInteractor.onDismissed();
+        mUserTracker.removeCallback(mOnUserSwitched);
     }
 
     /**
@@ -2228,29 +2263,6 @@
         mDialogPressDelay = 0; // ms
     }
 
-    private Handler mHandler = new Handler() {
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_DISMISS:
-                    if (mDialog != null) {
-                        if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
-                            // Hide instantly.
-                            mDialog.hide();
-                            mDialog.dismiss();
-                        } else {
-                            mDialog.dismiss();
-                        }
-                        mDialog = null;
-                    }
-                    break;
-                case MESSAGE_REFRESH:
-                    refreshSilentMode();
-                    mAdapter.notifyDataSetChanged();
-                    break;
-            }
-        }
-    };
-
     private void onAirplaneModeChanged() {
         // Let the service state callbacks handle the state.
         if (mHasTelephony || mAirplaneModeOn == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
index 981a55c..5ebbb46 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
@@ -77,9 +77,9 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.zIndex
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.keyboard.shortcut.ui.model.IconSource
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
  * A selectable surface with no default focus/hover indications.
@@ -235,18 +235,19 @@
         color = color.getDimmedColorIfDisabled(enabled),
         border = border,
         modifier = modifier.semantics { role = Role.Button },
-        interactionsConfig = InteractionsConfig(
-            hoverOverlayColor = MaterialTheme.colorScheme.onSurface,
-            hoverOverlayAlpha = 0.11f,
-            pressedOverlayColor = MaterialTheme.colorScheme.onSurface,
-            pressedOverlayAlpha = 0.15f,
-            focusOutlineColor = MaterialTheme.colorScheme.secondary,
-            focusOutlineStrokeWidth = 3.dp,
-            focusOutlinePadding = 2.dp,
-            surfaceCornerRadius = 28.dp,
-            focusOutlineCornerRadius = 33.dp,
-        ),
-        enabled = enabled
+        interactionsConfig =
+            InteractionsConfig(
+                hoverOverlayColor = MaterialTheme.colorScheme.onSurface,
+                hoverOverlayAlpha = 0.11f,
+                pressedOverlayColor = MaterialTheme.colorScheme.onSurface,
+                pressedOverlayAlpha = 0.15f,
+                focusOutlineColor = MaterialTheme.colorScheme.secondary,
+                focusOutlineStrokeWidth = 3.dp,
+                focusOutlinePadding = 2.dp,
+                surfaceCornerRadius = 28.dp,
+                focusOutlineCornerRadius = 33.dp,
+            ),
+        enabled = enabled,
     ) {
         Row(
             modifier =
@@ -267,7 +268,7 @@
     iconSource: IconSource,
     contentColor: Color,
     text: String?,
-    contentDescription: String?
+    contentDescription: String?,
 ) {
     if (iconSource.imageVector != null) {
         Icon(
@@ -278,8 +279,7 @@
         )
     }
 
-    if (iconSource.imageVector != null && text != null)
-        Spacer(modifier = Modifier.width(8.dp))
+    if (iconSource.imageVector != null && text != null) Spacer(modifier = Modifier.width(8.dp))
 
     if (text != null) {
         Text(
@@ -363,7 +363,7 @@
                     is HoverInteraction.Exit -> hoverInteractions.remove(interaction.enter)
                     is PressInteraction.Press -> pressInteractions.add(interaction)
                     is PressInteraction.Release -> pressInteractions.remove(interaction.press)
-                    is PressInteraction.Cancel -> pressInteractions.add(interaction.press)
+                    is PressInteraction.Cancel -> pressInteractions.remove(interaction.press)
                 }
                 isHovered.value = hoverInteractions.isNotEmpty()
                 isPressed.value = pressInteractions.isNotEmpty()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
index 922bc15..4e7164f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
@@ -21,7 +21,7 @@
 import android.hardware.input.StickyModifierState
 import android.provider.Settings
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt
index 9da9a73..32257df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt
@@ -18,7 +18,7 @@
 
 import android.view.KeyEvent
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.CommandQueue
 import javax.inject.Inject
@@ -29,6 +29,9 @@
 interface KeyEventRepository {
     /** Observable for whether the power button key is pressed/down or not. */
     val isPowerButtonDown: Flow<Boolean>
+
+    /** Observable for when the power button is being pressed but till the duration of long press */
+    val isPowerButtonLongPressed: Flow<Boolean>
 }
 
 @SysUISingleton
@@ -51,6 +54,21 @@
         awaitClose { commandQueue.removeCallback(callback) }
     }
 
+    override val isPowerButtonLongPressed: Flow<Boolean> = conflatedCallbackFlow {
+        val callback =
+            object : CommandQueue.Callbacks {
+                override fun handleSystemKey(event: KeyEvent) {
+                    if (event.keyCode == KeyEvent.KEYCODE_POWER) {
+                        trySendWithFailureLogging(event.action == KeyEvent.ACTION_DOWN
+                                && event.isLongPress, TAG, "updated isPowerButtonLongPressed")
+                    }
+                }
+            }
+        trySendWithFailureLogging(false, TAG, "init isPowerButtonLongPressed")
+        commandQueue.addCallback(callback)
+        awaitClose { commandQueue.removeCallback(callback) }
+    }
+
     companion object {
         private const val TAG = "KeyEventRepositoryImpl"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
index 9949fa5..ec9bbfb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
@@ -32,4 +32,5 @@
     repository: KeyEventRepository,
 ) {
     val isPowerButtonDown = repository.isPowerButtonDown
+    val isPowerButtonLongPressed = repository.isPowerButtonLongPressed
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 7574649..7968508 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -673,7 +673,8 @@
             if (SceneContainerFlag.isEnabled()) {
                 mDeviceEntryInteractorLazy.get().lockNow("doKeyguardTimeout");
             } else if (KeyguardWmStateRefactor.isEnabled()) {
-                mKeyguardServiceShowLockscreenInteractor.onKeyguardServiceDoKeyguardTimeout();
+                mKeyguardServiceShowLockscreenInteractor
+                        .onKeyguardServiceDoKeyguardTimeout(options);
             }
 
             mKeyguardViewMediator.doKeyguardTimeout(options);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 3b85b57..9ade503 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -51,10 +51,10 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.SystemUIAppComponentFactoryBase;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d8fc21a..4755e28 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -134,7 +134,6 @@
 import com.android.keyguard.mediator.ScreenOnCoordinator;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.DejankUtils;
-import com.android.systemui.Dumpable;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.TransitionAnimator;
@@ -194,7 +193,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
@@ -4129,14 +4127,14 @@
 
     private void notifyLockNowCallback() {
         List<LockNowCallback> callbacks;
+
         synchronized (mLockNowCallbacks) {
-            callbacks = new ArrayList<LockNowCallback>(mLockNowCallbacks);
+            callbacks = new ArrayList<>(mLockNowCallbacks);
             mLockNowCallbacks.clear();
         }
-        Iterator<LockNowCallback> iter = callbacks.listIterator();
-        while (iter.hasNext()) {
-            LockNowCallback callback = iter.next();
-            iter.remove();
+
+        for (int i = 0; i < callbacks.size(); i++) {
+            final LockNowCallback callback = callbacks.get(i);
             if (callback.mUserId != mSelectedUserInteractor.getSelectedUserId()) {
                 Log.i(TAG, "Not notifying lockNowCallback due to user mismatch");
                 continue;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 5869274..51b953e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -22,11 +22,14 @@
 import android.view.IRemoteAnimationFinishedCallback
 import android.view.RemoteAnimationTarget
 import android.view.WindowManager
+import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardShowWhileAwakeInteractor
 import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.window.flags.Flags
 import com.android.wm.shell.keyguard.KeyguardTransitions
 import java.util.concurrent.Executor
@@ -46,6 +49,9 @@
     private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
     private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
     private val keyguardTransitions: KeyguardTransitions,
+    private val selectedUserInteractor: SelectedUserInteractor,
+    private val lockPatternUtils: LockPatternUtils,
+    private val keyguardShowWhileAwakeInteractor: KeyguardShowWhileAwakeInteractor,
 ) {
 
     /**
@@ -92,12 +98,23 @@
      *   second timeout).
      */
     private var isKeyguardGoingAway = false
-        private set(value) {
+        private set(goingAway) {
             // TODO(b/278086361): Extricate the keyguard state controller.
-            keyguardStateController.notifyKeyguardGoingAway(value)
-            field = value
+            keyguardStateController.notifyKeyguardGoingAway(goingAway)
+
+            if (goingAway) {
+                keyguardGoingAwayRequestedForUserId = selectedUserInteractor.getSelectedUserId()
+            }
+
+            field = goingAway
         }
 
+    /**
+     * The current user ID when we asked WM to start the keyguard going away animation. This is used
+     * for validation when user switching occurs during unlock.
+     */
+    private var keyguardGoingAwayRequestedForUserId: Int = -1
+
     /** Callback provided by WM to call once we're done with the going away animation. */
     private var goingAwayRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback? = null
 
@@ -171,6 +188,14 @@
         nonApps: Array<RemoteAnimationTarget>,
         finishedCallback: IRemoteAnimationFinishedCallback,
     ) {
+        goingAwayRemoteAnimationFinishedCallback = finishedCallback
+
+        if (maybeStartTransitionIfUserSwitchedDuringGoingAway()) {
+            Log.d(TAG, "User switched during keyguard going away - ending remote animation.")
+            endKeyguardGoingAwayAnimation()
+            return
+        }
+
         // If we weren't expecting the keyguard to be going away, WM triggered this transition.
         if (!isKeyguardGoingAway) {
             // Since WM triggered this, we're likely not transitioning to GONE yet. See if we can
@@ -198,7 +223,6 @@
         }
 
         if (apps.isNotEmpty()) {
-            goingAwayRemoteAnimationFinishedCallback = finishedCallback
             keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
         } else {
             // Nothing to do here if we have no apps, end the animation, which will cancel it and WM
@@ -211,6 +235,7 @@
         // If WM cancelled the animation, we need to end immediately even if we're still using the
         // animation.
         endKeyguardGoingAwayAnimation()
+        maybeStartTransitionIfUserSwitchedDuringGoingAway()
     }
 
     /**
@@ -301,6 +326,29 @@
         }
     }
 
+    /**
+     * If necessary, start a transition to show/hide keyguard in response to a user switch during
+     * keyguard going away.
+     *
+     * Returns [true] if a transition was started, or false if a transition was not necessary.
+     */
+    private fun maybeStartTransitionIfUserSwitchedDuringGoingAway(): Boolean {
+        val currentUser = selectedUserInteractor.getSelectedUserId()
+        if (currentUser != keyguardGoingAwayRequestedForUserId) {
+            if (lockPatternUtils.isSecure(currentUser)) {
+                keyguardShowWhileAwakeInteractor.onSwitchedToSecureUserWhileKeyguardGoingAway()
+            } else {
+                keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
+                    reason = "User switch during keyguard going away, and new user is insecure"
+                )
+            }
+
+            return true
+        } else {
+            return false
+        }
+    }
+
     companion object {
         private val TAG = "WindowManagerLsVis"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
index 07ed194..4086192 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -30,7 +30,7 @@
 import com.android.settingslib.notification.modes.EnableDndDialogMetricsLogger
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
index e2642a0..683c11a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -20,7 +20,7 @@
 import android.content.Context
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 3555f06..a1dafb1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.controls.ControlsServiceInfo
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
index ad79177..01ff0e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.backup.BackupHelper
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserFileManager
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
index 1c9bc9f..10fc4c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
@@ -23,7 +23,7 @@
 import androidx.lifecycle.Observer
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.animation.Expandable
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
index d12c42a..7c33e29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 760adbf..56ea26e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -26,7 +26,7 @@
 import android.util.Log
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 0f5f313..30476b9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -35,7 +35,7 @@
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 4d999df..396f606 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.shared.model.AuthenticationReason
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt
index 7c43092..59e6a08 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.shared.model.DevicePosture
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 affcd33..cd0efda 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
@@ -24,7 +24,7 @@
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.data.repository.FacePropertyRepository
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt
new file mode 100644
index 0000000..16c2d14
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.os.IRemoteCallback
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * Holds an IRemoteCallback along with the current user ID at the time the callback was provided.
+ */
+data class ShowLockscreenCallback(val userId: Int, val remoteCallback: IRemoteCallback)
+
+/** Maintains state related to KeyguardService requests to show the lockscreen. */
+@SysUISingleton
+class KeyguardServiceShowLockscreenRepository @Inject constructor() {
+    val showLockscreenCallbacks = ArrayList<ShowLockscreenCallback>()
+
+    /**
+     * Adds a callback that we'll notify when we show the lockscreen (or affirmatively decide not to
+     * show it).
+     */
+    fun addShowLockscreenCallback(forUser: Int, callback: IRemoteCallback) {
+        synchronized(showLockscreenCallbacks) {
+            showLockscreenCallbacks.add(ShowLockscreenCallback(forUser, callback))
+        }
+    }
+
+    companion object {
+        private const val TAG = "ShowLockscreenRepository"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
index c5a6fa1..63a0286 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
@@ -20,7 +20,7 @@
 import com.android.keyguard.TrustGrantFlags
 import com.android.keyguard.logging.TrustRepositoryLogger
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index ef06a85..f53421d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -20,7 +20,6 @@
 import android.util.Log
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -60,7 +59,6 @@
     private val wakeToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     private val communalSceneInteractor: CommunalSceneInteractor,
-    private val communalInteractor: CommunalInteractor,
 ) :
     TransitionInteractor(
         fromState = KeyguardState.AOD,
@@ -103,14 +101,14 @@
                 )
                 .collect {
                     (
-                        _,
+                        detailedWakefulness,
                         startedStep,
                         canWakeDirectlyToGone,
                     ) ->
                     val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value
                     val biometricUnlockMode = keyguardInteractor.biometricUnlockState.value.mode
                     val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value
-                    val shouldShowCommunal = communalInteractor.shouldShowCommunal.value
+                    val autoOpenCommunal = communalSettingsInteractor.autoOpenEnabled.value
 
                     if (!maybeHandleInsecurePowerGesture()) {
                         val shouldTransitionToLockscreen =
@@ -137,8 +135,12 @@
                             (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen()) ||
                                 (KeyguardWmStateRefactor.isEnabled && canWakeDirectlyToGone)
 
+                        // Avoid transitioning to communal automatically if the device is waking
+                        // up due to motion.
                         val shouldTransitionToCommunal =
-                            communalSettingsInteractor.isV2FlagEnabled() && shouldShowCommunal
+                            communalSettingsInteractor.isV2FlagEnabled() &&
+                                autoOpenCommunal &&
+                                !detailedWakefulness.isAwakeFromMotionOrLift()
 
                         if (shouldTransitionToGone) {
                             // TODO(b/360368320): Adapt for scene framework
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 6f5f662..4aaa1fa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -34,8 +34,9 @@
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode.Companion.isWakeAndUnlock
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakefulnessModel
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.util.kotlin.Utils.Companion.sample
+import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -121,9 +122,10 @@
     private fun shouldTransitionToCommunal(
         shouldShowCommunal: Boolean,
         isCommunalAvailable: Boolean,
+        wakefulness: WakefulnessModel,
     ) =
         if (communalSettingsInteractor.isV2FlagEnabled()) {
-            shouldShowCommunal
+            shouldShowCommunal && !wakefulness.isAwakeFromMotionOrLift()
         } else {
             isCommunalAvailable && dreamManager.canStartDreaming(false)
         }
@@ -148,14 +150,14 @@
         }
 
         scope.launch {
-            powerInteractor.isAwake
+            powerInteractor.detailedWakefulness
                 .debounce(50L)
-                .filterRelevantKeyguardStateAnd { isAwake -> isAwake }
-                .sample(
+                .filterRelevantKeyguardStateAnd { wakefulness -> wakefulness.isAwake() }
+                .sampleCombine(
                     communalInteractor.isCommunalAvailable,
-                    communalInteractor.shouldShowCommunal,
+                    communalSettingsInteractor.autoOpenEnabled,
                 )
-                .collect { (_, isCommunalAvailable, shouldShowCommunal) ->
+                .collect { (detailedWakefulness, isCommunalAvailable, shouldShowCommunal) ->
                     val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value
                     val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value
                     val isKeyguardGoingAway = keyguardInteractor.isKeyguardGoingAway.value
@@ -186,7 +188,11 @@
                     } else if (isKeyguardOccludedLegacy) {
                         startTransitionTo(KeyguardState.OCCLUDED)
                     } else if (
-                        shouldTransitionToCommunal(shouldShowCommunal, isCommunalAvailable)
+                        shouldTransitionToCommunal(
+                            shouldShowCommunal,
+                            isCommunalAvailable,
+                            detailedWakefulness,
+                        )
                     ) {
                         if (!SceneContainerFlag.isEnabled) {
                             transitionToGlanceableHub()
@@ -208,8 +214,8 @@
         scope.launch {
             powerInteractor.detailedWakefulness
                 .filterRelevantKeyguardStateAnd { it.isAwake() }
-                .sample(
-                    communalInteractor.shouldShowCommunal,
+                .sampleCombine(
+                    communalSettingsInteractor.autoOpenEnabled,
                     communalInteractor.isCommunalAvailable,
                     keyguardInteractor.biometricUnlockState,
                     wakeToGoneInteractor.canWakeDirectlyToGone,
@@ -217,7 +223,7 @@
                 )
                 .collect {
                     (
-                        _,
+                        detailedWakefulness,
                         shouldShowCommunal,
                         isCommunalAvailable,
                         biometricUnlockState,
@@ -245,7 +251,11 @@
                                 )
                             }
                         } else if (
-                            shouldTransitionToCommunal(shouldShowCommunal, isCommunalAvailable)
+                            shouldTransitionToCommunal(
+                                shouldShowCommunal,
+                                isCommunalAvailable,
+                                detailedWakefulness,
+                            )
                         ) {
                             if (!SceneContainerFlag.isEnabled) {
                                 transitionToGlanceableHub()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 0fb98ff..3b1b6fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -115,7 +115,7 @@
                 powerInteractor.isAwake
                     .debounce(50L)
                     .filterRelevantKeyguardStateAnd { isAwake -> isAwake }
-                    .sample(communalInteractor.shouldShowCommunal)
+                    .sample(communalSettingsInteractor.autoOpenEnabled)
                     .collect { shouldShowCommunal ->
                         if (shouldShowCommunal) {
                             // This case handles tapping the power button to transition through
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index a01dc02..f8c7a86 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -20,7 +20,6 @@
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -69,7 +68,6 @@
     private val shadeRepository: ShadeRepository,
     powerInteractor: PowerInteractor,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
-    private val communalInteractor: CommunalInteractor,
     private val communalSceneInteractor: CommunalSceneInteractor,
     private val swipeToDismissInteractor: SwipeToDismissInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
@@ -355,7 +353,7 @@
 
     private fun listenForLockscreenToGlanceableHubV2() {
         scope.launch {
-            communalInteractor.shouldShowCommunal
+            communalSettingsInteractor.autoOpenEnabled
                 .filterRelevantKeyguardStateAnd { shouldShow -> shouldShow }
                 .collect {
                     communalSceneInteractor.changeScene(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index ab0efed..02e04aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -226,6 +226,10 @@
         }
 
     fun handleFidgetTap(x: Float, y: Float) {
+        if (!com.android.systemui.Flags.clockFidgetAnimation()) {
+            return
+        }
+
         if (selectedClockSize.value == ClockSizeSetting.DYNAMIC) {
             clockEventController.handleFidgetTap(x, y)
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
index b55bb38..6c08403 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
@@ -17,16 +17,27 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.annotation.SuppressLint
+import android.app.KeyguardManager.LOCK_ON_USER_SWITCH_CALLBACK
+import android.os.Bundle
+import android.os.IRemoteCallback
+import android.os.RemoteException
+import android.util.Log
+import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.data.repository.KeyguardServiceShowLockscreenRepository
+import com.android.systemui.keyguard.data.repository.ShowLockscreenCallback
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.launch
 
 /**
- * Logic around requests by [KeyguardService] to show keyguard right now, even though the device is
- * awake and not going to sleep.
+ * Logic around requests by [KeyguardService] and friends to show keyguard right now, even though
+ * the device is awake and not going to sleep.
  *
  * This can happen if WM#lockNow() is called, if KeyguardService#showDismissibleKeyguard is called
  * because we're folding with "continue using apps on fold" set to "swipe up to continue", or if the
@@ -38,7 +49,28 @@
 @SysUISingleton
 class KeyguardServiceShowLockscreenInteractor
 @Inject
-constructor(@Background val backgroundScope: CoroutineScope) {
+constructor(
+    @Background val backgroundScope: CoroutineScope,
+    private val selectedUserInteractor: SelectedUserInteractor,
+    private val repository: KeyguardServiceShowLockscreenRepository,
+    private val userTracker: UserTracker,
+    private val wmLockscreenVisibilityInteractor: Lazy<WindowManagerLockscreenVisibilityInteractor>,
+    private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+) : CoreStartable {
+
+    override fun start() {
+        backgroundScope.launch {
+            // Whenever we tell ATMS that lockscreen is visible, notify any showLockscreenCallbacks.
+            // This is not the only place we notify the lockNowCallbacks - there are cases where we
+            // decide not to show the lockscreen despite being asked to, and we need to notify the
+            // callback in those cases as well.
+            wmLockscreenVisibilityInteractor.get().lockscreenVisibility.collect { visible ->
+                if (visible) {
+                    notifyShowLockscreenCallbacks()
+                }
+            }
+        }
+    }
 
     /**
      * Emits whenever [KeyguardService] receives a call that indicates we should show the lockscreen
@@ -57,9 +89,38 @@
     /**
      * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
      * the device locked while the screen was on.
+     *
+     * We'll show keyguard, and if provided, save the lock on user switch callback, to notify it
+     * later when we successfully show.
      */
-    fun onKeyguardServiceDoKeyguardTimeout() {
+    fun onKeyguardServiceDoKeyguardTimeout(options: Bundle? = null) {
         backgroundScope.launch {
+            if (options?.getBinder(LOCK_ON_USER_SWITCH_CALLBACK) != null) {
+                val userId = userTracker.userId
+
+                // This callback needs to be invoked after we show the lockscreen (or decide not to
+                // show it) otherwise System UI will crash in 20 seconds, as a security measure.
+                repository.addShowLockscreenCallback(
+                    userId,
+                    IRemoteCallback.Stub.asInterface(
+                        options.getBinder(LOCK_ON_USER_SWITCH_CALLBACK)
+                    ),
+                )
+
+                Log.d(
+                    TAG,
+                    "Showing lockscreen now - setting required callback for user $userId. " +
+                        "SysUI will crash if this callback is not invoked.",
+                )
+
+                // If the keyguard is disabled or suppressed, we'll never actually show the
+                // lockscreen. Notify the callback so we don't crash.
+                if (!keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()) {
+                    Log.d(TAG, "Keyguard is disabled or suppressed, notifying callbacks now.")
+                    notifyShowLockscreenCallbacks()
+                }
+            }
+
             showNowEvents.emit(ShowWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
         }
     }
@@ -74,4 +135,31 @@
             showNowEvents.emit(ShowWhileAwakeReason.FOLDED_WITH_SWIPE_UP_TO_CONTINUE)
         }
     }
+
+    /** Notifies the callbacks that we've either locked, or decided not to lock. */
+    private fun notifyShowLockscreenCallbacks() {
+        var callbacks: MutableList<ShowLockscreenCallback>
+
+        synchronized(repository.showLockscreenCallbacks) {
+            callbacks = ArrayList(repository.showLockscreenCallbacks)
+            repository.showLockscreenCallbacks.clear()
+        }
+
+        callbacks.forEach { callback ->
+            if (callback.userId != selectedUserInteractor.getSelectedUserId()) {
+                Log.i(TAG, "Not notifying lockNowCallback due to user mismatch")
+                return
+            }
+            Log.i(TAG, "Notifying lockNowCallback")
+            try {
+                callback.remoteCallback.sendResult(null)
+            } catch (e: RemoteException) {
+                Log.e(TAG, "Could not issue LockNowCallback sendResult", e)
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "ShowLockscreenInteractor"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
index a8000a56..c67939a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractor.kt
@@ -16,15 +16,20 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.annotation.SuppressLint
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
 
 /** The reason we're showing lockscreen while awake, used for logging. */
 enum class ShowWhileAwakeReason(private val logReason: String) {
@@ -38,6 +43,9 @@
     ),
     KEYGUARD_TIMEOUT_WHILE_SCREEN_ON(
         "Timed out while the screen was kept on, or WM#lockNow() was called."
+    ),
+    SWITCHED_TO_SECURE_USER_WHILE_GOING_AWAY(
+        "User switch to secure user occurred during keyguardGoingAway sequence, so we're locking."
     );
 
     override fun toString(): String {
@@ -68,6 +76,7 @@
 class KeyguardShowWhileAwakeInteractor
 @Inject
 constructor(
+    @Background val backgroundScope: CoroutineScope,
     biometricSettingsRepository: BiometricSettingsRepository,
     keyguardEnabledInteractor: KeyguardEnabledInteractor,
     keyguardServiceShowLockscreenInteractor: KeyguardServiceShowLockscreenInteractor,
@@ -91,6 +100,15 @@
             .filter { reshow -> reshow }
             .map { ShowWhileAwakeReason.KEYGUARD_REENABLED }
 
+    /**
+     * Emits whenever a user switch to a secure user occurs during keyguard going away.
+     *
+     * This is an event flow, hence the SharedFlow.
+     */
+    @SuppressLint("SharedFlowCreation")
+    val switchedToSecureUserDuringGoingAway: MutableSharedFlow<ShowWhileAwakeReason> =
+        MutableSharedFlow()
+
     /** Emits whenever we should show lockscreen while the screen is on, for any reason. */
     val showWhileAwakeEvents: Flow<ShowWhileAwakeReason> =
         merge(
@@ -108,5 +126,15 @@
             keyguardServiceShowLockscreenInteractor.showNowEvents.filter {
                 keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed()
             },
+            switchedToSecureUserDuringGoingAway,
         )
+
+    /** A user switch to a secure user occurred while we were going away. We need to re-lock. */
+    fun onSwitchedToSecureUserWhileKeyguardGoingAway() {
+        backgroundScope.launch {
+            switchedToSecureUserDuringGoingAway.emit(
+                ShowWhileAwakeReason.SWITCHED_TO_SECURE_USER_WHILE_GOING_AWAY
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
index 705eaa2..55534c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
@@ -20,11 +20,14 @@
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
+import android.os.PowerManager
+import android.provider.Settings
 import android.view.accessibility.AccessibilityManager
 import androidx.annotation.VisibleForTesting
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags.doubleTapToSleep
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -36,10 +39,13 @@
 import com.android.systemui.shade.PulsingGestureListener
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
+import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -66,10 +72,13 @@
     private val accessibilityManager: AccessibilityManagerWrapper,
     private val pulsingGestureListener: PulsingGestureListener,
     private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
+    private val secureSettingsRepository: UserAwareSecureSettingsRepository,
+    private val powerManager: PowerManager,
+    private val systemClock: SystemClock,
 ) {
     /** Whether the long-press handling feature should be enabled. */
     val isLongPressHandlingEnabled: StateFlow<Boolean> =
-        if (isFeatureEnabled()) {
+        if (isLongPressFeatureEnabled()) {
                 combine(
                     transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
                     repository.isQuickSettingsVisible,
@@ -85,6 +94,30 @@
                 initialValue = false,
             )
 
+    /** Whether the double tap handling handling feature should be enabled. */
+    val isDoubleTapHandlingEnabled: StateFlow<Boolean> =
+        if (isDoubleTapFeatureEnabled()) {
+                combine(
+                    transitionInteractor.transitionValue(KeyguardState.LOCKSCREEN),
+                    repository.isQuickSettingsVisible,
+                    isDoubleTapSettingEnabled(),
+                ) {
+                    isFullyTransitionedToLockScreen,
+                    isQuickSettingsVisible,
+                    isDoubleTapSettingEnabled ->
+                    isFullyTransitionedToLockScreen == 1f &&
+                        !isQuickSettingsVisible &&
+                        isDoubleTapSettingEnabled
+                }
+            } else {
+                flowOf(false)
+            }
+            .stateIn(
+                scope = scope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false,
+            )
+
     private val _isMenuVisible = MutableStateFlow(false)
     /** Model for whether the menu should be shown. */
     val isMenuVisible: StateFlow<Boolean> =
@@ -116,7 +149,7 @@
     private var delayedHideMenuJob: Job? = null
 
     init {
-        if (isFeatureEnabled()) {
+        if (isLongPressFeatureEnabled()) {
             broadcastDispatcher
                 .broadcastFlow(IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
                 .onEach { hideMenu() }
@@ -175,17 +208,30 @@
 
     /** Notifies that the lockscreen has been double clicked. */
     fun onDoubleClick() {
-        pulsingGestureListener.onDoubleTapEvent()
+        if (isDoubleTapHandlingEnabled.value) {
+            powerManager.goToSleep(systemClock.uptimeMillis())
+        } else {
+            pulsingGestureListener.onDoubleTapEvent()
+        }
+    }
+
+    private fun isDoubleTapSettingEnabled(): Flow<Boolean> {
+        return secureSettingsRepository.boolSetting(Settings.Secure.DOUBLE_TAP_TO_SLEEP)
     }
 
     private fun showSettings() {
         _shouldOpenSettings.value = true
     }
 
-    private fun isFeatureEnabled(): Boolean {
+    private fun isLongPressFeatureEnabled(): Boolean {
         return context.resources.getBoolean(R.bool.long_press_keyguard_customize_lockscreen_enabled)
     }
 
+    private fun isDoubleTapFeatureEnabled(): Boolean {
+        return doubleTapToSleep() &&
+            context.resources.getBoolean(com.android.internal.R.bool.config_supportDoubleTapSleep)
+    }
+
     /** Updates application state to ask to show the menu. */
     private fun showMenu() {
         _isMenuVisible.value = true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index 2b4582a..780b7fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -29,6 +29,7 @@
     private val auditLogger: KeyguardTransitionAuditLogger,
     private val statusBarDisableFlagsInteractor: StatusBarDisableFlagsInteractor,
     private val keyguardStateCallbackInteractor: KeyguardStateCallbackInteractor,
+    private val keyguardServiceShowLockscreenInteractor: KeyguardServiceShowLockscreenInteractor,
 ) : CoreStartable {
 
     override fun start() {
@@ -54,6 +55,7 @@
         auditLogger.start()
         statusBarDisableFlagsInteractor.start()
         keyguardStateCallbackInteractor.start()
+        keyguardServiceShowLockscreenInteractor.start()
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 0b116de..438dff9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -139,7 +139,7 @@
     fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) {
         repository.maxAlpha.value =
             if (supportsAmbientMode) {
-                0.7f
+                0.54f
             } else {
                 1f
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 17e14c3..70a827d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -48,7 +48,7 @@
     /**
      * Updates UI for:
      * - device entry containing view (parent view for the below views)
-     *     - long-press handling view (transparent, no UI)
+     *     - touch handling view (transparent, no UI)
      *     - foreground icon view (lock/unlock/fingerprint)
      *     - background view (optional)
      */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
index 741b149..92b9da679 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
@@ -31,6 +31,37 @@
 object KeyguardPreviewSmartspaceViewBinder {
 
     @JvmStatic
+    fun bind(parentView: View, viewModel: KeyguardPreviewSmartspaceViewModel) {
+        if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
+            val largeDateView =
+                parentView.findViewById<View>(
+                    com.android.systemui.shared.R.id.date_smartspace_view_large
+                )
+            val smallDateView =
+                parentView.findViewById<View>(com.android.systemui.shared.R.id.date_smartspace_view)
+            parentView.repeatWhenAttached {
+                repeatOnLifecycle(Lifecycle.State.STARTED) {
+                    launch("$TAG#viewModel.selectedClockSize") {
+                        viewModel.previewingClockSize.collect {
+                            when (it) {
+                                ClockSizeSetting.DYNAMIC -> {
+                                    smallDateView?.visibility = View.GONE
+                                    largeDateView?.visibility = View.VISIBLE
+                                }
+
+                                ClockSizeSetting.SMALL -> {
+                                    smallDateView?.visibility = View.VISIBLE
+                                    largeDateView?.visibility = View.GONE
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @JvmStatic
     fun bind(
         smartspace: View,
         viewModel: KeyguardPreviewSmartspaceViewModel,
@@ -44,6 +75,7 @@
                             when (it) {
                                 ClockSizeSetting.DYNAMIC ->
                                     viewModel.getLargeClockSmartspaceTopPadding(clockPreviewConfig)
+
                                 ClockSizeSetting.SMALL ->
                                     viewModel.getSmallClockSmartspaceTopPadding(clockPreviewConfig)
                             }
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 45801ba..aeb3270 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
@@ -333,7 +333,7 @@
 
                     if (deviceEntryHapticsInteractor != null && vibratorHelper != null) {
                         launch {
-                            deviceEntryHapticsInteractor.playSuccessHaptic.collect {
+                            deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry.collect {
                                 if (msdlFeedback()) {
                                     msdlPlayer?.playToken(
                                         MSDLToken.UNLOCK,
@@ -474,7 +474,7 @@
                 val transition = blueprintViewModel.currentTransition.value
                 val shouldAnimate = transition != null && transition.config.type.animateNotifChanges
                 if (prevTransition == transition && shouldAnimate) {
-                    logger.w("Skipping; layout during transition")
+                    logger.w("Skipping onNotificationContainerBoundsChanged during transition")
                     return
                 }
 
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 e81d535..5ef2d6f 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
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.clocks.VRectF
 import com.android.systemui.res.R
 import com.android.systemui.shared.R as sharedR
 import kotlinx.coroutines.DisposableHandle
@@ -135,7 +136,7 @@
                                     }
                                 }
 
-                                if (clockBounds == null) return@collect
+                                if (clockBounds == VRectF.ZERO) return@collect
                                 if (isLargeClock) {
                                     val largeDateHeight =
                                         keyguardRootView
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardTouchViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardTouchViewBinder.kt
index 195413a..485e1ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardTouchViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardTouchViewBinder.kt
@@ -75,6 +75,13 @@
 
                     onSingleTap(x, y)
                 }
+
+                override fun onDoubleTapDetected(view: View) {
+                    if (falsingManager.isFalseDoubleTap()) {
+                        return
+                    }
+                    viewModel.onDoubleClick()
+                }
             }
 
         view.repeatWhenAttached {
@@ -90,9 +97,20 @@
                             }
                     }
                 }
+                launch("$TAG#viewModel.isDoubleTapHandlingEnabled") {
+                    viewModel.isDoubleTapHandlingEnabled.collect { isEnabled ->
+                        view.setDoublePressHandlingEnabled(isEnabled)
+                        view.contentDescription =
+                            if (isEnabled) {
+                                view.resources.getString(R.string.accessibility_desc_lock_screen)
+                            } else {
+                                null
+                            }
+                    }
+                }
             }
         }
     }
 
-    private const val TAG = "KeyguardLongPressViewBinder"
+    private const val TAG = "KeyguardTouchViewBinder"
 }
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 242926b..d749e3c 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
@@ -68,6 +68,7 @@
 import com.android.systemui.monet.Style
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockPreviewConfig
+import com.android.systemui.plugins.clocks.ContextExt.getId
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.res.R
@@ -126,6 +127,7 @@
 
     private val displayId = bundle.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY)
     private val display: Display? = displayManager.getDisplay(displayId)
+
     /**
      * Returns a key that should make the KeyguardPreviewRenderer unique and if two of them have the
      * same key they will be treated as the same KeyguardPreviewRenderer. Primary this is used to
@@ -144,6 +146,8 @@
         get() = checkNotNull(host.surfacePackage)
 
     private var smartSpaceView: View? = null
+    private var largeDateView: View? = null
+    private var smallDateView: View? = null
 
     private val disposables = DisposableHandles()
     private var isDestroyed = false
@@ -181,7 +185,7 @@
                     ContextThemeWrapper(context.createDisplayContext(it), context.getTheme())
                 } ?: context
 
-            val rootView = FrameLayout(previewContext)
+            val rootView = ConstraintLayout(previewContext)
 
             setupKeyguardRootView(previewContext, rootView)
 
@@ -252,6 +256,24 @@
 
     fun onClockSizeSelected(clockSize: ClockSizeSetting) {
         smartspaceViewModel.setOverrideClockSize(clockSize)
+        if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
+            when (clockSize) {
+                ClockSizeSetting.DYNAMIC -> {
+                    largeDateView?.post {
+                        smallDateView?.visibility = View.GONE
+                        largeDateView?.visibility = View.VISIBLE
+                    }
+                }
+
+                ClockSizeSetting.SMALL -> {
+                    largeDateView?.post {
+                        smallDateView?.visibility = View.VISIBLE
+                        largeDateView?.visibility = View.GONE
+                    }
+                }
+            }
+            smartSpaceView?.post { smartSpaceView?.visibility = View.GONE }
+        }
     }
 
     fun destroy() {
@@ -280,7 +302,7 @@
      *
      * The end padding is as follows: Below clock padding end
      */
-    private fun setUpSmartspace(previewContext: Context, parentView: ViewGroup) {
+    private fun setUpSmartspace(previewContext: Context, parentView: ConstraintLayout) {
         if (
             !lockscreenSmartspaceController.isEnabled ||
                 !lockscreenSmartspaceController.isDateWeatherDecoupled
@@ -292,40 +314,90 @@
             parentView.removeView(smartSpaceView)
         }
 
-        smartSpaceView =
-            lockscreenSmartspaceController.buildAndConnectDateView(
-                parent = parentView,
-                isLargeClock = false,
-            )
-
-        val topPadding: Int =
-            smartspaceViewModel.getLargeClockSmartspaceTopPadding(
-                ClockPreviewConfig(
-                    previewContext,
-                    getPreviewShadeLayoutWide(display!!),
-                    SceneContainerFlag.isEnabled,
+        if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
+            val cs = ConstraintSet()
+            cs.clone(parentView)
+            cs.apply {
+                val largeClockViewId = previewContext.getId("lockscreen_clock_view_large")
+                val smallClockViewId = previewContext.getId("lockscreen_clock_view")
+                largeDateView =
+                    lockscreenSmartspaceController
+                        .buildAndConnectDateView(parentView, true)
+                        ?.also { view ->
+                            constrainWidth(view.id, ConstraintSet.WRAP_CONTENT)
+                            constrainHeight(view.id, ConstraintSet.WRAP_CONTENT)
+                            connect(view.id, START, largeClockViewId, START)
+                            connect(view.id, ConstraintSet.END, largeClockViewId, ConstraintSet.END)
+                            connect(
+                                view.id,
+                                TOP,
+                                largeClockViewId,
+                                ConstraintSet.BOTTOM,
+                                smartspaceViewModel.getDateWeatherEndPadding(previewContext),
+                            )
+                        }
+                smallDateView =
+                    lockscreenSmartspaceController
+                        .buildAndConnectDateView(parentView, false)
+                        ?.also { view ->
+                            constrainWidth(view.id, ConstraintSet.WRAP_CONTENT)
+                            constrainHeight(view.id, ConstraintSet.WRAP_CONTENT)
+                            connect(
+                                view.id,
+                                START,
+                                smallClockViewId,
+                                ConstraintSet.END,
+                                context.resources.getDimensionPixelSize(
+                                    R.dimen.smartspace_padding_horizontal
+                                ),
+                            )
+                            connect(view.id, TOP, smallClockViewId, TOP)
+                            connect(
+                                view.id,
+                                ConstraintSet.BOTTOM,
+                                smallClockViewId,
+                                ConstraintSet.BOTTOM,
+                            )
+                        }
+                parentView.addView(largeDateView)
+                parentView.addView(smallDateView)
+            }
+            cs.applyTo(parentView)
+        } else {
+            smartSpaceView =
+                lockscreenSmartspaceController.buildAndConnectDateView(
+                    parent = parentView,
+                    isLargeClock = false,
                 )
-            )
-        val startPadding: Int = smartspaceViewModel.getDateWeatherStartPadding(previewContext)
-        val endPadding: Int = smartspaceViewModel.getDateWeatherEndPadding(previewContext)
 
-        smartSpaceView?.let {
-            it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
-            it.isClickable = false
-            it.isInvisible = true
-            parentView.addView(
-                it,
-                FrameLayout.LayoutParams(
-                    FrameLayout.LayoutParams.MATCH_PARENT,
-                    FrameLayout.LayoutParams.WRAP_CONTENT,
-                ),
-            )
+            val topPadding: Int =
+                smartspaceViewModel.getLargeClockSmartspaceTopPadding(
+                    ClockPreviewConfig(
+                        previewContext,
+                        getPreviewShadeLayoutWide(display!!),
+                        SceneContainerFlag.isEnabled,
+                    )
+                )
+            val startPadding: Int = smartspaceViewModel.getDateWeatherStartPadding(previewContext)
+            val endPadding: Int = smartspaceViewModel.getDateWeatherEndPadding(previewContext)
+
+            smartSpaceView?.let {
+                it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
+                it.isClickable = false
+                it.isInvisible = true
+                parentView.addView(
+                    it,
+                    FrameLayout.LayoutParams(
+                        FrameLayout.LayoutParams.MATCH_PARENT,
+                        FrameLayout.LayoutParams.WRAP_CONTENT,
+                    ),
+                )
+            }
+            smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
         }
-
-        smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
     }
 
-    private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
+    private fun setupKeyguardRootView(previewContext: Context, rootView: ConstraintLayout) {
         val keyguardRootView = KeyguardRootView(previewContext, null)
         rootView.addView(
             keyguardRootView,
@@ -341,6 +413,13 @@
 
         if (!shouldHideClock) {
             setUpClock(previewContext, rootView)
+            if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
+                setUpSmartspace(previewContext, keyguardRootView)
+                KeyguardPreviewSmartspaceViewBinder.bind(
+                    keyguardRootView,
+                    smartspaceViewModel,
+                )
+            }
             KeyguardPreviewClockViewBinder.bind(
                 keyguardRootView,
                 clockViewModel,
@@ -354,19 +433,22 @@
             )
         }
 
-        setUpSmartspace(previewContext, rootView)
-
-        smartSpaceView?.let {
-            KeyguardPreviewSmartspaceViewBinder.bind(
-                it,
-                smartspaceViewModel,
-                clockPreviewConfig =
-                    ClockPreviewConfig(
-                        previewContext,
-                        getPreviewShadeLayoutWide(display!!),
-                        SceneContainerFlag.isEnabled,
-                    ),
-            )
+        if (!com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
+            setUpSmartspace(previewContext, keyguardRootView)
+            smartSpaceView?.let {
+                KeyguardPreviewSmartspaceViewBinder.bind(
+                    it,
+                    smartspaceViewModel,
+                    clockPreviewConfig =
+                        ClockPreviewConfig(
+                            previewContext,
+                            getPreviewShadeLayoutWide(display!!),
+                            SceneContainerFlag.isEnabled,
+                            lockId = null,
+                            udfpsTop = null,
+                        ),
+                )
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index c4a7e1e..55fac3c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToAodTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel
@@ -272,6 +273,12 @@
 
     @Binds
     @IntoSet
+    abstract fun glanceableHubToLockscreen(
+        impl: GlanceableHubToLockscreenTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
     abstract fun occludedToGlanceableHub(
         impl: OccludedToGlanceableHubTransitionViewModel
     ): DeviceEntryIconTransition
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 0ccb24a..20fc884 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -61,6 +61,7 @@
     primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel,
     lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel,
     glanceableHubToAodTransitionViewModel: GlanceableHubToAodTransitionViewModel,
+    glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel,
 ) {
     val color: Flow<Int> =
         deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBackground ->
@@ -108,6 +109,7 @@
                             .deviceEntryBackgroundViewAlpha,
                         lockscreenToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
                         glanceableHubToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+                        glanceableHubToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
                     )
                     .merge()
                     .onStart {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index b4b4c82..bcbe666 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Scenes
@@ -49,7 +50,7 @@
     @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
     private val blurFactory: GlanceableHubBlurComponent.Factory,
-) : GlanceableHubTransition {
+) : GlanceableHubTransition, DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow
             .setup(
@@ -102,4 +103,8 @@
 
     val notificationTranslationX: Flow<Float> =
         keyguardTranslationX.map { it.value }.filterNotNull()
+
+    val deviceEntryBackgroundViewAlpha: Flow<Float> = keyguardAlpha
+
+    override val deviceEntryParentViewAlpha: Flow<Float> = keyguardAlpha
 }
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 d4676bc..830afea 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
@@ -31,10 +31,8 @@
 import com.android.systemui.keyguard.domain.interactor.PulseExpansionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
@@ -132,6 +130,8 @@
     private val occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
     private val occludedToDozingTransitionViewModel: OccludedToDozingTransitionViewModel,
     private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+    private val occludedToPrimaryBouncerTransitionViewModel:
+        OccludedToPrimaryBouncerTransitionViewModel,
     private val offToLockscreenTransitionViewModel: OffToLockscreenTransitionViewModel,
     private val primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
     private val primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
@@ -163,43 +163,27 @@
             .onStart { emit(false) }
             .distinctUntilChanged()
 
-    private val isOnLockscreen: Flow<Boolean> =
+    private val isOnOrGoingToLockscreen: Flow<Boolean> =
         combine(
-                keyguardTransitionInteractor.isFinishedIn(LOCKSCREEN).onStart { emit(false) },
-                anyOf(
-                    keyguardTransitionInteractor.isInTransition(Edge.create(to = LOCKSCREEN)),
-                    keyguardTransitionInteractor.isInTransition(Edge.create(from = LOCKSCREEN)),
-                ),
-            ) { onLockscreen, transitioningToOrFromLockscreen ->
-                onLockscreen || transitioningToOrFromLockscreen
+                keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it == 1f },
+                keyguardTransitionInteractor.isInTransition(Edge.create(to = LOCKSCREEN)),
+            ) { onLockscreen, transitioningToLockscreen ->
+                onLockscreen || transitioningToLockscreen
             }
             .distinctUntilChanged()
 
     private val alphaOnShadeExpansion: Flow<Float> =
         combineTransform(
-                anyOf(
-                    keyguardTransitionInteractor.isInTransition(
-                        edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone),
-                        edgeWithoutSceneContainer = Edge.create(from = LOCKSCREEN, to = GONE),
-                    ),
-                    keyguardTransitionInteractor.isInTransition(
-                        edge = Edge.create(from = Overlays.Bouncer, to = LOCKSCREEN),
-                        edgeWithoutSceneContainer =
-                            Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
-                    ),
-                    keyguardTransitionInteractor.isInTransition(
-                        Edge.create(from = LOCKSCREEN, to = DREAMING)
-                    ),
-                    keyguardTransitionInteractor.isInTransition(
-                        Edge.create(from = LOCKSCREEN, to = OCCLUDED)
-                    ),
+                keyguardTransitionInteractor.isInTransition(
+                    edge = Edge.create(from = Overlays.Bouncer, to = LOCKSCREEN),
+                    edgeWithoutSceneContainer = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
                 ),
-                isOnLockscreen,
+                isOnOrGoingToLockscreen,
                 shadeInteractor.qsExpansion,
                 shadeInteractor.shadeExpansion,
-            ) { disabledTransitionRunning, isOnLockscreen, qsExpansion, shadeExpansion ->
+            ) { disabledTransitionRunning, isOnOrGoingToLockscreen, qsExpansion, shadeExpansion ->
                 // Fade out quickly as the shade expands
-                if (isOnLockscreen && !disabledTransitionRunning) {
+                if (isOnOrGoingToLockscreen && !disabledTransitionRunning) {
                     val alpha =
                         1f -
                             MathUtils.constrainedMap(
@@ -288,6 +272,7 @@
                         occludedToAodTransitionViewModel.lockscreenAlpha,
                         occludedToDozingTransitionViewModel.lockscreenAlpha,
                         occludedToLockscreenTransitionViewModel.lockscreenAlpha,
+                        occludedToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
                         offToLockscreenTransitionViewModel.lockscreenAlpha,
                         primaryBouncerToAodTransitionViewModel.lockscreenAlpha,
                         primaryBouncerToGoneTransitionViewModel.lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardTouchHandlingViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardTouchHandlingViewModel.kt
index 1d2edc6..d4e7af4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardTouchHandlingViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardTouchHandlingViewModel.kt
@@ -33,6 +33,9 @@
     /** Whether the long-press handling feature should be enabled. */
     val isLongPressHandlingEnabled: Flow<Boolean> = interactor.isLongPressHandlingEnabled
 
+    /** Whether the double tap handling feature should be enabled. */
+    val isDoubleTapHandlingEnabled: Flow<Boolean> = interactor.isDoubleTapHandlingEnabled
+
     /** Notifies that the user has long-pressed on the lock screen.
      *
      * @param isA11yAction: Whether the action was performed as an a11y action
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 3758afa..9312bca 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
@@ -66,6 +66,9 @@
         transitionAnimation.sharedFlow(
             duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
             onStep = alphaForAnimationStep,
+            // Rapid swipes to bouncer, and may end up skipping intermediate values that would've
+            // caused a complete fade out of lockscreen elements. Ensure it goes to 0f.
+            onFinish = { 0f },
         )
 
     val lockscreenAlpha: Flow<Float> = shortcutsAlpha
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
index f14a5a2..67c3071 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
@@ -45,6 +45,12 @@
             )
             .setupWithoutSceneContainer(edge = Edge.create(OCCLUDED, PRIMARY_BOUNCER))
 
+    /**
+     * Reasserts that lockscreen content should not be visible. It is possible the keyguard alpha is
+     * set to 1f if coming from an expanded shade that collapsed to launch an occluding activity.
+     */
+    val lockscreenAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f)
+
     override val windowBlurRadius: Flow<Float> =
         shadeDependentFlows.transitionFlow(
             flowWhenShadeIsExpanded =
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index faa6c52..a85b9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -327,7 +327,8 @@
     @SysUISingleton
     @KeyguardBlueprintLog
     public static LogBuffer provideKeyguardBlueprintLog(LogBufferFactory factory) {
-        return factory.create("KeyguardBlueprintLog", 100);
+        // TODO(b/389987229): Reduce back to 100
+        return factory.create("KeyguardBlueprintLog", 1000);
     }
 
     /**
@@ -337,7 +338,8 @@
     @SysUISingleton
     @KeyguardClockLog
     public static LogBuffer provideKeyguardClockLog(LogBufferFactory factory) {
-        return factory.create("KeyguardClockLog", 100);
+        // TODO(b/389987229): Reduce back to 100
+        return factory.create("KeyguardClockLog", 1000);
     }
 
     /**
@@ -347,7 +349,8 @@
     @SysUISingleton
     @KeyguardSmallClockLog
     public static LogBuffer provideKeyguardSmallClockLog(LogBufferFactory factory) {
-        return factory.create("KeyguardSmallClockLog", 100);
+        // TODO(b/389987229): Reduce back to 100
+        return factory.create("KeyguardSmallClockLog", 1000);
     }
 
     /**
@@ -357,7 +360,8 @@
     @SysUISingleton
     @KeyguardLargeClockLog
     public static LogBuffer provideKeyguardLargeClockLog(LogBufferFactory factory) {
-        return factory.create("KeyguardLargeClockLog", 100);
+        // TODO(b/389987229): Reduce back to 100
+        return factory.create("KeyguardLargeClockLog", 1000);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java
index b0c2f8c..b27a421 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java
@@ -26,8 +26,7 @@
 import javax.inject.Qualifier;
 
 /**
- * A {@link LogBuffer} for QS tiles messages. It's used exclusively in
- * {@link com.android.systemui.qs.tiles.base.logging.QSTileLogger}
+ * A {@link LogBuffer} for QS tiles messages. It's used exclusively in @link QSTileLogger}.
  */
 @Qualifier
 @Documented
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt
index 9e32dd8..c1658e1 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/AmbientLightModeMonitor.kt
@@ -122,7 +122,7 @@
         }
 
     /** Interface of the ambient light mode callback, which gets triggered when the mode changes. */
-    interface Callback {
+    fun interface Callback {
         fun onChange(@AmbientLightMode mode: Int)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
index 57d7098..ce5fd19 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
@@ -20,7 +20,7 @@
 import android.content.IntentFilter
 import android.os.UserManager
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.shared.condition.Condition
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -35,7 +35,7 @@
 constructor(
     broadcastDispatcher: BroadcastDispatcher,
     private val userManager: UserManager,
-    @Application private val coroutineScope: CoroutineScope,
+    @Background private val coroutineScope: CoroutineScope,
 ) : Condition(coroutineScope) {
     private var job: Job? = null
     private val directBootFlow =
@@ -45,7 +45,7 @@
             .cancellable()
             .distinctUntilChanged()
 
-    override fun start() {
+    override suspend fun start() {
         job = coroutineScope.launch { directBootFlow.collect { updateCondition(it) } }
         updateCondition(!userManager.isUserUnlocked)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java
deleted file mode 100644
index 5ec81a9..0000000
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.java
+++ /dev/null
@@ -1,137 +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.lowlightclock;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.shared.condition.Condition;
-import com.android.systemui.statusbar.commandline.Command;
-import com.android.systemui.statusbar.commandline.CommandRegistry;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import java.io.PrintWriter;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * This condition registers for and fulfills cmd shell commands to force a device into or out of
- * low-light conditions.
- */
-public class ForceLowLightCondition extends Condition {
-    /**
-     * Command root
-     */
-    public static final String COMMAND_ROOT = "low-light";
-    /**
-     * Command for forcing device into low light.
-     */
-    public static final String COMMAND_ENABLE_LOW_LIGHT = "enable";
-
-    /**
-     * Command for preventing a device from entering low light.
-     */
-    public static final String COMMAND_DISABLE_LOW_LIGHT = "disable";
-
-    /**
-     * Command for clearing previously forced low-light conditions.
-     */
-    public static final String COMMAND_CLEAR_LOW_LIGHT = "clear";
-
-    private static final String TAG = "ForceLowLightCondition";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    /**
-     * Default Constructor.
-     *
-     * @param commandRegistry command registry to register commands with.
-     */
-    @Inject
-    public ForceLowLightCondition(
-            @Application CoroutineScope scope,
-            CommandRegistry commandRegistry
-    ) {
-        super(scope, null, true);
-
-        if (DEBUG) {
-            Log.d(TAG, "registering commands");
-        }
-        commandRegistry.registerCommand(COMMAND_ROOT, () -> new Command() {
-            @Override
-            public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) {
-                if (args.size() != 1) {
-                    pw.println("no command specified");
-                    help(pw);
-                    return;
-                }
-
-                final String cmd = args.get(0);
-
-                if (TextUtils.equals(cmd, COMMAND_ENABLE_LOW_LIGHT)) {
-                    logAndPrint(pw, "forcing low light");
-                    updateCondition(true);
-                } else if (TextUtils.equals(cmd, COMMAND_DISABLE_LOW_LIGHT)) {
-                    logAndPrint(pw, "forcing to not enter low light");
-                    updateCondition(false);
-                } else if (TextUtils.equals(cmd, COMMAND_CLEAR_LOW_LIGHT)) {
-                    logAndPrint(pw, "clearing any forced low light");
-                    clearCondition();
-                } else {
-                    pw.println("invalid command");
-                    help(pw);
-                }
-            }
-
-            @Override
-            public void help(@NonNull PrintWriter pw) {
-                pw.println("Usage: adb shell cmd statusbar low-light <cmd>");
-                pw.println("Supported commands:");
-                pw.println("  - enable");
-                pw.println("    forces device into low-light");
-                pw.println("  - disable");
-                pw.println("    forces device to not enter low-light");
-                pw.println("  - clear");
-                pw.println("    clears any previously forced state");
-            }
-
-            private void logAndPrint(PrintWriter pw, String message) {
-                pw.println(message);
-                if (DEBUG) {
-                    Log.d(TAG, message);
-                }
-            }
-        });
-    }
-
-    @Override
-    protected void start() {
-    }
-
-    @Override
-    protected void stop() {
-    }
-
-    @Override
-    public int getStartStrategy() {
-        return START_EAGERLY;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.kt
new file mode 100644
index 0000000..85a9430
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.lowlightclock
+
+import android.text.TextUtils
+import android.util.Log
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.condition.Condition
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * This condition registers for and fulfills cmd shell commands to force a device into or out of
+ * low-light conditions.
+ */
+class ForceLowLightCondition
+@Inject
+constructor(@Background scope: CoroutineScope, commandRegistry: CommandRegistry) :
+    Condition(scope, null, true) {
+    /**
+     * Default Constructor.
+     *
+     * @param commandRegistry command registry to register commands with.
+     */
+    init {
+        if (DEBUG) {
+            Log.d(TAG, "registering commands")
+        }
+        commandRegistry.registerCommand(COMMAND_ROOT) {
+            object : Command {
+                override fun execute(pw: PrintWriter, args: List<String>) {
+                    if (args.size != 1) {
+                        pw.println("no command specified")
+                        help(pw)
+                        return
+                    }
+
+                    val cmd = args[0]
+
+                    if (TextUtils.equals(cmd, COMMAND_ENABLE_LOW_LIGHT)) {
+                        logAndPrint(pw, "forcing low light")
+                        updateCondition(true)
+                    } else if (TextUtils.equals(cmd, COMMAND_DISABLE_LOW_LIGHT)) {
+                        logAndPrint(pw, "forcing to not enter low light")
+                        updateCondition(false)
+                    } else if (TextUtils.equals(cmd, COMMAND_CLEAR_LOW_LIGHT)) {
+                        logAndPrint(pw, "clearing any forced low light")
+                        clearCondition()
+                    } else {
+                        pw.println("invalid command")
+                        help(pw)
+                    }
+                }
+
+                override fun help(pw: PrintWriter) {
+                    pw.println("Usage: adb shell cmd statusbar low-light <cmd>")
+                    pw.println("Supported commands:")
+                    pw.println("  - enable")
+                    pw.println("    forces device into low-light")
+                    pw.println("  - disable")
+                    pw.println("    forces device to not enter low-light")
+                    pw.println("  - clear")
+                    pw.println("    clears any previously forced state")
+                }
+
+                private fun logAndPrint(pw: PrintWriter, message: String) {
+                    pw.println(message)
+                    if (DEBUG) {
+                        Log.d(TAG, message)
+                    }
+                }
+            }
+        }
+    }
+
+    override suspend fun start() {}
+
+    override fun stop() {}
+
+    override val startStrategy: Int
+        get() = START_EAGERLY
+
+    companion object {
+        /** Command root */
+        const val COMMAND_ROOT: String = "low-light"
+
+        /** Command for forcing device into low light. */
+        const val COMMAND_ENABLE_LOW_LIGHT: String = "enable"
+
+        /** Command for preventing a device from entering low light. */
+        const val COMMAND_DISABLE_LOW_LIGHT: String = "disable"
+
+        /** Command for clearing previously forced low-light conditions. */
+        const val COMMAND_CLEAR_LOW_LIGHT: String = "clear"
+
+        private const val TAG = "ForceLowLightCondition"
+        private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java
deleted file mode 100644
index c1a24e7..0000000
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.java
+++ /dev/null
@@ -1,77 +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.lowlightclock;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.shared.condition.Condition;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import javax.inject.Inject;
-
-/**
- * Condition for monitoring when the device enters and exits lowlight mode.
- */
-public class LowLightCondition extends Condition {
-    private final AmbientLightModeMonitor mAmbientLightModeMonitor;
-    private final UiEventLogger mUiEventLogger;
-
-    @Inject
-    public LowLightCondition(@Application CoroutineScope scope,
-            AmbientLightModeMonitor ambientLightModeMonitor,
-            UiEventLogger uiEventLogger) {
-        super(scope);
-        mAmbientLightModeMonitor = ambientLightModeMonitor;
-        mUiEventLogger = uiEventLogger;
-    }
-
-    @Override
-    protected void start() {
-        mAmbientLightModeMonitor.start(this::onLowLightChanged);
-    }
-
-    @Override
-    protected void stop() {
-        mAmbientLightModeMonitor.stop();
-
-        // Reset condition met to false.
-        updateCondition(false);
-    }
-
-    @Override
-    public int getStartStrategy() {
-        // As this condition keeps the lowlight sensor active, it should only run when needed.
-        return START_WHEN_NEEDED;
-    }
-
-    private void onLowLightChanged(int lowLightMode) {
-        if (lowLightMode == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED) {
-            // Ignore undecided mode changes.
-            return;
-        }
-
-        final boolean isLowLight = lowLightMode == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK;
-        if (isLowLight == isConditionMet()) {
-            // No change in condition, don't do anything.
-            return;
-        }
-        mUiEventLogger.log(isLowLight ? LowLightDockEvent.AMBIENT_LIGHT_TO_DARK
-                : LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT);
-        updateCondition(isLowLight);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt
new file mode 100644
index 0000000..34e9a63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.lowlightclock
+
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.condition.Condition
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** Condition for monitoring when the device enters and exits lowlight mode. */
+class LowLightCondition
+@Inject
+constructor(
+    @Background scope: CoroutineScope,
+    private val ambientLightModeMonitor: AmbientLightModeMonitor,
+    private val uiEventLogger: UiEventLogger,
+) : Condition(scope) {
+    override suspend fun start() {
+        ambientLightModeMonitor.start { lowLightMode: Int -> onLowLightChanged(lowLightMode) }
+    }
+
+    override fun stop() {
+        ambientLightModeMonitor.stop()
+
+        // Reset condition met to false.
+        updateCondition(false)
+    }
+
+    override val startStrategy: Int
+        get() = // As this condition keeps the lowlight sensor active, it should only run when
+            // needed.
+            START_WHEN_NEEDED
+
+    private fun onLowLightChanged(lowLightMode: Int) {
+        if (lowLightMode == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED) {
+            // Ignore undecided mode changes.
+            return
+        }
+
+        val isLowLight = lowLightMode == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK
+        if (isLowLight == isConditionMet) {
+            // No change in condition, don't do anything.
+            return
+        }
+        uiEventLogger.log(
+            if (isLowLight) LowLightDockEvent.AMBIENT_LIGHT_TO_DARK
+            else LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT
+        )
+        updateCondition(isLowLight)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java
deleted file mode 100644
index 8157255..0000000
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.lowlightclock;
-
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-
-import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.shared.condition.Condition;
-import com.android.systemui.util.settings.SecureSettings;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import javax.inject.Inject;
-
-/**
- * Condition for monitoring if the screensaver setting is enabled.
- */
-public class ScreenSaverEnabledCondition extends Condition {
-    private static final String TAG = ScreenSaverEnabledCondition.class.getSimpleName();
-
-    private final boolean mScreenSaverEnabledByDefaultConfig;
-    private final SecureSettings mSecureSettings;
-
-    private final ContentObserver mScreenSaverSettingObserver = new ContentObserver(null) {
-        @Override
-        public void onChange(boolean selfChange) {
-            updateScreenSaverEnabledSetting();
-        }
-    };
-
-    @Inject
-    public ScreenSaverEnabledCondition(@Application CoroutineScope scope, @Main Resources resources,
-            SecureSettings secureSettings) {
-        super(scope);
-        mScreenSaverEnabledByDefaultConfig = resources.getBoolean(
-                com.android.internal.R.bool.config_dreamsEnabledByDefault);
-        mSecureSettings = secureSettings;
-    }
-
-    @Override
-    protected void start() {
-        mSecureSettings.registerContentObserverForUserSync(
-                Settings.Secure.SCREENSAVER_ENABLED,
-                mScreenSaverSettingObserver, UserHandle.USER_CURRENT);
-        updateScreenSaverEnabledSetting();
-    }
-
-    @Override
-    protected void stop() {
-        mSecureSettings.unregisterContentObserverSync(mScreenSaverSettingObserver);
-    }
-
-    @Override
-    public int getStartStrategy() {
-        return START_EAGERLY;
-    }
-
-    private void updateScreenSaverEnabledSetting() {
-        final boolean enabled = mSecureSettings.getIntForUser(
-                Settings.Secure.SCREENSAVER_ENABLED,
-                mScreenSaverEnabledByDefaultConfig ? 1 : 0,
-                UserHandle.USER_CURRENT) != 0;
-        if (!enabled) {
-            Log.i(TAG, "Disabling low-light clock because screen saver has been disabled");
-        }
-        updateCondition(enabled);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.kt
new file mode 100644
index 0000000..e761cc0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.lowlightclock
+
+import android.content.res.Resources
+import android.database.ContentObserver
+import android.os.UserHandle
+import android.provider.Settings
+import android.util.Log
+import com.android.internal.R
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.shared.condition.Condition
+import com.android.systemui.util.settings.SecureSettings
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/** Condition for monitoring if the screensaver setting is enabled. */
+class ScreenSaverEnabledCondition
+@Inject
+constructor(
+    @Background scope: CoroutineScope,
+    @Main resources: Resources,
+    private val secureSettings: SecureSettings,
+) : Condition(scope) {
+    private val screenSaverEnabledByDefaultConfig =
+        resources.getBoolean(R.bool.config_dreamsEnabledByDefault)
+
+    private val screenSaverSettingObserver: ContentObserver =
+        object : ContentObserver(null) {
+            override fun onChange(selfChange: Boolean) {
+                updateScreenSaverEnabledSetting()
+            }
+        }
+
+    public override suspend fun start() {
+        secureSettings.registerContentObserverForUserSync(
+            Settings.Secure.SCREENSAVER_ENABLED,
+            screenSaverSettingObserver,
+            UserHandle.USER_CURRENT,
+        )
+        updateScreenSaverEnabledSetting()
+    }
+
+    override fun stop() {
+        secureSettings.unregisterContentObserverSync(screenSaverSettingObserver)
+    }
+
+    override val startStrategy: Int
+        get() = START_EAGERLY
+
+    private fun updateScreenSaverEnabledSetting() {
+        val enabled =
+            secureSettings.getIntForUser(
+                Settings.Secure.SCREENSAVER_ENABLED,
+                if (screenSaverEnabledByDefaultConfig) 1 else 0,
+                UserHandle.USER_CURRENT,
+            ) != 0
+        if (!enabled) {
+            Log.i(TAG, "Disabling low-light clock because screen saver has been disabled")
+        }
+        updateCondition(enabled)
+    }
+
+    companion object {
+        private val TAG: String = ScreenSaverEnabledCondition::class.java.simpleName
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/media/NotificationMediaManager.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
rename to packages/SystemUI/src/com/android/systemui/media/NotificationMediaManager.java
index 18f4b4a..db4c7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationMediaManager.java
@@ -11,9 +11,9 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
-package com.android.systemui.statusbar;
+package com.android.systemui.media;
 
 import static com.android.systemui.Flags.mediaControlsUserInitiatedDeleteintent;
 
@@ -40,6 +40,8 @@
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.shared.model.MediaData;
 import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index e7c2a45..b71d8c9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -17,6 +17,7 @@
 package com.android.systemui.media;
 
 import android.annotation.Nullable;
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -126,6 +127,8 @@
                 Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
                         + Binder.getCallingUid() + ")");
             }
+            enforceUriUserId(uri);
+
             Client client;
             synchronized (mClients) {
                 client = mClients.get(token);
@@ -207,6 +210,7 @@
 
         @Override
         public String getTitle(Uri uri) {
+            enforceUriUserId(uri);
             final UserHandle user = Binder.getCallingUserHandle();
             return Ringtone.getTitle(getContextForUser(user), uri,
                     false /*followSettingsUri*/, false /*allowRemote*/);
@@ -214,6 +218,7 @@
 
         @Override
         public ParcelFileDescriptor openRingtone(Uri uri) {
+            enforceUriUserId(uri);
             final UserHandle user = Binder.getCallingUserHandle();
             final ContentResolver resolver = getContextForUser(user).getContentResolver();
 
@@ -241,6 +246,28 @@
         }
     };
 
+    /**
+     * Must be called from the Binder calling thread.
+     * Ensures caller is from the same userId as the content they're trying to access.
+     * @param uri the URI to check
+     * @throws SecurityException when in a non-system call and userId in uri differs from the
+     *                           caller's userId
+     */
+    private void enforceUriUserId(Uri uri) throws SecurityException {
+        final int uriUserId = ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId());
+        // for a non-system call, verify the URI to play belongs to the same user as the caller
+        if (UserHandle.isApp(Binder.getCallingUid()) && (UserHandle.myUserId() != uriUserId)) {
+            final String errorMessage = "Illegal access to uri=" + uri
+                    + " content associated with user=" + uriUserId
+                    + ", current userID: " + UserHandle.myUserId();
+            if (android.media.audio.Flags.ringtoneUserUriCheck()) {
+                throw new SecurityException(errorMessage);
+            } else {
+                Log.e(TAG, errorMessage, new Exception());
+            }
+        }
+    }
+
     private Context getContextForUser(UserHandle user) {
         try {
             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
index 46cf0a6..c2efc75 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
@@ -66,6 +66,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.NotificationMediaManager.isPlayingState
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager.Companion.isMediaNotification
 import com.android.systemui.media.controls.domain.resume.MediaResumeListener
 import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
@@ -83,7 +84,6 @@
 import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.util.Assert
 import com.android.systemui.util.Utils
@@ -1256,7 +1256,7 @@
         return MediaAction(
             Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
             action,
-            context.getString(R.string.controls_media_resume),
+            context.getString(R.string.controls_media_button_play),
             if (Flags.mediaControlsUiUpdate()) {
                 context.getDrawable(R.drawable.ic_media_play_button_container)
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
index 5fef81f..da462e6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
@@ -30,6 +30,8 @@
 import android.util.Log
 import androidx.media.utils.MediaConstants
 import com.android.systemui.Flags
+import com.android.systemui.media.NotificationMediaManager.isConnectingState
+import com.android.systemui.media.NotificationMediaManager.isPlayingState
 import com.android.systemui.media.controls.domain.pipeline.LegacyMediaDataManagerImpl.Companion.MAX_COMPACT_ACTIONS
 import com.android.systemui.media.controls.domain.pipeline.LegacyMediaDataManagerImpl.Companion.MAX_NOTIFICATION_ACTIONS
 import com.android.systemui.media.controls.shared.MediaControlDrawables
@@ -38,8 +40,6 @@
 import com.android.systemui.media.controls.shared.model.MediaNotificationAction
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
-import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.util.kotlin.logI
 
 private const val TAG = "MediaActions"
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
index 8bb7303..a7c5a36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
@@ -51,6 +51,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.media.NotificationMediaManager.isPlayingState
 import com.android.systemui.media.controls.shared.model.MediaAction
 import com.android.systemui.media.controls.shared.model.MediaButton
 import com.android.systemui.media.controls.shared.model.MediaData
@@ -60,7 +61,6 @@
 import com.android.systemui.media.controls.util.MediaDataUtils
 import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.util.kotlin.logD
 import java.util.concurrent.ConcurrentHashMap
@@ -521,7 +521,7 @@
         return MediaAction(
             Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
             action,
-            context.getString(R.string.controls_media_resume),
+            context.getString(R.string.controls_media_button_play),
             if (Flags.mediaControlsUiUpdate()) {
                 context.getDrawable(R.drawable.ic_media_play_button_container)
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index fe852ce..ca4a659 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -66,6 +66,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.NotificationMediaManager.isPlayingState
 import com.android.systemui.media.controls.data.repository.MediaDataRepository
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager.Companion.isMediaNotification
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
@@ -85,7 +86,6 @@
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.util.Assert
 import com.android.systemui.util.Utils
@@ -1193,7 +1193,7 @@
         return MediaAction(
             Icon.createWithResource(context, iconId).setTint(themeText).loadDrawable(context),
             action,
-            context.getString(R.string.controls_media_resume),
+            context.getString(R.string.controls_media_button_play),
             if (Flags.mediaControlsUiUpdate()) {
                 context.getDrawable(R.drawable.ic_media_play_button_container)
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index 49b53c2..dfb32e6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -37,6 +37,7 @@
 import com.android.settingslib.media.MediaDevice
 import com.android.settingslib.media.PhoneMediaDevice
 import com.android.settingslib.media.flags.Flags
+import com.android.systemui.Flags.mediaControlsDeviceManagerBackgroundExecution
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.controls.shared.MediaControlDrawables
@@ -94,36 +95,21 @@
         data: MediaData,
         immediately: Boolean,
         receivedSmartspaceCardLatency: Int,
-        isSsReactivated: Boolean
+        isSsReactivated: Boolean,
     ) {
-        if (oldKey != null && oldKey != key) {
-            val oldEntry = entries.remove(oldKey)
-            oldEntry?.stop()
-        }
-        var entry = entries[key]
-        if (entry == null || entry.token != data.token) {
-            entry?.stop()
-            if (data.device != null) {
-                // If we were already provided device info (e.g. from RCN), keep that and don't
-                // listen for updates, but process once to push updates to listeners
-                processDevice(key, oldKey, data.device)
-                return
-            }
-            val controller = data.token?.let { controllerFactory.create(it) }
-            val localMediaManager =
-                localMediaManagerFactory.create(data.packageName, controller?.sessionToken)
-            val muteAwaitConnectionManager =
-                muteAwaitConnectionManagerFactory.create(localMediaManager)
-            entry = Entry(key, oldKey, controller, localMediaManager, muteAwaitConnectionManager)
-            entries[key] = entry
-            entry.start()
+        if (mediaControlsDeviceManagerBackgroundExecution()) {
+            bgExecutor.execute { onMediaLoaded(key, oldKey, data) }
+        } else {
+            onMediaLoaded(key, oldKey, data)
         }
     }
 
     override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
-        val token = entries.remove(key)
-        token?.stop()
-        token?.let { listeners.forEach { it.onKeyRemoved(key, userInitiated) } }
+        if (mediaControlsDeviceManagerBackgroundExecution()) {
+            bgExecutor.execute { onMediaRemoved(key, userInitiated) }
+        } else {
+            onMediaRemoved(key, userInitiated)
+        }
     }
 
     fun dump(pw: PrintWriter) {
@@ -141,6 +127,47 @@
         listeners.forEach { it.onMediaDeviceChanged(key, oldKey, device) }
     }
 
+    private fun onMediaLoaded(key: String, oldKey: String?, data: MediaData) {
+        if (oldKey != null && oldKey != key) {
+            val oldEntry = entries.remove(oldKey)
+            oldEntry?.stop()
+        }
+        var entry = entries[key]
+        if (entry == null || entry.token != data.token) {
+            entry?.stop()
+            if (data.device != null) {
+                // If we were already provided device info (e.g. from RCN), keep that and
+                // don't listen for updates, but process once to push updates to listeners
+                if (mediaControlsDeviceManagerBackgroundExecution()) {
+                    fgExecutor.execute { processDevice(key, oldKey, data.device) }
+                } else {
+                    processDevice(key, oldKey, data.device)
+                }
+                return
+            }
+            val controller = data.token?.let { controllerFactory.create(it) }
+            val localMediaManager =
+                localMediaManagerFactory.create(data.packageName, controller?.sessionToken)
+            val muteAwaitConnectionManager =
+                muteAwaitConnectionManagerFactory.create(localMediaManager)
+            entry = Entry(key, oldKey, controller, localMediaManager, muteAwaitConnectionManager)
+            entries[key] = entry
+            entry.start()
+        }
+    }
+
+    private fun onMediaRemoved(key: String, userInitiated: Boolean) {
+        val token = entries.remove(key)
+        token?.stop()
+        if (mediaControlsDeviceManagerBackgroundExecution()) {
+            fgExecutor.execute {
+                token?.let { listeners.forEach { it.onKeyRemoved(key, userInitiated) } }
+            }
+        } else {
+            token?.let { listeners.forEach { it.onKeyRemoved(key, userInitiated) } }
+        }
+    }
+
     interface Listener {
         /** Called when the route has changed for a given notification. */
         fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?)
@@ -260,7 +287,7 @@
         override fun onAboutToConnectDeviceAdded(
             deviceAddress: String,
             deviceName: String,
-            deviceIcon: Drawable?
+            deviceIcon: Drawable?,
         ) {
             aboutToConnectDeviceOverride =
                 AboutToConnectDevice(
@@ -270,8 +297,8 @@
                             /* enabled */ enabled = true,
                             /* icon */ deviceIcon,
                             /* name */ deviceName,
-                            /* showBroadcastButton */ showBroadcastButton = false
-                        )
+                            /* showBroadcastButton */ showBroadcastButton = false,
+                        ),
                 )
             updateCurrent()
         }
@@ -292,7 +319,7 @@
 
         override fun onBroadcastMetadataChanged(
             broadcastId: Int,
-            metadata: BluetoothLeBroadcastMetadata
+            metadata: BluetoothLeBroadcastMetadata,
         ) {
             logger.logBroadcastMetadataChanged(broadcastId, metadata.toString())
             updateCurrent()
@@ -352,14 +379,14 @@
                             //           route.
                             connectedDevice?.copy(
                                 name = it.name ?: connectedDevice.name,
-                                icon = icon
+                                icon = icon,
                             )
                         }
                             ?: MediaDeviceData(
                                 enabled = false,
                                 icon = MediaControlDrawables.getHomeDevices(context),
                                 name = context.getString(R.string.media_seamless_other_device),
-                                showBroadcastButton = false
+                                showBroadcastButton = false,
                             )
                     logger.logRemoteDevice(routingSession?.name, connectedDevice)
                 } else {
@@ -398,7 +425,7 @@
                         device?.iconWithoutBackground,
                         name,
                         id = device?.id,
-                        showBroadcastButton = false
+                        showBroadcastButton = false,
                     )
             }
         }
@@ -415,7 +442,7 @@
                 icon = iconWithoutBackground,
                 name = name,
                 id = id,
-                showBroadcastButton = false
+                showBroadcastButton = false,
             )
 
         private fun getLeAudioBroadcastDeviceData(): MediaDeviceData {
@@ -425,7 +452,7 @@
                     icon = MediaControlDrawables.getLeAudioSharing(context),
                     name = context.getString(R.string.audio_sharing_description),
                     intent = null,
-                    showBroadcastButton = false
+                    showBroadcastButton = false,
                 )
             } else {
                 MediaDeviceData(
@@ -433,7 +460,7 @@
                     icon = MediaControlDrawables.getAntenna(context),
                     name = broadcastDescription,
                     intent = null,
-                    showBroadcastButton = true
+                    showBroadcastButton = true,
                 )
             }
         }
@@ -449,7 +476,7 @@
                 device,
                 controller,
                 routingSession?.name,
-                selectedRoutes?.firstOrNull()?.name
+                selectedRoutes?.firstOrNull()?.name,
             )
 
             if (controller == null) {
@@ -514,7 +541,7 @@
                 MediaDataUtils.getAppLabel(
                     context,
                     localMediaManager.packageName,
-                    context.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name)
+                    context.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name),
                 )
             val isCurrentBroadcastedApp = TextUtils.equals(mediaApp, currentBroadcastedApp)
             if (isCurrentBroadcastedApp) {
@@ -538,5 +565,5 @@
  */
 private data class AboutToConnectDevice(
     val fullMediaDevice: MediaDevice? = null,
-    val backupMediaDeviceData: MediaDeviceData? = null
+    val backupMediaDeviceData: MediaDeviceData? = null,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
index 684a560..be4e6cc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
@@ -25,12 +25,12 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.NotificationMediaManager.isPlayingState
 import com.android.systemui.media.controls.shared.model.MediaData
 import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
 import com.android.systemui.media.controls.util.MediaControllerFactory
 import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.time.SystemClock
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index cedf661..5b65531 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -127,7 +127,6 @@
         }
 
         holder.seekBar.setMax(data.duration)
-        val totalTimeDescription = data.durationDescription
         if (data.scrubbing) {
             holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
         }
@@ -147,17 +146,9 @@
                 }
             }
 
-            val elapsedTimeDescription = data.elapsedTimeDescription
             if (data.scrubbing) {
                 holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
             }
-
-            holder.seekBar.contentDescription =
-                holder.seekBar.context.getString(
-                    R.string.controls_media_seekbar_description,
-                    elapsedTimeDescription,
-                    totalTimeDescription,
-                )
         }
     }
 
@@ -166,6 +157,18 @@
         return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
     }
 
+    fun updateContentDescription(
+        elapsedTimeDescription: CharSequence,
+        durationDescription: CharSequence,
+    ) {
+        holder.seekBar.contentDescription =
+            holder.seekBar.context.getString(
+                R.string.controls_media_seekbar_description,
+                elapsedTimeDescription,
+                durationDescription,
+            )
+    }
+
     @VisibleForTesting
     open fun buildResetAnimator(targetTime: Int): Animator {
         val animator =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index ac6343c..71b3223 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -159,8 +159,8 @@
     /** Is the player currently visible (at the end of the transformation */
     private var playersVisible: Boolean = false
 
-    /** Are we currently disabling pagination only allowing one media session to show */
-    private var currentlyDisablePagination: Boolean = false
+    /** Are we currently disabling scolling, only allowing the first media session to show */
+    private var currentlyDisableScrolling: Boolean = false
 
     /**
      * The desired location where we'll be at the end of the transformation. Usually this matches
@@ -1130,21 +1130,22 @@
         val endShowsActive = hostStates[currentEndLocation]?.showsOnlyActiveMedia ?: true
         val startShowsActive =
             hostStates[currentStartLocation]?.showsOnlyActiveMedia ?: endShowsActive
-        val startDisablePagination = hostStates[currentStartLocation]?.disablePagination ?: false
-        val endDisablePagination = hostStates[currentEndLocation]?.disablePagination ?: false
+        val startDisableScrolling = hostStates[currentStartLocation]?.disableScrolling ?: false
+        val endDisableScrolling = hostStates[currentEndLocation]?.disableScrolling ?: false
 
         if (
             currentlyShowingOnlyActive != endShowsActive ||
-                currentlyDisablePagination != endDisablePagination ||
+                currentlyDisableScrolling != endDisableScrolling ||
                 ((currentTransitionProgress != 1.0f && currentTransitionProgress != 0.0f) &&
                     (startShowsActive != endShowsActive ||
-                        startDisablePagination != endDisablePagination))
+                        startDisableScrolling != endDisableScrolling))
         ) {
             // Whenever we're transitioning from between differing states or the endstate differs
             // we reset the translation
             currentlyShowingOnlyActive = endShowsActive
-            currentlyDisablePagination = endDisablePagination
+            currentlyDisableScrolling = endDisableScrolling
             mediaCarouselScrollHandler.resetTranslation(animate = true)
+            mediaCarouselScrollHandler.scrollingDisabled = currentlyDisableScrolling
         }
     }
 
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 006eb20..f69985e 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
@@ -224,6 +224,8 @@
             this::setIsScrubbing;
     private final SeekBarViewModel.EnabledChangeListener mEnabledChangeListener =
             this::setIsSeekBarEnabled;
+    private final SeekBarViewModel.ContentDescriptionListener mContentDescriptionListener =
+            this::setSeekbarContentDescription;
 
     private final BroadcastDialogController mBroadcastDialogController;
     private boolean mIsCurrentBroadcastedApp = false;
@@ -327,6 +329,7 @@
         }
         mSeekBarViewModel.removeScrubbingChangeListener(mScrubbingChangeListener);
         mSeekBarViewModel.removeEnabledChangeListener(mEnabledChangeListener);
+        mSeekBarViewModel.removeContentDescriptionListener(mContentDescriptionListener);
         mSeekBarViewModel.onDestroy();
         mMediaViewController.onDestroy();
     }
@@ -395,6 +398,10 @@
         });
     }
 
+    private void setSeekbarContentDescription(CharSequence elapsedTime, CharSequence duration) {
+        mSeekBarObserver.updateContentDescription(elapsedTime, duration);
+    }
+
     /**
      * Reloads animator duration scale.
      */
@@ -424,6 +431,7 @@
         mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
         mSeekBarViewModel.setScrubbingChangeListener(mScrubbingChangeListener);
         mSeekBarViewModel.setEnabledChangeListener(mEnabledChangeListener);
+        mSeekBarViewModel.setContentDescriptionListener(mContentDescriptionListener);
         mMediaViewController.attach(player);
 
         vh.getPlayer().setOnLongClickListener(v -> {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index dba1900..e87d5de 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -229,6 +229,20 @@
             }
         }
 
+    private val seekbarDescriptionListener =
+        object : SeekBarViewModel.ContentDescriptionListener {
+            override fun onContentDescriptionChanged(
+                elapsedTimeDescription: CharSequence,
+                durationDescription: CharSequence,
+            ) {
+                if (!SceneContainerFlag.isEnabled) return
+                seekBarObserver.updateContentDescription(
+                    elapsedTimeDescription,
+                    durationDescription,
+                )
+            }
+        }
+
     /**
      * Sets the listening state of the player.
      *
@@ -350,6 +364,7 @@
             }
             seekBarViewModel.removeScrubbingChangeListener(scrubbingChangeListener)
             seekBarViewModel.removeEnabledChangeListener(enabledChangeListener)
+            seekBarViewModel.removeContentDescriptionListener(seekbarDescriptionListener)
             seekBarViewModel.onDestroy()
         }
         mediaHostStatesManager.removeController(this)
@@ -653,6 +668,7 @@
         seekBarViewModel.attachTouchHandlers(mediaViewHolder.seekBar)
         seekBarViewModel.setScrubbingChangeListener(scrubbingChangeListener)
         seekBarViewModel.setEnabledChangeListener(enabledChangeListener)
+        seekBarViewModel.setContentDescriptionListener(seekbarDescriptionListener)
 
         val mediaCard = mediaViewHolder.player
         attach(mediaViewHolder.player)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
index 9cf4a7b..68865d6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
@@ -127,6 +127,9 @@
             scrollView.relativeScrollX = newRelativeScroll
         }
 
+    /** Is scrolling disabled for the carousel */
+    var scrollingDisabled: Boolean = false
+
     /** Does the dismiss currently show the setting cog? */
     var showsSettingsButton: Boolean = false
 
@@ -270,6 +273,10 @@
     }
 
     private fun onTouch(motionEvent: MotionEvent): Boolean {
+        if (scrollingDisabled) {
+            return false
+        }
+
         val isUp = motionEvent.action == MotionEvent.ACTION_UP
         if (gestureDetector.onTouchEvent(motionEvent)) {
             if (isUp) {
@@ -349,6 +356,10 @@
     }
 
     fun onScroll(down: MotionEvent, lastMotion: MotionEvent, distanceX: Float): Boolean {
+        if (scrollingDisabled) {
+            return false
+        }
+
         val totalX = lastMotion.x - down.x
         val currentTranslation = scrollView.getContentTranslation()
         if (currentTranslation != 0.0f || !scrollView.canScrollHorizontally((-totalX).toInt())) {
@@ -405,6 +416,10 @@
     }
 
     private fun onFling(vX: Float, vY: Float): Boolean {
+        if (scrollingDisabled) {
+            return false
+        }
+
         if (vX * vX < 0.5 * vY * vY) {
             return false
         }
@@ -575,6 +590,9 @@
      * @param destIndex destination index to indicate where the scroll should end.
      */
     fun scrollToPlayer(sourceIndex: Int = -1, destIndex: Int) {
+        if (scrollingDisabled) {
+            return
+        }
         if (sourceIndex >= 0 && sourceIndex < mediaContent.childCount) {
             scrollView.relativeScrollX = sourceIndex * playerWidthPlusPadding
         }
@@ -596,6 +614,9 @@
      * @param step A positive number means next, and negative means previous.
      */
     fun scrollByStep(step: Int) {
+        if (scrollingDisabled) {
+            return
+        }
         val destIndex = visibleMediaIndex + step
         if (destIndex >= mediaContent.childCount || destIndex < 0) {
             if (!showsSettingsButton) return
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index a518349..37af764 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -295,7 +295,7 @@
                 changedListener?.invoke()
             }
 
-        override var disablePagination: Boolean = false
+        override var disableScrolling: Boolean = false
             set(value) {
                 if (field == value) {
                     return
@@ -320,7 +320,7 @@
             mediaHostState.visible = visible
             mediaHostState.disappearParameters = disappearParameters.deepCopy()
             mediaHostState.falsingProtectionNeeded = falsingProtectionNeeded
-            mediaHostState.disablePagination = disablePagination
+            mediaHostState.disableScrolling = disableScrolling
             return mediaHostState
         }
 
@@ -349,7 +349,7 @@
             if (!disappearParameters.equals(other.disappearParameters)) {
                 return false
             }
-            if (disablePagination != other.disablePagination) {
+            if (disableScrolling != other.disableScrolling) {
                 return false
             }
             return true
@@ -363,7 +363,7 @@
             result = 31 * result + showsOnlyActiveMedia.hashCode()
             result = 31 * result + if (visible) 1 else 2
             result = 31 * result + disappearParameters.hashCode()
-            result = 31 * result + disablePagination.hashCode()
+            result = 31 * result + disableScrolling.hashCode()
             return result
         }
     }
@@ -423,10 +423,11 @@
     var disappearParameters: DisappearParameters
 
     /**
-     * Whether pagination should be disabled for this host, meaning that when there are multiple
-     * media sessions, only the first one will appear.
+     * Whether scrolling should be disabled for this host, meaning that when there are multiple
+     * media sessions, it will not be possible to scroll between media sessions or swipe away the
+     * entire media carousel. The first media session will always be shown.
      */
-    var disablePagination: Boolean
+    var disableScrolling: Boolean
 
     /** Get a copy of this view state, deepcopying all appropriate members */
     fun copy(): MediaHostState
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt
index 0e8e595..848d822 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt
@@ -26,6 +26,10 @@
 import android.widget.TextView
 import androidx.constraintlayout.widget.Barrier
 import com.android.internal.widget.CachingIconView
+import com.android.systemui.FontStyles.GSF_HEADLINE_SMALL
+import com.android.systemui.FontStyles.GSF_LABEL_LARGE
+import com.android.systemui.FontStyles.GSF_LABEL_MEDIUM
+import com.android.systemui.FontStyles.GSF_TITLE_MEDIUM
 import com.android.systemui.res.R
 import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
 import com.android.systemui.surfaceeffects.ripple.MultiRippleView
@@ -177,9 +181,9 @@
                 R.id.touch_ripple_view,
             )
 
-        val headlineSmallTF: Typeface = Typeface.create("gsf-headline-small", Typeface.NORMAL)
-        val titleMediumTF: Typeface = Typeface.create("gsf-title-medium", Typeface.NORMAL)
-        val labelMediumTF: Typeface = Typeface.create("gsf-label-medium", Typeface.NORMAL)
-        val labelLargeTF: Typeface = Typeface.create("gsf-label-large", Typeface.NORMAL)
+        val headlineSmallTF: Typeface = Typeface.create(GSF_HEADLINE_SMALL, Typeface.NORMAL)
+        val titleMediumTF: Typeface = Typeface.create(GSF_TITLE_MEDIUM, Typeface.NORMAL)
+        val labelMediumTF: Typeface = Typeface.create(GSF_LABEL_MEDIUM, Typeface.NORMAL)
+        val labelLargeTF: Typeface = Typeface.create(GSF_LABEL_LARGE, Typeface.NORMAL)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index a1f0cc3..78a8cf8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -39,8 +39,8 @@
 import com.android.systemui.Flags
 import com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.media.NotificationMediaManager
 import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.util.concurrency.RepeatableExecutor
 import java.util.Locale
 import javax.inject.Inject
@@ -104,19 +104,20 @@
         )
         set(value) {
             val enabledChanged = value.enabled != field.enabled
+            field = value
             if (enabledChanged) {
                 enabledChangeListener?.onEnabledChanged(value.enabled)
             }
+            _progress.postValue(value)
+
             bgExecutor.execute {
                 val durationDescription = formatTimeContentDescription(value.duration)
                 val elapsedDescription =
                     value.elapsedTime?.let { formatTimeContentDescription(it) } ?: ""
-                field =
-                    value.copy(
-                        durationDescription = durationDescription,
-                        elapsedTimeDescription = elapsedDescription,
-                    )
-                _progress.postValue(field)
+                contentDescriptionListener?.onContentDescriptionChanged(
+                    elapsedDescription,
+                    durationDescription,
+                )
             }
         }
 
@@ -175,6 +176,7 @@
 
     private var scrubbingChangeListener: ScrubbingChangeListener? = null
     private var enabledChangeListener: EnabledChangeListener? = null
+    private var contentDescriptionListener: ContentDescriptionListener? = null
 
     /** Set to true when the user is touching the seek bar to change the position. */
     private var scrubbing = false
@@ -394,6 +396,16 @@
         }
     }
 
+    fun setContentDescriptionListener(listener: ContentDescriptionListener) {
+        contentDescriptionListener = listener
+    }
+
+    fun removeContentDescriptionListener(listener: ContentDescriptionListener) {
+        if (listener == contentDescriptionListener) {
+            contentDescriptionListener = null
+        }
+    }
+
     /** returns a pair of whether seekbar is enabled and the duration of media. */
     private fun getEnabledStateAndDuration(metadata: MediaMetadata?): Pair<Boolean, Int> {
         val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
@@ -468,6 +480,13 @@
         fun onEnabledChanged(enabled: Boolean)
     }
 
+    interface ContentDescriptionListener {
+        fun onContentDescriptionChanged(
+            elapsedTimeDescription: CharSequence,
+            durationDescription: CharSequence,
+        )
+    }
+
     private class SeekBarChangeListener(
         val viewModel: SeekBarViewModel,
         val falsingManager: FalsingManager,
@@ -639,7 +658,5 @@
         val duration: Int,
         /** whether seekBar is listening to progress updates */
         val listening: Boolean,
-        val elapsedTimeDescription: CharSequence = "",
-        val durationDescription: CharSequence = "",
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java
index c58ba37..ac1672d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java
@@ -351,8 +351,9 @@
         @VisibleForTesting
         void showCustomEndSessionDialog(MediaDevice device) {
             MediaSessionReleaseDialog mediaSessionReleaseDialog = new MediaSessionReleaseDialog(
-                    mContext, () -> transferOutput(device), mController.getColorButtonBackground(),
-                    mController.getColorItemContent());
+                    mContext, () -> transferOutput(device),
+                    mController.getColorSchemeLegacy().getColorButtonBackground(),
+                    mController.getColorSchemeLegacy().getColorItemContent());
             mediaSessionReleaseDialog.show();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java
index 300a357..6ab4a52 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java
@@ -50,9 +50,11 @@
 import com.android.media.flags.Flags;
 import com.android.settingslib.media.InputMediaDevice;
 import com.android.settingslib.media.MediaDevice;
-import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.res.R;
 
+import java.util.concurrent.Executor;
 /**
  * A RecyclerView adapter for the legacy UI media output dialog device list.
  */
@@ -61,14 +63,20 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int UNMUTE_DEFAULT_VOLUME = 2;
-    private static final float DEVICE_DISABLED_ALPHA = 0.5f;
-    private static final float DEVICE_ACTIVE_ALPHA = 1f;
+    @VisibleForTesting static final float DEVICE_DISABLED_ALPHA = 0.5f;
+    @VisibleForTesting static final float DEVICE_ACTIVE_ALPHA = 1f;
+    private final Executor mMainExecutor;
+    private final Executor mBackgroundExecutor;
     View mHolderView;
-    private boolean mIsInitVolumeFirstTime;
 
-    public MediaOutputAdapterLegacy(MediaSwitchingController controller) {
+    public MediaOutputAdapterLegacy(
+            MediaSwitchingController controller,
+            @Main Executor mainExecutor,
+            @Background Executor backgroundExecutor
+    ) {
         super(controller);
-        mIsInitVolumeFirstTime = true;
+        mMainExecutor = mainExecutor;
+        mBackgroundExecutor = backgroundExecutor;
     }
 
     @Override
@@ -181,9 +189,9 @@
             mEndTouchArea.setVisibility(View.GONE);
             mEndClickIcon.setVisibility(View.GONE);
             mContainerLayout.setOnClickListener(null);
-            mTitleText.setTextColor(mController.getColorItemContent());
-            mSubTitleText.setTextColor(mController.getColorItemContent());
-            mVolumeValueText.setTextColor(mController.getColorItemContent());
+            mTitleText.setTextColor(mController.getColorSchemeLegacy().getColorItemContent());
+            mSubTitleText.setTextColor(mController.getColorSchemeLegacy().getColorItemContent());
+            mVolumeValueText.setTextColor(mController.getColorSchemeLegacy().getColorItemContent());
             mIconAreaLayout.setBackground(null);
             updateIconAreaClickListener(null);
             updateSeekBarProgressColor();
@@ -193,14 +201,14 @@
 
         /** Binds a ViewHolder for a "Connect a device" item. */
         void onBindPairNewDevice() {
-            mTitleText.setTextColor(mController.getColorItemContent());
+            mTitleText.setTextColor(mController.getColorSchemeLegacy().getColorItemContent());
             mCheckBox.setVisibility(View.GONE);
             updateTitle(mContext.getText(R.string.media_output_dialog_pairing_new));
             updateItemBackground(ConnectionState.DISCONNECTED);
             final Drawable addDrawable = mContext.getDrawable(R.drawable.ic_add);
             mTitleIcon.setImageDrawable(addDrawable);
-            mTitleIcon.setImageTintList(
-                    ColorStateList.valueOf(mController.getColorItemContent()));
+            mTitleIcon.setImageTintList(ColorStateList.valueOf(
+                    mController.getColorSchemeLegacy().getColorItemContent()));
             mContainerLayout.setOnClickListener(mController::launchBluetoothPairing);
         }
 
@@ -251,10 +259,9 @@
                     updateSeekbarProgressBackground();
                 }
             }
-            boolean isCurrentSeekbarInvisible = mSeekBar.getVisibility() == View.GONE;
             mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
             if (showSeekBar) {
-                initSeekbar(device, isCurrentSeekbarInvisible);
+                initSeekbar(device);
                 updateContainerContentA11yImportance(false /* isImportant */);
                 mSeekBar.setContentDescription(contentDescription);
             } else {
@@ -264,9 +271,8 @@
 
         void updateGroupSeekBar(String contentDescription) {
             updateSeekbarProgressBackground();
-            boolean isCurrentSeekbarInvisible = mSeekBar.getVisibility() == View.GONE;
             mSeekBar.setVisibility(View.VISIBLE);
-            initGroupSeekbar(isCurrentSeekbarInvisible);
+            initGroupSeekbar();
             updateContainerContentA11yImportance(false /* isImportant */);
             mSeekBar.setContentDescription(contentDescription);
         }
@@ -297,8 +303,8 @@
         protected void updateLoadingIndicator(ConnectionState connectionState) {
             if (connectionState == ConnectionState.CONNECTING) {
                 mProgressBar.setVisibility(View.VISIBLE);
-                mProgressBar.getIndeterminateDrawable().setTintList(
-                        ColorStateList.valueOf(mController.getColorItemContent()));
+                mProgressBar.getIndeterminateDrawable().setTintList(ColorStateList.valueOf(
+                        mController.getColorSchemeLegacy().getColorItemContent()));
             } else {
                 mProgressBar.setVisibility(View.GONE);
             }
@@ -318,8 +324,8 @@
 
             // Connected or connecting state has a darker background.
             int backgroundColor = isConnected || isConnecting
-                    ? mController.getColorConnectedItemBackground()
-                    : mController.getColorItemBackground();
+                    ? mController.getColorSchemeLegacy().getColorConnectedItemBackground()
+                    : mController.getColorSchemeLegacy().getColorItemBackground();
             mItemLayout.setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
         }
 
@@ -332,13 +338,13 @@
         }
 
         private void updateSeekBarProgressColor() {
-            mSeekBar.setProgressTintList(
-                    ColorStateList.valueOf(mController.getColorSeekbarProgress()));
+            mSeekBar.setProgressTintList(ColorStateList.valueOf(
+                    mController.getColorSchemeLegacy().getColorSeekbarProgress()));
             final Drawable contrastDotDrawable =
                     ((LayerDrawable) mSeekBar.getProgressDrawable()).findDrawableByLayerId(
                             R.id.contrast_dot);
-            contrastDotDrawable.setTintList(
-                    ColorStateList.valueOf(mController.getColorItemContent()));
+            contrastDotDrawable.setTintList(ColorStateList.valueOf(
+                    mController.getColorSchemeLegacy().getColorItemContent()));
         }
 
         void updateSeekbarProgressBackground() {
@@ -354,31 +360,21 @@
                             mActiveRadius, 0, 0});
         }
 
-        private void initializeSeekbarVolume(
-                @Nullable MediaDevice device, int currentVolume,
-                boolean isCurrentSeekbarInvisible) {
+        private void initializeSeekbarVolume(@Nullable MediaDevice device, int currentVolume) {
             if (!isDragging()) {
                 if (mSeekBar.getVolume() != currentVolume && (mLatestUpdateVolume == -1
                         || currentVolume == mLatestUpdateVolume)) {
                     // Update only if volume of device and value of volume bar doesn't match.
                     // Check if response volume match with the latest request, to ignore obsolete
                     // response
-                    if (isCurrentSeekbarInvisible && !mIsInitVolumeFirstTime) {
+                    if (!mVolumeAnimator.isStarted()) {
                         if (currentVolume == 0) {
                             updateMutedVolumeIcon(device);
                         } else {
                             updateUnmutedVolumeIcon(device);
                         }
-                    } else {
-                        if (!mVolumeAnimator.isStarted()) {
-                            if (currentVolume == 0) {
-                                updateMutedVolumeIcon(device);
-                            } else {
-                                updateUnmutedVolumeIcon(device);
-                            }
-                            mSeekBar.setVolume(currentVolume);
-                            mLatestUpdateVolume = -1;
-                        }
+                        mSeekBar.setVolume(currentVolume);
+                        mLatestUpdateVolume = -1;
                     }
                 } else if (currentVolume == 0) {
                     mSeekBar.resetVolume();
@@ -388,12 +384,9 @@
                     mLatestUpdateVolume = -1;
                 }
             }
-            if (mIsInitVolumeFirstTime) {
-                mIsInitVolumeFirstTime = false;
-            }
         }
 
-        void initSeekbar(@NonNull MediaDevice device, boolean isCurrentSeekbarInvisible) {
+        void initSeekbar(@NonNull MediaDevice device) {
             SeekBarVolumeControl volumeControl = new SeekBarVolumeControl() {
                 @Override
                 public int getVolume() {
@@ -422,7 +415,7 @@
             }
             mSeekBar.setMaxVolume(device.getMaxVolume());
             final int currentVolume = device.getCurrentVolume();
-            initializeSeekbarVolume(device, currentVolume, isCurrentSeekbarInvisible);
+            initializeSeekbarVolume(device, currentVolume);
 
             mSeekBar.setOnSeekBarChangeListener(new MediaSeekBarChangedListener(
                     device, volumeControl) {
@@ -435,7 +428,7 @@
         }
 
         // Initializes the seekbar for a group of devices.
-        void initGroupSeekbar(boolean isCurrentSeekbarInvisible) {
+        void initGroupSeekbar() {
             SeekBarVolumeControl volumeControl = new SeekBarVolumeControl() {
                 @Override
                 public int getVolume() {
@@ -462,7 +455,7 @@
             mSeekBar.setMaxVolume(mController.getSessionVolumeMax());
 
             final int currentVolume = mController.getSessionVolume();
-            initializeSeekbarVolume(null, currentVolume, isCurrentSeekbarInvisible);
+            initializeSeekbarVolume(null, currentVolume);
             mSeekBar.setOnSeekBarChangeListener(new MediaSeekBarChangedListener(
                     null, volumeControl) {
                 @Override
@@ -503,9 +496,10 @@
             boolean isInputMediaDevice = device instanceof InputMediaDevice;
             int id = getDrawableId(isInputMediaDevice, isMutedVolumeIcon);
             mTitleIcon.setImageDrawable(mContext.getDrawable(id));
-            mTitleIcon.setImageTintList(ColorStateList.valueOf(mController.getColorItemContent()));
-            mIconAreaLayout.setBackgroundTintList(
-                    ColorStateList.valueOf(mController.getColorSeekbarProgress()));
+            mTitleIcon.setImageTintList(ColorStateList.valueOf(
+                    mController.getColorSchemeLegacy().getColorItemContent()));
+            mIconAreaLayout.setBackgroundTintList(ColorStateList.valueOf(
+                    mController.getColorSchemeLegacy().getColorSeekbarProgress()));
         }
 
         @VisibleForTesting
@@ -534,8 +528,8 @@
                 mStatusIcon.setVisibility(View.GONE);
             } else {
                 mStatusIcon.setImageDrawable(deviceStatusIcon);
-                mStatusIcon.setImageTintList(
-                        ColorStateList.valueOf(mController.getColorItemContent()));
+                mStatusIcon.setImageTintList(ColorStateList.valueOf(
+                        mController.getColorSchemeLegacy().getColorItemContent()));
                 if (deviceStatusIcon instanceof AnimatedVectorDrawable) {
                     ((AnimatedVectorDrawable) deviceStatusIcon).start();
                 }
@@ -585,9 +579,10 @@
         private void updateEndAreaWithIcon(View.OnClickListener clickListener,
                 @DrawableRes int iconDrawableId,
                 @StringRes int accessibilityStringId) {
-            updateEndAreaColor(mController.getColorSeekbarProgress());
+            updateEndAreaColor(mController.getColorSchemeLegacy().getColorSeekbarProgress());
             mEndClickIcon.setImageTintList(
-                    ColorStateList.valueOf(mController.getColorItemContent()));
+                    ColorStateList.valueOf(
+                            mController.getColorSchemeLegacy().getColorItemContent()));
             mEndClickIcon.setOnClickListener(clickListener);
             Drawable drawable = mContext.getDrawable(iconDrawableId);
             mEndClickIcon.setImageDrawable(drawable);
@@ -600,8 +595,9 @@
         private void updateEndAreaForGroupCheckBox(@NonNull MediaDevice device,
                 @NonNull GroupStatus groupStatus) {
             boolean isEnabled = isGroupCheckboxEnabled(groupStatus);
-            updateEndAreaColor(groupStatus.selected() ? mController.getColorSeekbarProgress()
-                    : mController.getColorItemBackground());
+            updateEndAreaColor(groupStatus.selected()
+                    ? mController.getColorSchemeLegacy().getColorSeekbarProgress()
+                    : mController.getColorSchemeLegacy().getColorItemBackground());
             mCheckBox.setContentDescription(mContext.getString(
                     groupStatus.selected() ? R.string.accessibility_remove_device_from_group
                             : R.string.accessibility_add_device_to_group));
@@ -611,7 +607,7 @@
                     isEnabled ? (buttonView, isChecked) -> onGroupActionTriggered(
                             !groupStatus.selected(), device) : null);
             mCheckBox.setEnabled(isEnabled);
-            setCheckBoxColor(mCheckBox, mController.getColorItemContent());
+            setCheckBoxColor(mCheckBox, mController.getColorSchemeLegacy().getColorItemContent());
         }
 
         private void setCheckBoxColor(CheckBox checkBox, int color) {
@@ -714,15 +710,15 @@
         }
 
         protected void setUpDeviceIcon(@NonNull MediaDevice device) {
-            ThreadUtils.postOnBackgroundThread(() -> {
+            mBackgroundExecutor.execute(() -> {
                 Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
-                ThreadUtils.postOnMainThread(() -> {
+                mMainExecutor.execute(() -> {
                     if (!TextUtils.equals(mDeviceId, device.getId())) {
                         return;
                     }
                     mTitleIcon.setImageIcon(icon);
-                    mTitleIcon.setImageTintList(
-                            ColorStateList.valueOf(mController.getColorItemContent()));
+                    mTitleIcon.setImageTintList(ColorStateList.valueOf(
+                            mController.getColorSchemeLegacy().getColorItemContent()));
                 });
             });
         }
@@ -807,7 +803,7 @@
         }
 
         void onBind(String groupDividerTitle) {
-            mTitleText.setTextColor(mController.getColorItemContent());
+            mTitleText.setTextColor(mController.getColorSchemeLegacy().getColorItemContent());
             mTitleText.setText(groupDividerTitle);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index d791361..49d09cf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -40,7 +40,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowManager;
@@ -93,13 +92,10 @@
     private ImageView mAppResourceIcon;
     private ImageView mBroadcastIcon;
     private RecyclerView mDevicesRecyclerView;
-    private LinearLayout mDeviceListLayout;
+    private ViewGroup mDeviceListLayout;
     private LinearLayout mMediaMetadataSectionLayout;
     private Button mDoneButton;
     private Button mStopButton;
-    private int mListMaxHeight;
-    private int mItemHeight;
-    private int mListPaddingTop;
     private WallpaperColors mWallpaperColors;
     private boolean mShouldLaunchLeBroadcastDialog;
     private boolean mIsLeBroadcastCallbackRegistered;
@@ -109,17 +105,6 @@
 
     protected Executor mExecutor;
 
-    private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> {
-        ViewGroup.LayoutParams params = mDeviceListLayout.getLayoutParams();
-        int totalItemsHeight = mAdapter.getItemCount() * mItemHeight
-                + mListPaddingTop;
-        int correctHeight = Math.min(totalItemsHeight, mListMaxHeight);
-        // Set max height for list
-        if (correctHeight != params.height) {
-            params.height = correctHeight;
-            mDeviceListLayout.setLayoutParams(params);
-        }
-    };
 
     private final BluetoothLeBroadcast.Callback mBroadcastCallback =
             new BluetoothLeBroadcast.Callback() {
@@ -220,12 +205,6 @@
         mBroadcastSender = broadcastSender;
         mMediaSwitchingController = mediaSwitchingController;
         mLayoutManager = new LayoutManagerWrapper(mContext);
-        mListMaxHeight = context.getResources().getDimensionPixelSize(
-                R.dimen.media_output_dialog_list_max_height);
-        mItemHeight = context.getResources().getDimensionPixelSize(
-                R.dimen.media_output_dialog_list_item_height);
-        mListPaddingTop = mContext.getResources().getDimensionPixelSize(
-                R.dimen.media_output_dialog_list_padding_top);
         mExecutor = Executors.newSingleThreadExecutor();
         mIncludePlaybackAndAppMetadata = includePlaybackAndAppMetadata;
     }
@@ -258,8 +237,6 @@
         mAppResourceIcon = mDialogView.requireViewById(R.id.app_source_icon);
         mBroadcastIcon = mDialogView.requireViewById(R.id.broadcast_icon);
 
-        mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
-                mDeviceListLayoutListener);
         // Init device list
         mLayoutManager.setAutoMeasureEnabled(true);
         mDevicesRecyclerView.setLayoutManager(mLayoutManager);
@@ -342,7 +319,8 @@
                 WallpaperColors wallpaperColors = WallpaperColors.fromBitmap(icon.getBitmap());
                 colorSetUpdated = !wallpaperColors.equals(mWallpaperColors);
                 if (colorSetUpdated) {
-                    mMediaSwitchingController.setCurrentColorScheme(wallpaperColors, isDarkThemeOn);
+                    mMediaSwitchingController.updateCurrentColorScheme(wallpaperColors,
+                            isDarkThemeOn);
                     updateButtonBackgroundColorFilter();
                     updateDialogBackgroundColor();
                 }
@@ -359,7 +337,8 @@
             mAppResourceIcon.setVisibility(View.GONE);
         } else if (appSourceIcon != null) {
             Icon appIcon = appSourceIcon.toIcon(mContext);
-            mAppResourceIcon.setColorFilter(mMediaSwitchingController.getColorItemContent());
+            mAppResourceIcon.setColorFilter(
+                    mMediaSwitchingController.getColorSchemeLegacy().getColorItemContent());
             mAppResourceIcon.setImageIcon(appIcon);
         } else {
             Drawable appIconDrawable = mMediaSwitchingController.getAppSourceIconFromPackage();
@@ -369,12 +348,6 @@
                 mAppResourceIcon.setVisibility(View.GONE);
             }
         }
-        if (mHeaderIcon.getVisibility() == View.VISIBLE) {
-            final int size = getHeaderIconSize();
-            final int padding = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.media_output_dialog_header_icon_padding);
-            mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
-        }
 
         if (!mIncludePlaybackAndAppMetadata) {
             mHeaderTitle.setVisibility(View.GONE);
@@ -419,18 +392,19 @@
     private void updateButtonBackgroundColorFilter() {
         ColorFilter buttonColorFilter =
                 new PorterDuffColorFilter(
-                        mMediaSwitchingController.getColorButtonBackground(),
+                        mMediaSwitchingController.getColorSchemeLegacy().getColorButtonBackground(),
                         PorterDuff.Mode.SRC_IN);
         mDoneButton.getBackground().setColorFilter(buttonColorFilter);
         mStopButton.getBackground().setColorFilter(buttonColorFilter);
-        mDoneButton.setTextColor(mMediaSwitchingController.getColorPositiveButtonText());
+        mDoneButton.setTextColor(
+                mMediaSwitchingController.getColorSchemeLegacy().getColorPositiveButtonText());
     }
 
     private void updateDialogBackgroundColor() {
-        getDialogView()
-                .getBackground()
-                .setTint(mMediaSwitchingController.getColorDialogBackground());
-        mDeviceListLayout.setBackgroundColor(mMediaSwitchingController.getColorDialogBackground());
+        getDialogView().getBackground().setTint(
+                mMediaSwitchingController.getColorSchemeLegacy().getColorDialogBackground());
+        mDeviceListLayout.setBackgroundColor(
+                mMediaSwitchingController.getColorSchemeLegacy().getColorDialogBackground());
     }
 
     public void handleLeBroadcastStarted() {
@@ -520,8 +494,6 @@
 
     abstract IconCompat getHeaderIcon();
 
-    abstract int getHeaderIconSize();
-
     abstract CharSequence getHeaderText();
 
     abstract CharSequence getHeaderSubtitle();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index 9ade9e2..791a61c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -52,6 +52,8 @@
 
 import com.google.zxing.WriterException;
 
+import java.util.concurrent.Executor;
+
 /**
  * Dialog for media output broadcast.
  */
@@ -239,13 +241,16 @@
             Context context,
             boolean aboveStatusbar,
             BroadcastSender broadcastSender,
-            MediaSwitchingController mediaSwitchingController) {
+            MediaSwitchingController mediaSwitchingController,
+            Executor mainExecutor,
+            Executor backgroundExecutor) {
         super(
                 context,
                 broadcastSender,
                 mediaSwitchingController, /* includePlaybackAndAppMetadata */
                 true);
-        mAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController);
+        mAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController, mainExecutor,
+                backgroundExecutor);
         // TODO(b/226710953): Move the part to MediaOutputBaseDialog for every class
         //  that extends MediaOutputBaseDialog
         if (!aboveStatusbar) {
@@ -295,12 +300,6 @@
     }
 
     @Override
-    int getHeaderIconSize() {
-        return mContext.getResources().getDimensionPixelSize(
-                R.dimen.media_output_dialog_header_album_icon_size);
-    }
-
-    @Override
     CharSequence getHeaderText() {
         return mMediaSwitchingController.getHeaderTitle();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogManager.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogManager.kt
index 2e7e66f..81c85a6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogManager.kt
@@ -20,6 +20,9 @@
 import android.view.View
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
 import javax.inject.Inject
 
 /** Manager to create and show a [MediaOutputBroadcastDialog]. */
@@ -29,7 +32,9 @@
     private val context: Context,
     private val broadcastSender: BroadcastSender,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
-    private val mediaSwitchingControllerFactory: MediaSwitchingController.Factory
+    private val mediaSwitchingControllerFactory: MediaSwitchingController.Factory,
+    @Main private val mainExecutor: Executor,
+    @Background private val backgroundExecutor: Executor,
 ) {
     var mediaOutputBroadcastDialog: MediaOutputBroadcastDialog? = null
 
@@ -47,7 +52,14 @@
                 /* token */ null,
             )
         val dialog =
-            MediaOutputBroadcastDialog(context, aboveStatusBar, broadcastSender, controller)
+            MediaOutputBroadcastDialog(
+                context,
+                aboveStatusBar,
+                broadcastSender,
+                controller,
+                mainExecutor,
+                backgroundExecutor,
+            )
         mediaOutputBroadcastDialog = dialog
 
         // Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputColorSchemeLegacy.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputColorSchemeLegacy.kt
new file mode 100644
index 0000000..7f0fa46
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputColorSchemeLegacy.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dialog
+
+import android.content.Context
+import com.android.settingslib.Utils
+import com.android.systemui.monet.ColorScheme
+import com.android.systemui.res.R
+
+abstract class MediaOutputColorSchemeLegacy {
+    companion object Factory {
+        @JvmStatic
+        fun fromSystemColors(context: Context): MediaOutputColorSchemeLegacy {
+            return MediaOutputColorSchemeLegacySystem(context)
+        }
+
+        @JvmStatic
+        fun fromDynamicColors(
+            colorScheme: ColorScheme,
+            isDarkTheme: Boolean,
+        ): MediaOutputColorSchemeLegacy {
+            return MediaOutputColorSchemeLegacyDynamic(colorScheme, isDarkTheme)
+        }
+    }
+
+    abstract fun getColorConnectedItemBackground(): Int
+
+    abstract fun getColorPositiveButtonText(): Int
+
+    abstract fun getColorDialogBackground(): Int
+
+    abstract fun getColorItemContent(): Int
+
+    abstract fun getColorSeekbarProgress(): Int
+
+    abstract fun getColorButtonBackground(): Int
+
+    abstract fun getColorItemBackground(): Int
+}
+
+class MediaOutputColorSchemeLegacySystem(private val mContext: Context) :
+    MediaOutputColorSchemeLegacy() {
+
+    override fun getColorConnectedItemBackground() =
+        Utils.getColorStateListDefaultColor(
+            mContext,
+            R.color.media_dialog_connected_item_background,
+        )
+
+    override fun getColorPositiveButtonText() =
+        Utils.getColorStateListDefaultColor(mContext, R.color.media_dialog_solid_button_text)
+
+    override fun getColorDialogBackground() =
+        Utils.getColorStateListDefaultColor(mContext, R.color.media_dialog_background)
+
+    override fun getColorItemContent() =
+        Utils.getColorStateListDefaultColor(mContext, R.color.media_dialog_item_main_content)
+
+    override fun getColorSeekbarProgress() =
+        Utils.getColorStateListDefaultColor(mContext, R.color.media_dialog_seekbar_progress)
+
+    override fun getColorButtonBackground() =
+        Utils.getColorStateListDefaultColor(mContext, R.color.media_dialog_button_background)
+
+    override fun getColorItemBackground() =
+        Utils.getColorStateListDefaultColor(mContext, R.color.media_dialog_item_background)
+}
+
+class MediaOutputColorSchemeLegacyDynamic(colorScheme: ColorScheme, isDarkTheme: Boolean) :
+    MediaOutputColorSchemeLegacy() {
+    private var mColorItemContent: Int
+    private var mColorSeekbarProgress: Int
+    private var mColorButtonBackground: Int
+    private var mColorItemBackground: Int
+    private var mColorConnectedItemBackground: Int
+    private var mColorPositiveButtonText: Int
+    private var mColorDialogBackground: Int
+
+    init {
+        if (isDarkTheme) {
+            mColorItemContent = colorScheme.accent1.s100 // A1-100
+            mColorSeekbarProgress = colorScheme.accent2.s600 // A2-600
+            mColorButtonBackground = colorScheme.accent1.s300 // A1-300
+            mColorItemBackground = colorScheme.neutral2.s800 // N2-800
+            mColorConnectedItemBackground = colorScheme.accent2.s800 // A2-800
+            mColorPositiveButtonText = colorScheme.accent2.s800 // A2-800
+            mColorDialogBackground = colorScheme.neutral1.s900 // N1-900
+        } else {
+            mColorItemContent = colorScheme.accent1.s800 // A1-800
+            mColorSeekbarProgress = colorScheme.accent1.s300 // A1-300
+            mColorButtonBackground = colorScheme.accent1.s600 // A1-600
+            mColorItemBackground = colorScheme.accent2.s50 // A2-50
+            mColorConnectedItemBackground = colorScheme.accent1.s100 // A1-100
+            mColorPositiveButtonText = colorScheme.neutral1.s50 // N1-50
+            mColorDialogBackground = colorScheme.backgroundColor
+        }
+    }
+
+    override fun getColorConnectedItemBackground() = mColorConnectedItemBackground
+
+    override fun getColorPositiveButtonText() = mColorPositiveButtonText
+
+    override fun getColorDialogBackground() = mColorDialogBackground
+
+    override fun getColorItemContent() = mColorItemContent
+
+    override fun getColorSeekbarProgress() = mColorSeekbarProgress
+
+    override fun getColorButtonBackground() = mColorButtonBackground
+
+    override fun getColorItemBackground() = mColorItemBackground
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 2e602be..163ff24 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -34,6 +34,8 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.res.R;
 
+import java.util.concurrent.Executor;
+
 /**
  * Dialog for media output transferring.
  */
@@ -49,11 +51,14 @@
             MediaSwitchingController mediaSwitchingController,
             DialogTransitionAnimator dialogTransitionAnimator,
             UiEventLogger uiEventLogger,
+            Executor mainExecutor,
+            Executor backgroundExecutor,
             boolean includePlaybackAndAppMetadata) {
         super(context, broadcastSender, mediaSwitchingController, includePlaybackAndAppMetadata);
         mDialogTransitionAnimator = dialogTransitionAnimator;
         mUiEventLogger = uiEventLogger;
-        mAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController);
+        mAdapter = new MediaOutputAdapterLegacy(mMediaSwitchingController, mainExecutor,
+                backgroundExecutor);
         if (!aboveStatusbar) {
             getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
         }
@@ -76,12 +81,6 @@
     }
 
     @Override
-    int getHeaderIconSize() {
-        return mContext.getResources().getDimensionPixelSize(
-                R.dimen.media_output_dialog_header_album_icon_size);
-    }
-
-    @Override
     CharSequence getHeaderText() {
         return mMediaSwitchingController.getHeaderTitle();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt
index 4e9451a..d3a81a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt
@@ -25,6 +25,9 @@
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
 import javax.inject.Inject
 
 /** Manager to create and show a [MediaOutputDialog]. */
@@ -37,6 +40,9 @@
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val mediaSwitchingControllerFactory: MediaSwitchingController.Factory,
 ) {
+    @Inject @Main lateinit var mainExecutor: Executor
+    @Inject @Background lateinit var backgroundExecutor: Executor
+
     companion object {
         const val INTERACTION_JANK_TAG = "media_output"
         var mediaOutputDialog: MediaOutputDialog? = null
@@ -51,7 +57,7 @@
         aboveStatusBar: Boolean,
         view: View? = null,
         userHandle: UserHandle? = null,
-        token: MediaSession.Token? = null
+        token: MediaSession.Token? = null,
     ) {
         createAndShowWithController(
             packageName,
@@ -62,8 +68,8 @@
                         it,
                         DialogCuj(
                             InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                            INTERACTION_JANK_TAG
-                        )
+                            INTERACTION_JANK_TAG,
+                        ),
                     )
                 },
             userHandle = userHandle,
@@ -128,15 +134,14 @@
                 controller,
                 dialogTransitionAnimator,
                 uiEventLogger,
-                includePlaybackAndAppMetadata
+                mainExecutor,
+                backgroundExecutor,
+                includePlaybackAndAppMetadata,
             )
 
         // Show the dialog.
         if (dialogTransitionAnimatorController != null) {
-            dialogTransitionAnimator.show(
-                mediaOutputDialog,
-                dialogTransitionAnimatorController,
-            )
+            dialogTransitionAnimator.show(mediaOutputDialog, dialogTransitionAnimatorController)
         } else {
             mediaOutputDialog.show()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
index 9d37580..d0c6a3e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
@@ -66,6 +66,7 @@
 import androidx.core.graphics.drawable.IconCompat;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.media.flags.Flags;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -78,11 +79,12 @@
 import com.android.settingslib.media.InputRouteManager;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
-import com.android.settingslib.media.flags.Flags;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.dialog.MediaItem.MediaItemType;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
@@ -114,6 +116,8 @@
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import javax.inject.Inject;
+
 /**
  * Controller for a dialog that allows users to switch media output and input devices, control
  * volume, connect to new devices, etc.
@@ -141,7 +145,7 @@
     @VisibleForTesting
     final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
     final List<MediaDevice> mCachedMediaDevices = new CopyOnWriteArrayList<>();
-    private final List<MediaItem> mOutputMediaItemList = new CopyOnWriteArrayList<>();
+    private final OutputMediaItemListProxy mOutputMediaItemListProxy;
     private final List<MediaItem> mInputMediaItemList = new CopyOnWriteArrayList<>();
     private final AudioManager mAudioManager;
     private final PowerExemptionManager mPowerExemptionManager;
@@ -149,7 +153,8 @@
     private final NearbyMediaDevicesManager mNearbyMediaDevicesManager;
     private final Map<String, Integer> mNearbyDeviceInfoMap = new ConcurrentHashMap<>();
     private final MediaSession.Token mToken;
-
+    @Inject @Main Executor mMainExecutor;
+    @Inject @Background Executor mBackgroundExecutor;
     @VisibleForTesting
     boolean mIsRefreshing = false;
     @VisibleForTesting
@@ -163,17 +168,10 @@
     @VisibleForTesting
     MediaOutputMetricLogger mMetricLogger;
     private int mCurrentState;
-
-    private int mColorItemContent;
-    private int mColorSeekbarProgress;
-    private int mColorButtonBackground;
-    private int mColorItemBackground;
-    private int mColorConnectedItemBackground;
-    private int mColorPositiveButtonText;
-    private int mColorDialogBackground;
     private FeatureFlags mFeatureFlags;
     private UserTracker mUserTracker;
     private VolumePanelGlobalStateInteractor mVolumePanelGlobalStateInteractor;
+    @NonNull private MediaOutputColorSchemeLegacy mMediaOutputColorSchemeLegacy;
 
     public enum BroadcastNotifyDialog {
         ACTION_FIRST_LAUNCH,
@@ -228,22 +226,10 @@
                 InfoMediaManager.createInstance(mContext, packageName, userHandle, lbm, token);
         mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
         mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
+        mOutputMediaItemListProxy = new OutputMediaItemListProxy(context);
         mDialogTransitionAnimator = dialogTransitionAnimator;
         mNearbyMediaDevicesManager = nearbyMediaDevicesManager;
-        mColorItemContent = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_item_main_content);
-        mColorSeekbarProgress = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_seekbar_progress);
-        mColorButtonBackground = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_button_background);
-        mColorItemBackground = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_item_background);
-        mColorConnectedItemBackground = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_connected_item_background);
-        mColorPositiveButtonText = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_solid_button_text);
-        mColorDialogBackground = Utils.getColorStateListDefaultColor(mContext,
-                R.color.media_dialog_background);
+        mMediaOutputColorSchemeLegacy = MediaOutputColorSchemeLegacy.fromSystemColors(mContext);
 
         if (enableInputRouting()) {
             mInputRouteManager = new InputRouteManager(mContext, audioManager);
@@ -260,7 +246,7 @@
     protected void start(@NonNull Callback cb) {
         synchronized (mMediaDevicesLock) {
             mCachedMediaDevices.clear();
-            mOutputMediaItemList.clear();
+            mOutputMediaItemListProxy.clear();
         }
         mNearbyDeviceInfoMap.clear();
         if (mNearbyMediaDevicesManager != null) {
@@ -306,7 +292,7 @@
         mLocalMediaManager.stopScan();
         synchronized (mMediaDevicesLock) {
             mCachedMediaDevices.clear();
-            mOutputMediaItemList.clear();
+            mOutputMediaItemListProxy.clear();
         }
         if (mNearbyMediaDevicesManager != null) {
             mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this);
@@ -322,7 +308,8 @@
     }
 
     private MediaController getMediaController() {
-        if (mToken != null && Flags.usePlaybackInfoForRoutingControls()) {
+        if (mToken != null
+                && com.android.settingslib.media.flags.Flags.usePlaybackInfoForRoutingControls()) {
             return new MediaController(mContext, mToken);
         } else {
             for (NotificationEntry entry : mNotifCollection.getAllNotifs()) {
@@ -348,7 +335,7 @@
 
     @Override
     public void onDeviceListUpdate(List<MediaDevice> devices) {
-        boolean isListEmpty = mOutputMediaItemList.isEmpty();
+        boolean isListEmpty = mOutputMediaItemListProxy.isEmpty();
         if (isListEmpty || !mIsRefreshing) {
             buildMediaItems(devices);
             mCallback.onDeviceListChanged();
@@ -362,11 +349,12 @@
     }
 
     @Override
-    public void onSelectedDeviceStateChanged(MediaDevice device,
-            @LocalMediaManager.MediaDeviceState int state) {
+    public void onSelectedDeviceStateChanged(
+            MediaDevice device, @LocalMediaManager.MediaDeviceState int state) {
         mCallback.onRouteChanged();
         mMetricLogger.logOutputItemSuccess(
-                device.toString(), new ArrayList<>(mOutputMediaItemList));
+                device.toString(),
+                new ArrayList<>(mOutputMediaItemListProxy.getOutputMediaItemList()));
     }
 
     @Override
@@ -377,7 +365,8 @@
     @Override
     public void onRequestFailed(int reason) {
         mCallback.onRouteChanged();
-        mMetricLogger.logOutputItemFailure(new ArrayList<>(mOutputMediaItemList), reason);
+        mMetricLogger.logOutputItemFailure(
+                new ArrayList<>(mOutputMediaItemListProxy.getOutputMediaItemList()), reason);
     }
 
     /**
@@ -396,7 +385,7 @@
         }
         try {
             synchronized (mMediaDevicesLock) {
-                mOutputMediaItemList.removeIf((MediaItem::isMutingExpectedDevice));
+                mOutputMediaItemListProxy.removeMutingExpectedDevices();
             }
             mAudioManager.cancelMuteAwaitConnection(mAudioManager.getMutingExpectedDevice());
         } catch (Exception e) {
@@ -568,26 +557,15 @@
         return null;
     }
 
-    void setCurrentColorScheme(WallpaperColors wallpaperColors, boolean isDarkTheme) {
-        ColorScheme mCurrentColorScheme = new ColorScheme(wallpaperColors,
+    void updateCurrentColorScheme(WallpaperColors wallpaperColors, boolean isDarkTheme) {
+        ColorScheme currentColorScheme = new ColorScheme(wallpaperColors,
                 isDarkTheme);
-        if (isDarkTheme) {
-            mColorItemContent = mCurrentColorScheme.getAccent1().getS100(); // A1-100
-            mColorSeekbarProgress = mCurrentColorScheme.getAccent2().getS600(); // A2-600
-            mColorButtonBackground = mCurrentColorScheme.getAccent1().getS300(); // A1-300
-            mColorItemBackground = mCurrentColorScheme.getNeutral2().getS800(); // N2-800
-            mColorConnectedItemBackground = mCurrentColorScheme.getAccent2().getS800(); // A2-800
-            mColorPositiveButtonText = mCurrentColorScheme.getAccent2().getS800(); // A2-800
-            mColorDialogBackground = mCurrentColorScheme.getNeutral1().getS900(); // N1-900
-        } else {
-            mColorItemContent = mCurrentColorScheme.getAccent1().getS800(); // A1-800
-            mColorSeekbarProgress = mCurrentColorScheme.getAccent1().getS300(); // A1-300
-            mColorButtonBackground = mCurrentColorScheme.getAccent1().getS600(); // A1-600
-            mColorItemBackground = mCurrentColorScheme.getAccent2().getS50(); // A2-50
-            mColorConnectedItemBackground = mCurrentColorScheme.getAccent1().getS100(); // A1-100
-            mColorPositiveButtonText = mCurrentColorScheme.getNeutral1().getS50(); // N1-50
-            mColorDialogBackground = mCurrentColorScheme.getBackgroundColor();
-        }
+        mMediaOutputColorSchemeLegacy = MediaOutputColorSchemeLegacy.fromDynamicColors(
+                currentColorScheme, isDarkTheme);
+    }
+
+    MediaOutputColorSchemeLegacy getColorSchemeLegacy() {
+        return mMediaOutputColorSchemeLegacy;
     }
 
     public void refreshDataSetIfNeeded() {
@@ -598,49 +576,37 @@
         }
     }
 
-    public int getColorConnectedItemBackground() {
-        return mColorConnectedItemBackground;
-    }
-
-    public int getColorPositiveButtonText() {
-        return mColorPositiveButtonText;
-    }
-
-    public int getColorDialogBackground() {
-        return mColorDialogBackground;
-    }
-
-    public int getColorItemContent() {
-        return mColorItemContent;
-    }
-
-    public int getColorSeekbarProgress() {
-        return mColorSeekbarProgress;
-    }
-
-    public int getColorButtonBackground() {
-        return mColorButtonBackground;
-    }
-
-    public int getColorItemBackground() {
-        return mColorItemBackground;
-    }
-
     private void buildMediaItems(List<MediaDevice> devices) {
         synchronized (mMediaDevicesLock) {
-            List<MediaItem> updatedMediaItems = buildMediaItems(mOutputMediaItemList, devices);
-            mOutputMediaItemList.clear();
-            mOutputMediaItemList.addAll(updatedMediaItems);
-        }
-    }
-
-    protected List<MediaItem> buildMediaItems(List<MediaItem> oldMediaItems,
-            List<MediaDevice> devices) {
-        synchronized (mMediaDevicesLock) {
             if (!mLocalMediaManager.isPreferenceRouteListingExist()) {
                 attachRangeInfo(devices);
                 Collections.sort(devices, Comparator.naturalOrder());
             }
+            if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
+                // For the first time building list, to make sure the top device is the connected
+                // device.
+                boolean needToHandleMutingExpectedDevice =
+                        hasMutingExpectedDevice() && !isCurrentConnectedDeviceRemote();
+                final MediaDevice connectedMediaDevice =
+                        needToHandleMutingExpectedDevice ? null : getCurrentConnectedMediaDevice();
+                mOutputMediaItemListProxy.updateMediaDevices(
+                        devices,
+                        getSelectedMediaDevice(),
+                        connectedMediaDevice,
+                        needToHandleMutingExpectedDevice,
+                        getConnectNewDeviceItem());
+            } else {
+                List<MediaItem> updatedMediaItems =
+                        buildMediaItems(
+                                mOutputMediaItemListProxy.getOutputMediaItemList(), devices);
+                mOutputMediaItemListProxy.clearAndAddAll(updatedMediaItems);
+            }
+        }
+    }
+
+    protected List<MediaItem> buildMediaItems(
+            List<MediaItem> oldMediaItems, List<MediaDevice> devices) {
+        synchronized (mMediaDevicesLock) {
             // For the first time building list, to make sure the top device is the connected
             // device.
             boolean needToHandleMutingExpectedDevice =
@@ -698,6 +664,26 @@
             List<MediaItem> finalMediaItems = targetMediaDevices.stream()
                     .map(MediaItem::createDeviceMediaItem)
                     .collect(Collectors.toList());
+
+            boolean shouldAddFirstSeenSelectedDevice = Flags.enableOutputSwitcherDeviceGrouping();
+
+            if (shouldAddFirstSeenSelectedDevice) {
+                finalMediaItems.clear();
+                Set<String> selectedDevicesIds = getSelectedMediaDevice().stream()
+                        .map(MediaDevice::getId)
+                        .collect(Collectors.toSet());
+                for (MediaDevice targetMediaDevice : targetMediaDevices) {
+                    if (shouldAddFirstSeenSelectedDevice
+                            && selectedDevicesIds.contains(targetMediaDevice.getId())) {
+                        finalMediaItems.add(MediaItem.createDeviceMediaItem(
+                                targetMediaDevice, /* isFirstDeviceInGroup */ true));
+                        shouldAddFirstSeenSelectedDevice = false;
+                    } else {
+                        finalMediaItems.add(MediaItem.createDeviceMediaItem(
+                                targetMediaDevice, /* isFirstDeviceInGroup */ false));
+                    }
+                }
+            }
             dividerItems.forEach(finalMediaItems::add);
             attachConnectNewDeviceItemIfNeeded(finalMediaItems);
             return finalMediaItems;
@@ -705,7 +691,7 @@
     }
 
     private boolean enableInputRouting() {
-        return com.android.media.flags.Flags.enableAudioInputDeviceRoutingAndVolumeControl();
+        return Flags.enableAudioInputDeviceRoutingAndVolumeControl();
     }
 
     private void buildInputMediaItems(List<MediaDevice> devices) {
@@ -722,7 +708,8 @@
      * list.
      */
     @GuardedBy("mMediaDevicesLock")
-    private List<MediaItem> categorizeMediaItemsLocked(MediaDevice connectedMediaDevice,
+    private List<MediaItem> categorizeMediaItemsLocked(
+            MediaDevice connectedMediaDevice,
             List<MediaDevice> devices,
             boolean needToHandleMutingExpectedDevice) {
         List<MediaItem> finalMediaItems = new ArrayList<>();
@@ -732,8 +719,7 @@
         if (connectedMediaDevice != null) {
             selectedDevicesIds.add(connectedMediaDevice.getId());
         }
-        boolean groupSelectedDevices =
-                com.android.media.flags.Flags.enableOutputSwitcherDeviceGrouping();
+        boolean groupSelectedDevices = Flags.enableOutputSwitcherDeviceGrouping();
         int nextSelectedItemIndex = 0;
         boolean suggestedDeviceAdded = false;
         boolean displayGroupAdded = false;
@@ -781,6 +767,14 @@
     }
 
     private void attachConnectNewDeviceItemIfNeeded(List<MediaItem> mediaItems) {
+        MediaItem connectNewDeviceItem = getConnectNewDeviceItem();
+        if (connectNewDeviceItem != null) {
+            mediaItems.add(connectNewDeviceItem);
+        }
+    }
+
+    @Nullable
+    private MediaItem getConnectNewDeviceItem() {
         boolean isSelectedDeviceNotAGroup = getSelectedMediaDevice().size() == 1;
         if (enableInputRouting()) {
             // When input routing is enabled, there are expected to be at least 2 total selected
@@ -789,9 +783,9 @@
         }
 
         // Attach "Connect a device" item only when current output is not remote and not a group
-        if (!isCurrentConnectedDeviceRemote() && isSelectedDeviceNotAGroup) {
-            mediaItems.add(MediaItem.createPairNewDeviceMediaItem());
-        }
+        return (!isCurrentConnectedDeviceRemote() && isSelectedDeviceNotAGroup)
+                ? MediaItem.createPairNewDeviceMediaItem()
+                : null;
     }
 
     private void attachRangeInfo(List<MediaDevice> devices) {
@@ -880,13 +874,13 @@
         mediaItems.add(
                 MediaItem.createGroupDividerMediaItem(
                         mContext.getString(R.string.media_output_group_title)));
-        mediaItems.addAll(mOutputMediaItemList);
+        mediaItems.addAll(mOutputMediaItemListProxy.getOutputMediaItemList());
     }
 
     public List<MediaItem> getMediaItemList() {
         // If input routing is not enabled, only return output media items.
         if (!enableInputRouting()) {
-            return mOutputMediaItemList;
+            return mOutputMediaItemListProxy.getOutputMediaItemList();
         }
 
         // If input routing is enabled, return both output and input media items.
@@ -900,6 +894,11 @@
         return mLocalMediaManager.getCurrentConnectedDevice();
     }
 
+    @VisibleForTesting
+    void clearMediaItemList() {
+        mOutputMediaItemListProxy.clear();
+    }
+
     boolean addDeviceToPlayMedia(MediaDevice device) {
         mMetricLogger.logInteractionExpansion(device);
         return mLocalMediaManager.addDeviceToPlayMedia(device);
@@ -992,7 +991,7 @@
 
     public boolean isAnyDeviceTransferring() {
         synchronized (mMediaDevicesLock) {
-            for (MediaItem mediaItem : mOutputMediaItemList) {
+            for (MediaItem mediaItem : mOutputMediaItemListProxy.getOutputMediaItemList()) {
                 if (mediaItem.getMediaDevice().isPresent()
                         && mediaItem.getMediaDevice().get().getState()
                         == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
@@ -1032,8 +1031,11 @@
         startActivity(launchIntent, controller);
     }
 
-    void launchLeBroadcastNotifyDialog(View mediaOutputDialog, BroadcastSender broadcastSender,
-            BroadcastNotifyDialog action, final DialogInterface.OnClickListener listener) {
+    void launchLeBroadcastNotifyDialog(
+            View mediaOutputDialog,
+            BroadcastSender broadcastSender,
+            BroadcastNotifyDialog action,
+            final DialogInterface.OnClickListener listener) {
         final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
         switch (action) {
             case ACTION_FIRST_LAUNCH:
@@ -1076,7 +1078,7 @@
                         mVolumePanelGlobalStateInteractor,
                         mUserTracker);
         MediaOutputBroadcastDialog dialog = new MediaOutputBroadcastDialog(mContext, true,
-                broadcastSender, controller);
+                broadcastSender, controller, mMainExecutor, mBackgroundExecutor);
         dialog.show();
     }
 
@@ -1263,8 +1265,8 @@
         return !sourceList.isEmpty();
     }
 
-    boolean addSourceIntoSinkDeviceWithBluetoothLeAssistant(BluetoothDevice sink,
-            BluetoothLeBroadcastMetadata metadata, boolean isGroupOp) {
+    boolean addSourceIntoSinkDeviceWithBluetoothLeAssistant(
+            BluetoothDevice sink, BluetoothLeBroadcastMetadata metadata, boolean isGroupOp) {
         LocalBluetoothLeBroadcastAssistant assistant =
                 mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
         if (assistant == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/OutputMediaItemListProxy.java b/packages/SystemUI/src/com/android/systemui/media/dialog/OutputMediaItemListProxy.java
new file mode 100644
index 0000000..45ca2c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/OutputMediaItemListProxy.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dialog;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.media.flags.Flags;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.res.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
+
+/** A proxy of holding the list of Output Switcher's output media items. */
+public class OutputMediaItemListProxy {
+    private final Context mContext;
+    private final List<MediaItem> mOutputMediaItemList;
+
+    // Use separated lists to hold different media items and create the list of output media items
+    // by using those separated lists and group dividers.
+    private final List<MediaItem> mSelectedMediaItems;
+    private final List<MediaItem> mSuggestedMediaItems;
+    private final List<MediaItem> mSpeakersAndDisplaysMediaItems;
+    @Nullable private MediaItem mConnectNewDeviceMediaItem;
+
+    public OutputMediaItemListProxy(Context context) {
+        mContext = context;
+        mOutputMediaItemList = new CopyOnWriteArrayList<>();
+        mSelectedMediaItems = new CopyOnWriteArrayList<>();
+        mSuggestedMediaItems = new CopyOnWriteArrayList<>();
+        mSpeakersAndDisplaysMediaItems = new CopyOnWriteArrayList<>();
+    }
+
+    /** Returns the list of output media items. */
+    public List<MediaItem> getOutputMediaItemList() {
+        if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
+            if (isEmpty() && !mOutputMediaItemList.isEmpty()) {
+                // Ensures mOutputMediaItemList is empty when all individual media item lists are
+                // empty, preventing unexpected state issues.
+                mOutputMediaItemList.clear();
+            } else if (!isEmpty() && mOutputMediaItemList.isEmpty()) {
+                // When any individual media item list is modified, the cached mOutputMediaItemList
+                // is emptied. On the next request for the output media item list, a fresh list is
+                // created and stored in the cache.
+                mOutputMediaItemList.addAll(createOutputMediaItemList());
+            }
+        }
+        return mOutputMediaItemList;
+    }
+
+    private List<MediaItem> createOutputMediaItemList() {
+        List<MediaItem> finalMediaItems = new CopyOnWriteArrayList<>();
+        finalMediaItems.addAll(mSelectedMediaItems);
+        if (!mSuggestedMediaItems.isEmpty()) {
+            finalMediaItems.add(
+                    MediaItem.createGroupDividerMediaItem(
+                            mContext.getString(
+                                    R.string.media_output_group_title_suggested_device)));
+            finalMediaItems.addAll(mSuggestedMediaItems);
+        }
+        if (!mSpeakersAndDisplaysMediaItems.isEmpty()) {
+            finalMediaItems.add(
+                    MediaItem.createGroupDividerMediaItem(
+                            mContext.getString(
+                                    R.string.media_output_group_title_speakers_and_displays)));
+            finalMediaItems.addAll(mSpeakersAndDisplaysMediaItems);
+        }
+        if (mConnectNewDeviceMediaItem != null) {
+            finalMediaItems.add(mConnectNewDeviceMediaItem);
+        }
+        return finalMediaItems;
+    }
+
+    /** Updates the list of output media items with a given list of media devices. */
+    public void updateMediaDevices(
+            List<MediaDevice> devices,
+            List<MediaDevice> selectedDevices,
+            @Nullable MediaDevice connectedMediaDevice,
+            boolean needToHandleMutingExpectedDevice,
+            @Nullable MediaItem connectNewDeviceMediaItem) {
+        Set<String> selectedOrConnectedMediaDeviceIds =
+                selectedDevices.stream().map(MediaDevice::getId).collect(Collectors.toSet());
+        if (connectedMediaDevice != null) {
+            selectedOrConnectedMediaDeviceIds.add(connectedMediaDevice.getId());
+        }
+
+        List<MediaItem> selectedMediaItems = new ArrayList<>();
+        List<MediaItem> suggestedMediaItems = new ArrayList<>();
+        List<MediaItem> speakersAndDisplaysMediaItems = new ArrayList<>();
+        Map<String, MediaItem> deviceIdToMediaItemMap = new HashMap<>();
+        buildMediaItems(
+                devices,
+                selectedOrConnectedMediaDeviceIds,
+                needToHandleMutingExpectedDevice,
+                selectedMediaItems,
+                suggestedMediaItems,
+                speakersAndDisplaysMediaItems,
+                deviceIdToMediaItemMap);
+
+        List<MediaItem> updatedSelectedMediaItems = new CopyOnWriteArrayList<>();
+        List<MediaItem> updatedSuggestedMediaItems = new CopyOnWriteArrayList<>();
+        List<MediaItem> updatedSpeakersAndDisplaysMediaItems = new CopyOnWriteArrayList<>();
+        if (isEmpty()) {
+            updatedSelectedMediaItems.addAll(selectedMediaItems);
+            updatedSuggestedMediaItems.addAll(suggestedMediaItems);
+            updatedSpeakersAndDisplaysMediaItems.addAll(speakersAndDisplaysMediaItems);
+        } else {
+            Set<String> updatedDeviceIds = new HashSet<>();
+            // Preserve the existing media item order while updating with the latest device
+            // information. Some items may retain their original group (suggested, speakers and
+            // displays) to maintain this order.
+            updateMediaItems(
+                    mSelectedMediaItems,
+                    updatedSelectedMediaItems,
+                    deviceIdToMediaItemMap,
+                    updatedDeviceIds);
+            updateMediaItems(
+                    mSuggestedMediaItems,
+                    updatedSuggestedMediaItems,
+                    deviceIdToMediaItemMap,
+                    updatedDeviceIds);
+            updateMediaItems(
+                    mSpeakersAndDisplaysMediaItems,
+                    updatedSpeakersAndDisplaysMediaItems,
+                    deviceIdToMediaItemMap,
+                    updatedDeviceIds);
+
+            // Append new media items that are not already in the existing lists to the output list.
+            List<MediaItem> remainingMediaItems = new ArrayList<>();
+            remainingMediaItems.addAll(
+                    getRemainingMediaItems(selectedMediaItems, updatedDeviceIds));
+            remainingMediaItems.addAll(
+                    getRemainingMediaItems(suggestedMediaItems, updatedDeviceIds));
+            remainingMediaItems.addAll(
+                    getRemainingMediaItems(speakersAndDisplaysMediaItems, updatedDeviceIds));
+            updatedSpeakersAndDisplaysMediaItems.addAll(remainingMediaItems);
+        }
+
+        if (Flags.enableOutputSwitcherDeviceGrouping() && !updatedSelectedMediaItems.isEmpty()) {
+            MediaItem selectedMediaItem = updatedSelectedMediaItems.get(0);
+            Optional<MediaDevice> mediaDeviceOptional = selectedMediaItem.getMediaDevice();
+            if (mediaDeviceOptional.isPresent()) {
+                MediaItem updatedMediaItem =
+                        MediaItem.createDeviceMediaItem(
+                                mediaDeviceOptional.get(), /* isFirstDeviceInGroup= */ true);
+                updatedSelectedMediaItems.remove(0);
+                updatedSelectedMediaItems.add(0, updatedMediaItem);
+            }
+        }
+
+        mSelectedMediaItems.clear();
+        mSelectedMediaItems.addAll(updatedSelectedMediaItems);
+        mSuggestedMediaItems.clear();
+        mSuggestedMediaItems.addAll(updatedSuggestedMediaItems);
+        mSpeakersAndDisplaysMediaItems.clear();
+        mSpeakersAndDisplaysMediaItems.addAll(updatedSpeakersAndDisplaysMediaItems);
+        mConnectNewDeviceMediaItem = connectNewDeviceMediaItem;
+
+        // The cached mOutputMediaItemList is cleared upon any update to individual media item
+        // lists. This ensures getOutputMediaItemList() computes and caches a fresh list on the next
+        // invocation.
+        mOutputMediaItemList.clear();
+    }
+
+    /** Updates the list of output media items with the given list. */
+    public void clearAndAddAll(List<MediaItem> updatedMediaItems) {
+        mOutputMediaItemList.clear();
+        mOutputMediaItemList.addAll(updatedMediaItems);
+    }
+
+    /** Removes the media items with muting expected devices. */
+    public void removeMutingExpectedDevices() {
+        if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
+            mSelectedMediaItems.removeIf((MediaItem::isMutingExpectedDevice));
+            mSuggestedMediaItems.removeIf((MediaItem::isMutingExpectedDevice));
+            mSpeakersAndDisplaysMediaItems.removeIf((MediaItem::isMutingExpectedDevice));
+            if (mConnectNewDeviceMediaItem != null
+                    && mConnectNewDeviceMediaItem.isMutingExpectedDevice()) {
+                mConnectNewDeviceMediaItem = null;
+            }
+        }
+        mOutputMediaItemList.removeIf((MediaItem::isMutingExpectedDevice));
+    }
+
+    /** Clears the output media item list. */
+    public void clear() {
+        if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
+            mSelectedMediaItems.clear();
+            mSuggestedMediaItems.clear();
+            mSpeakersAndDisplaysMediaItems.clear();
+            mConnectNewDeviceMediaItem = null;
+        }
+        mOutputMediaItemList.clear();
+    }
+
+    /** Returns whether the output media item list is empty. */
+    public boolean isEmpty() {
+        if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
+            return mSelectedMediaItems.isEmpty()
+                    && mSuggestedMediaItems.isEmpty()
+                    && mSpeakersAndDisplaysMediaItems.isEmpty()
+                    && (mConnectNewDeviceMediaItem == null);
+        } else {
+            return mOutputMediaItemList.isEmpty();
+        }
+    }
+
+    private void buildMediaItems(
+            List<MediaDevice> devices,
+            Set<String> selectedOrConnectedMediaDeviceIds,
+            boolean needToHandleMutingExpectedDevice,
+            List<MediaItem> selectedMediaItems,
+            List<MediaItem> suggestedMediaItems,
+            List<MediaItem> speakersAndDisplaysMediaItems,
+            Map<String, MediaItem> deviceIdToMediaItemMap) {
+        for (MediaDevice device : devices) {
+            String deviceId = device.getId();
+            MediaItem mediaItem = MediaItem.createDeviceMediaItem(device);
+            if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) {
+                selectedMediaItems.add(0, mediaItem);
+            } else if (!needToHandleMutingExpectedDevice
+                    && selectedOrConnectedMediaDeviceIds.contains(device.getId())) {
+                if (Flags.enableOutputSwitcherDeviceGrouping()) {
+                    selectedMediaItems.add(mediaItem);
+                } else {
+                    selectedMediaItems.add(0, mediaItem);
+                }
+            } else if (device.isSuggestedDevice()) {
+                suggestedMediaItems.add(mediaItem);
+            } else {
+                speakersAndDisplaysMediaItems.add(mediaItem);
+            }
+            deviceIdToMediaItemMap.put(deviceId, mediaItem);
+        }
+    }
+
+    /** Returns a list of media items that remains the same order as the existing media items. */
+    private void updateMediaItems(
+            List<MediaItem> existingMediaItems,
+            List<MediaItem> updatedMediaItems,
+            Map<String, MediaItem> deviceIdToMediaItemMap,
+            Set<String> updatedDeviceIds) {
+        List<String> existingDeviceIds = getDeviceIds(existingMediaItems);
+        for (String deviceId : existingDeviceIds) {
+            MediaItem mediaItem = deviceIdToMediaItemMap.get(deviceId);
+            if (mediaItem != null) {
+                updatedMediaItems.add(mediaItem);
+                updatedDeviceIds.add(deviceId);
+            }
+        }
+    }
+
+    /**
+     * Returns media items from the input list that are not associated with the given device IDs.
+     */
+    private List<MediaItem> getRemainingMediaItems(
+            List<MediaItem> mediaItems, Set<String> deviceIds) {
+        List<MediaItem> remainingMediaItems = new ArrayList<>();
+        for (MediaItem item : mediaItems) {
+            Optional<MediaDevice> mediaDeviceOptional = item.getMediaDevice();
+            if (mediaDeviceOptional.isPresent()) {
+                String deviceId = mediaDeviceOptional.get().getId();
+                if (!deviceIds.contains(deviceId)) {
+                    remainingMediaItems.add(item);
+                }
+            }
+        }
+        return remainingMediaItems;
+    }
+
+    /** Returns a list of media device IDs for the given list of media items. */
+    private List<String> getDeviceIds(List<MediaItem> mediaItems) {
+        List<String> deviceIds = new ArrayList<>();
+        for (MediaItem item : mediaItems) {
+            if (item != null && item.getMediaDevice().isPresent()) {
+                deviceIds.add(item.getMediaDevice().get().getId());
+            }
+        }
+        return deviceIds;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/domain/interactor/MediaInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/domain/interactor/MediaInteractor.kt
new file mode 100644
index 0000000..afed141
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/domain/interactor/MediaInteractor.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.remedia.domain.interactor
+
+import com.android.systemui.media.remedia.domain.model.MediaSessionModel
+
+/**
+ * Defines interface for classes that can provide business logic in the domain of the media controls
+ * element.
+ */
+interface MediaInteractor {
+
+    /** The list of sessions. Needs to be backed by a compose snapshot state. */
+    val sessions: List<MediaSessionModel>
+
+    /** Seek to [to], in milliseconds on the media session with the given [sessionKey]. */
+    fun seek(sessionKey: Any, to: Long)
+
+    /** Hide the representation of the media session with the given [sessionKey]. */
+    fun hide(sessionKey: Any)
+
+    /** Open media settings. */
+    fun openMediaSettings()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaActionModel.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt
copy to packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaActionModel.kt
index dfeb65b..02e4d7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaActionModel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.data.entity
+package com.android.systemui.media.remedia.domain.model
 
-import android.graphics.drawable.Icon
+import com.android.systemui.common.shared.model.Icon
 
-sealed interface CustomTileDefaults {
+sealed interface MediaActionModel {
+    data class Action(val icon: Icon, val onClick: (() -> Unit)?) : MediaActionModel
 
-    data object Error : CustomTileDefaults
-    data class Result(
-        val icon: Icon,
-        val label: CharSequence,
-    ) : CustomTileDefaults
+    data object ReserveSpace : MediaActionModel
+
+    data object None : MediaActionModel
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaOutputDeviceModel.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaOutputDeviceModel.kt
index ccfb609..d581ae3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaOutputDeviceModel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.media.remedia.domain.model
 
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.common.shared.model.Icon
 
-val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
+data class MediaOutputDeviceModel(val name: String, val icon: Icon, val isInProgress: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaSessionModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaSessionModel.kt
new file mode 100644
index 0000000..e64ce73
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/domain/model/MediaSessionModel.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.remedia.domain.model
+
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.graphics.ImageBitmap
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.media.remedia.shared.model.MediaCardActionButtonLayout
+import com.android.systemui.media.remedia.shared.model.MediaColorScheme
+import com.android.systemui.media.remedia.shared.model.MediaSessionState
+
+/** Data model representing a media session. */
+@Stable
+interface MediaSessionModel {
+    /** Unique identifier. */
+    val key: Any
+
+    val appName: String
+
+    val appIcon: Icon
+
+    val background: ImageBitmap?
+
+    val colorScheme: MediaColorScheme
+
+    val title: String
+
+    val subtitle: String
+
+    val onClick: () -> Unit
+
+    /**
+     * Whether the session is currently active. Under some UIs, only currently active session should
+     * be shown.
+     */
+    val isActive: Boolean
+
+    /** Whether the session can be hidden/dismissed by the user. */
+    val canBeHidden: Boolean
+
+    /**
+     * Whether the session currently supports scrubbing (e.g. moving to a different position iin the
+     * playback.
+     */
+    val canBeScrubbed: Boolean
+
+    val state: MediaSessionState
+
+    /** The position of the playback within the current track. */
+    val positionMs: Long
+
+    /** The total duration of the current track. */
+    val durationMs: Long
+
+    val outputDevice: MediaOutputDeviceModel
+
+    /** How to lay out the action buttons. */
+    val actionButtonLayout: MediaCardActionButtonLayout
+    val playPauseAction: MediaActionModel
+    val leftAction: MediaActionModel
+    val rightAction: MediaActionModel
+    val additionalActions: List<MediaActionModel.Action>
+}
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaActionState.kt
similarity index 75%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaActionState.kt
index 5d14631..c3ce503 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaActionState.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,6 +14,10 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package com.android.systemui.media.remedia.shared.model
 
-parcelable PictureProfileHandle;
+enum class MediaActionState {
+    Enabled,
+    Disabled,
+    Hidden,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaCardActionButtonLayout.kt
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt
copy to packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaCardActionButtonLayout.kt
index dfeb65b..554fb1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaCardActionButtonLayout.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.data.entity
+package com.android.systemui.media.remedia.shared.model
 
-import android.graphics.drawable.Icon
-
-sealed interface CustomTileDefaults {
-
-    data object Error : CustomTileDefaults
-    data class Result(
-        val icon: Icon,
-        val label: CharSequence,
-    ) : CustomTileDefaults
+enum class MediaCardActionButtonLayout {
+    /** Shows the play/pause button and left/right buttons in privileged positions on the card */
+    WithPlayPause,
+    /** Shows all action buttons along the bottom row. */
+    SecondaryActionsOnly,
 }
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaColorScheme.kt
similarity index 71%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaColorScheme.kt
index 5d14631..8dba170 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/shared/model/MediaColorScheme.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,6 +14,8 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package com.android.systemui.media.remedia.shared.model
 
-parcelable PictureProfileHandle;
+import androidx.compose.ui.graphics.Color
+
+data class MediaColorScheme(val primary: Color, val onPrimary: Color)
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/DismissibleHorizontalPager.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/DismissibleHorizontalPager.kt
new file mode 100644
index 0000000..8df916f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/DismissibleHorizontalPager.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.remedia.ui.compose
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PagerScope
+import androidx.compose.foundation.pager.PagerState
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.graphicsLayer
+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.unit.Dp
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.dp
+import com.android.compose.modifiers.thenIf
+import kotlinx.coroutines.launch
+
+/** State for a [DismissibleHorizontalPager] */
+class DismissibleHorizontalPagerState(
+    val isDismissible: Boolean,
+    val isScrollingEnabled: Boolean,
+    val pagerState: PagerState,
+    val offset: Animatable<Float, AnimationVector1D>,
+)
+
+/**
+ * Returns a remembered [DismissibleHorizontalPagerState] that starts at [initialPage] and has
+ * [pageCount] total pages.
+ */
+@Composable
+fun rememberDismissibleHorizontalPagerState(
+    isDismissible: Boolean = true,
+    isScrollingEnabled: Boolean = true,
+    initialPage: Int = 0,
+    pageCount: () -> Int,
+): DismissibleHorizontalPagerState {
+    val pagerState = rememberPagerState(initialPage = initialPage, pageCount = pageCount)
+    val offset = remember { Animatable(0f) }
+
+    return remember(isDismissible, isScrollingEnabled, pagerState, offset) {
+        DismissibleHorizontalPagerState(
+            isDismissible = isDismissible,
+            isScrollingEnabled = isScrollingEnabled,
+            pagerState = pagerState,
+            offset = offset,
+        )
+    }
+}
+
+/**
+ * A [HorizontalPager] that can be swiped-away to dismiss by the user when swiped farther left or
+ * right once fully scrolled to the left-most or right-most page, respectively.
+ */
+@Composable
+fun DismissibleHorizontalPager(
+    state: DismissibleHorizontalPagerState,
+    onDismissed: () -> Unit,
+    modifier: Modifier = Modifier,
+    key: ((Int) -> Any)? = null,
+    pageSpacing: Dp = 0.dp,
+    isFalseTouchDetected: Boolean,
+    indicator: @Composable BoxScope.() -> Unit,
+    pageContent: @Composable PagerScope.(page: Int) -> Unit,
+) {
+    val scope = rememberCoroutineScope()
+
+    val nestedScrollConnection = remember {
+        object : NestedScrollConnection {
+            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+                return if (state.offset.value > 0f && available.x < 0f) {
+                    scope.launch { state.offset.snapTo(state.offset.value + available.x) }
+                    Offset(available.x, 0f)
+                } else if (state.offset.value < 0f && available.x > 0f) {
+                    scope.launch { state.offset.snapTo(state.offset.value + available.x) }
+                    Offset(available.x, 0f)
+                } else {
+                    Offset.Zero
+                }
+            }
+
+            override fun onPostScroll(
+                consumed: Offset,
+                available: Offset,
+                source: NestedScrollSource,
+            ): Offset {
+                return if (available.x > 0f) {
+                    scope.launch { state.offset.snapTo(state.offset.value + available.x) }
+                    Offset(available.x, 0f)
+                } else if (available.x < 0f) {
+                    scope.launch { state.offset.snapTo(state.offset.value + available.x) }
+                    Offset(available.x, 0f)
+                } else {
+                    Offset.Zero
+                }
+            }
+
+            override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+                scope.launch {
+                    state.offset.animateTo(
+                        if (state.offset.value >= state.pagerState.layoutInfo.pageSize / 2f) {
+                            state.pagerState.layoutInfo.pageSize * 2f
+                        } else if (
+                            state.offset.value <= -state.pagerState.layoutInfo.pageSize / 2f
+                        ) {
+                            -state.pagerState.layoutInfo.pageSize * 2f
+                        } else {
+                            0f
+                        }
+                    )
+                    if (state.offset.value != 0f) {
+                        onDismissed()
+                    }
+                }
+                return super.onPostFling(consumed, available)
+            }
+        }
+    }
+
+    Box(modifier = modifier) {
+        HorizontalPager(
+            state = state.pagerState,
+            userScrollEnabled = state.isScrollingEnabled && !isFalseTouchDetected,
+            key = key,
+            pageSpacing = pageSpacing,
+            pageContent = pageContent,
+            modifier =
+                Modifier.thenIf(state.isDismissible) {
+                    Modifier.nestedScroll(nestedScrollConnection).graphicsLayer {
+                        translationX = state.offset.value
+                    }
+                },
+        )
+
+        indicator()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
index c9fb8e8..f072388 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
@@ -20,6 +20,7 @@
 
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.Crossfade
+import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.RepeatMode
@@ -35,7 +36,10 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.hoverable
+import androidx.compose.foundation.indication
 import androidx.compose.foundation.interaction.DragInteraction
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -43,9 +47,9 @@
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
@@ -63,10 +67,13 @@
 import androidx.compose.material3.SliderDefaults.colors
 import androidx.compose.material3.SliderState
 import androidx.compose.material3.Text
+import androidx.compose.material3.ripple
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -87,15 +94,23 @@
 import androidx.compose.ui.graphics.drawscope.clipRect
 import androidx.compose.ui.graphics.drawscope.translate
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerEventType
+import androidx.compose.ui.input.pointer.PointerInputChange
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastForEachIndexed
+import androidx.compose.ui.util.fastRoundToInt
 import com.android.compose.PlatformButton
 import com.android.compose.PlatformIconButton
 import com.android.compose.PlatformOutlinedButton
@@ -105,19 +120,148 @@
 import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.compose.animation.scene.rememberMutableSceneTransitionLayoutState
 import com.android.compose.animation.scene.transitions
-import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.compose.ui.graphics.painter.rememberDrawablePainter
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.common.ui.compose.PagerDots
 import com.android.systemui.common.ui.compose.load
 import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.media.remedia.shared.model.MediaCardActionButtonLayout
+import com.android.systemui.media.remedia.shared.model.MediaColorScheme
 import com.android.systemui.media.remedia.shared.model.MediaSessionState
 import com.android.systemui.media.remedia.ui.viewmodel.MediaCardGutsViewModel
 import com.android.systemui.media.remedia.ui.viewmodel.MediaCardViewModel
+import com.android.systemui.media.remedia.ui.viewmodel.MediaCarouselVisibility
+import com.android.systemui.media.remedia.ui.viewmodel.MediaNavigationViewModel
 import com.android.systemui.media.remedia.ui.viewmodel.MediaOutputSwitcherChipViewModel
 import com.android.systemui.media.remedia.ui.viewmodel.MediaPlayPauseActionViewModel
 import com.android.systemui.media.remedia.ui.viewmodel.MediaSecondaryActionViewModel
-import com.android.systemui.media.remedia.ui.viewmodel.MediaSeekBarViewModel
+import com.android.systemui.media.remedia.ui.viewmodel.MediaViewModel
 import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * Renders a media controls UI element.
+ *
+ * This composable supports a multitude of presentation styles/layouts controlled by the
+ * [presentationStyle] parameter. If the card carousel can be swiped away to dismiss by the user,
+ * the [onDismissed] callback will be invoked when/if that happens.
+ */
+@Composable
+fun Media(
+    viewModelFactory: MediaViewModel.Factory,
+    presentationStyle: MediaPresentationStyle,
+    behavior: MediaUiBehavior,
+    onDismissed: () -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    val context = LocalContext.current
+    val viewModel: MediaViewModel =
+        rememberViewModel("Media.viewModel") {
+            viewModelFactory.create(
+                context = context,
+                carouselVisibility = behavior.carouselVisibility,
+            )
+        }
+
+    CardCarousel(
+        viewModel = viewModel,
+        presentationStyle = presentationStyle,
+        behavior = behavior,
+        onDismissed = onDismissed,
+        modifier = modifier,
+    )
+}
+
+/**
+ * Renders a media controls carousel of cards.
+ *
+ * This composable supports a multitude of presentation styles/layouts controlled by the
+ * [presentationStyle] parameter. The behavior is controlled by [behavior]. If
+ * [MediaUiBehavior.isCarouselDismissible] is `true`, the [onDismissed] callback will be invoked
+ * when/if that happens.
+ */
+@Composable
+private fun CardCarousel(
+    viewModel: MediaViewModel,
+    presentationStyle: MediaPresentationStyle,
+    behavior: MediaUiBehavior,
+    onDismissed: () -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    AnimatedVisibility(visible = viewModel.isCarouselVisible, modifier = modifier) {
+        CardCarouselContent(
+            viewModel = viewModel,
+            presentationStyle = presentationStyle,
+            behavior = behavior,
+            onDismissed = onDismissed,
+        )
+    }
+}
+
+@Composable
+private fun CardCarouselContent(
+    viewModel: MediaViewModel,
+    presentationStyle: MediaPresentationStyle,
+    behavior: MediaUiBehavior,
+    onDismissed: () -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    val pagerState =
+        rememberDismissibleHorizontalPagerState(
+            isDismissible = behavior.isCarouselDismissible,
+            isScrollingEnabled = behavior.isCarouselScrollingEnabled,
+        ) {
+            viewModel.cards.size
+        }
+    var isFalseTouchDetected: Boolean by
+        remember(behavior.isCarouselScrollFalseTouch) { mutableStateOf(false) }
+
+    val roundedCornerShape = RoundedCornerShape(32.dp)
+
+    LaunchedEffect(pagerState.pagerState.currentPage) {
+        viewModel.onCardSelected(pagerState.pagerState.currentPage)
+    }
+
+    DismissibleHorizontalPager(
+        state = pagerState,
+        onDismissed = onDismissed,
+        pageSpacing = 8.dp,
+        key = { index -> viewModel.cards[index].key },
+        indicator = {
+            if (pagerState.pagerState.pageCount > 1) {
+                PagerDots(
+                    pagerState = pagerState.pagerState,
+                    activeColor = Color(0xffdee0ff),
+                    nonActiveColor = Color(0xffa7a9ca),
+                    dotSize = 6.dp,
+                    spaceSize = 6.dp,
+                    modifier =
+                        Modifier.align(Alignment.BottomCenter).padding(8.dp).graphicsLayer {
+                            translationX = pagerState.offset.value
+                        },
+                )
+            }
+        },
+        isFalseTouchDetected = isFalseTouchDetected,
+        modifier =
+            modifier.padding(8.dp).clip(roundedCornerShape).pointerInput(behavior) {
+                if (behavior.isCarouselScrollFalseTouch != null) {
+                    awaitEachGesture {
+                        awaitFirstDown(false, PointerEventPass.Initial)
+                        isFalseTouchDetected = behavior.isCarouselScrollFalseTouch.invoke()
+                    }
+                }
+            },
+    ) { index ->
+        Card(
+            viewModel = viewModel.cards[index],
+            presentationStyle = presentationStyle,
+            modifier = Modifier.clip(roundedCornerShape),
+        )
+    }
+}
 
 /** Renders the UI of a single media card. */
 @Composable
@@ -139,7 +283,7 @@
 
     Box(modifier) {
         if (stlState.currentScene != Media.Scenes.Compact) {
-            CardBackground(imageLoader = viewModel.artLoader, modifier = Modifier.matchParentSize())
+            CardBackground(image = viewModel.background, modifier = Modifier.matchParentSize())
         }
 
         key(stlState) {
@@ -158,6 +302,22 @@
     }
 }
 
+@Composable
+private fun rememberAnimatedColorScheme(colorScheme: MediaColorScheme): AnimatedColorScheme {
+    val animatedPrimary by animateColorAsState(targetValue = colorScheme.primary)
+    val animatedOnPrimary by animateColorAsState(targetValue = colorScheme.onPrimary)
+
+    return remember {
+        object : AnimatedColorScheme {
+            override val primary: Color
+                get() = animatedPrimary
+
+            override val onPrimary: Color
+                get() = animatedOnPrimary
+        }
+    }
+}
+
 /**
  * Renders the foreground of a card, including all UI content and the internal "guts".
  *
@@ -180,6 +340,8 @@
     val isGutsVisible = viewModel.guts.isVisible
     LaunchedEffect(isGutsVisible) { gutsAlphaAnimatable.animateTo(if (isGutsVisible) 1f else 0f) }
 
+    val colorScheme = rememberAnimatedColorScheme(viewModel.colorScheme)
+
     // Use a custom layout to measure the content even if the content is being hidden because the
     // internal guts are showing. This is needed because only the content knows the size the of the
     // card and the guts are set to be the same size of the content.
@@ -189,6 +351,7 @@
                 viewModel = viewModel,
                 threeRows = threeRows,
                 fillHeight = fillHeight,
+                colorScheme = colorScheme,
                 modifier =
                     Modifier.graphicsLayer {
                         compositingStrategy = CompositingStrategy.ModulateAlpha
@@ -198,6 +361,7 @@
 
             CardGuts(
                 viewModel = viewModel.guts,
+                colorScheme = colorScheme,
                 modifier =
                     Modifier.graphicsLayer {
                         compositingStrategy = CompositingStrategy.ModulateAlpha
@@ -232,29 +396,81 @@
     viewModel: MediaCardViewModel,
     threeRows: Boolean,
     fillHeight: Boolean,
+    colorScheme: AnimatedColorScheme,
     modifier: Modifier = Modifier,
 ) {
     Column(
         modifier =
-            modifier
-                .combinedClickable(
-                    onClick = viewModel.onClick,
-                    onLongClick = viewModel.onLongClick,
-                    onClickLabel = viewModel.onClickLabel,
-                )
-                .padding(16.dp)
+            modifier.combinedClickable(
+                onClick = viewModel.onClick,
+                onLongClick = viewModel.onLongClick,
+                onClickLabel = viewModel.onClickLabel,
+            )
     ) {
         // Always add the first/top row, regardless of presentation style.
-        Row(verticalAlignment = Alignment.CenterVertically) {
+        Box(modifier = Modifier.fillMaxWidth()) {
             // Icon.
             Icon(
                 icon = viewModel.icon,
-                tint = LocalAndroidColorScheme.current.primaryFixed,
-                modifier = Modifier.size(24.dp).clip(CircleShape),
+                tint = colorScheme.primary,
+                modifier =
+                    Modifier.align(Alignment.TopStart)
+                        .padding(top = 16.dp, start = 16.dp)
+                        .size(24.dp)
+                        .clip(CircleShape),
             )
-            Spacer(modifier = Modifier.weight(1f))
-            viewModel.outputSwitcherChips.fastForEach { chip ->
-                OutputSwitcherChip(viewModel = chip, modifier = Modifier.padding(start = 8.dp))
+
+            var cardMaxWidth: Int by remember { mutableIntStateOf(0) }
+            Row(
+                horizontalArrangement = Arrangement.spacedBy(8.dp),
+                modifier =
+                    Modifier.align(Alignment.TopEnd)
+                        // Output switcher chips must each be limited to at most 40% of the maximum
+                        // width of the card.
+                        //
+                        // This saves the maximum possible width of the card so it can be referred
+                        // to by child custom layout code below.
+                        //
+                        // The assumption is that the row can be as wide as the entire card.
+                        .layout { measurable, constraints ->
+                            cardMaxWidth = constraints.maxWidth
+                            val placeable = measurable.measure(constraints)
+
+                            layout(placeable.measuredWidth, placeable.measuredHeight) {
+                                placeable.place(0, 0)
+                            }
+                        },
+            ) {
+                viewModel.outputSwitcherChips.fastForEach { chip ->
+                    OutputSwitcherChip(
+                        viewModel = chip,
+                        colorScheme = colorScheme,
+                        modifier =
+                            Modifier
+                                // Each chip must be limited to 40% of the width of the card at
+                                // most.
+                                //
+                                // The underlying assumption is that there'll never be more than one
+                                // chip with text and one more icon-only chip. Only the one with
+                                // text can ever end up being too wide.
+                                .layout { measurable, constraints ->
+                                    val placeable =
+                                        measurable.measure(
+                                            constraints.copy(
+                                                maxWidth =
+                                                    min(
+                                                        (cardMaxWidth * 0.4f).fastRoundToInt(),
+                                                        constraints.maxWidth,
+                                                    )
+                                            )
+                                        )
+
+                                    layout(placeable.measuredWidth, placeable.measuredHeight) {
+                                        placeable.place(0, 0)
+                                    }
+                                },
+                    )
+                }
             }
         }
 
@@ -271,7 +487,7 @@
             // Second row.
             Row(
                 verticalAlignment = Alignment.CenterVertically,
-                modifier = Modifier.padding(top = 16.dp),
+                modifier = Modifier.padding(start = 16.dp, top = 16.dp, end = 16.dp),
             ) {
                 Metadata(
                     title = viewModel.title,
@@ -280,14 +496,16 @@
                     modifier = Modifier.weight(1f).padding(end = 8.dp),
                 )
 
-                AnimatedVisibility(visible = viewModel.playPauseAction.isVisible) {
-                    PlayPauseAction(
-                        viewModel = viewModel.playPauseAction,
-                        buttonWidth = 48.dp,
-                        buttonColor = LocalAndroidColorScheme.current.primaryFixed,
-                        iconColor = LocalAndroidColorScheme.current.onPrimaryFixed,
-                        buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp },
-                    )
+                if (viewModel.actionButtonLayout == MediaCardActionButtonLayout.WithPlayPause) {
+                    AnimatedVisibility(visible = viewModel.playPauseAction != null) {
+                        PlayPauseAction(
+                            viewModel = checkNotNull(viewModel.playPauseAction),
+                            buttonWidth = 48.dp,
+                            buttonColor = colorScheme.primary,
+                            iconColor = colorScheme.onPrimary,
+                            buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp },
+                        )
+                    }
                 }
             }
 
@@ -295,9 +513,16 @@
             Row(
                 horizontalArrangement = Arrangement.spacedBy(8.dp),
                 verticalAlignment = Alignment.CenterVertically,
-                modifier = Modifier.padding(top = 24.dp),
+                modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp, bottom = 16.dp),
             ) {
-                Navigation(viewModel = viewModel.seekBar, isSeekBarVisible = true)
+                Navigation(
+                    viewModel = viewModel.navigation,
+                    isSeekBarVisible = true,
+                    areActionsVisible =
+                        viewModel.actionButtonLayout == MediaCardActionButtonLayout.WithPlayPause,
+                    modifier = Modifier.weight(1f),
+                )
+
                 viewModel.additionalActions.fastForEachIndexed { index, action ->
                     SecondaryAction(
                         viewModel = action,
@@ -311,7 +536,7 @@
             // Bottom row.
             Row(
                 verticalAlignment = Alignment.CenterVertically,
-                modifier = Modifier.padding(top = 36.dp),
+                modifier = Modifier.padding(start = 16.dp, top = 36.dp, end = 16.dp, bottom = 16.dp),
             ) {
                 Metadata(
                     title = viewModel.title,
@@ -321,18 +546,34 @@
                 )
 
                 Navigation(
-                    viewModel = viewModel.seekBar,
+                    viewModel = viewModel.navigation,
                     isSeekBarVisible = false,
+                    areActionsVisible =
+                        viewModel.actionButtonLayout == MediaCardActionButtonLayout.WithPlayPause,
                     modifier = Modifier.padding(end = 8.dp),
                 )
 
-                PlayPauseAction(
-                    viewModel = viewModel.playPauseAction,
-                    buttonWidth = 48.dp,
-                    buttonColor = LocalAndroidColorScheme.current.primaryFixed,
-                    iconColor = LocalAndroidColorScheme.current.onPrimaryFixed,
-                    buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp },
-                )
+                if (
+                    viewModel.actionButtonLayout == MediaCardActionButtonLayout.SecondaryActionsOnly
+                ) {
+                    viewModel.additionalActions.fastForEachIndexed { index, action ->
+                        SecondaryAction(
+                            viewModel = action,
+                            element = Media.Elements.additionalActionButton(index),
+                            modifier = Modifier.padding(end = 8.dp),
+                        )
+                    }
+                }
+
+                AnimatedVisibility(visible = viewModel.playPauseAction != null) {
+                    PlayPauseAction(
+                        viewModel = checkNotNull(viewModel.playPauseAction),
+                        buttonWidth = 48.dp,
+                        buttonColor = colorScheme.primary,
+                        iconColor = colorScheme.onPrimary,
+                        buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp },
+                    )
+                }
             }
         }
     }
@@ -375,18 +616,18 @@
             iconColor = MaterialTheme.colorScheme.onSurface,
         )
 
-        val nextAction = (viewModel.seekBar as? MediaSeekBarViewModel.Showing)?.next
-        if (nextAction != null) {
+        val rightAction = (viewModel.navigation as? MediaNavigationViewModel.Showing)?.right
+        if (rightAction != null) {
             SecondaryAction(
-                viewModel = nextAction,
+                viewModel = rightAction,
                 element = Media.Elements.NextButton,
                 iconColor = MaterialTheme.colorScheme.onSurface,
             )
         }
 
-        AnimatedVisibility(visible = viewModel.playPauseAction.isVisible) {
+        AnimatedVisibility(visible = viewModel.playPauseAction != null) {
             PlayPauseAction(
-                viewModel = viewModel.playPauseAction,
+                viewModel = checkNotNull(viewModel.playPauseAction),
                 buttonWidth = 72.dp,
                 buttonColor = MaterialTheme.colorScheme.primaryContainer,
                 iconColor = MaterialTheme.colorScheme.onPrimaryContainer,
@@ -398,47 +639,38 @@
 
 /** Renders the background of a card, loading the artwork and showing an overlay on top of it. */
 @Composable
-private fun CardBackground(imageLoader: suspend () -> ImageBitmap, modifier: Modifier = Modifier) {
-    var image: ImageBitmap? by remember { mutableStateOf(null) }
-    LaunchedEffect(imageLoader) {
-        image = null
-        image = imageLoader()
-    }
-
-    val gradientBaseColor = MaterialTheme.colorScheme.onSurface
-    Box(
-        modifier =
-            modifier.drawWithContent {
-                // Draw the content of the box (loaded art or placeholder).
-                drawContent()
-
-                if (image != null) {
-                    // Then draw the overlay.
-                    drawRect(
-                        brush =
-                            Brush.radialGradient(
-                                0f to gradientBaseColor.copy(alpha = 0.65f),
-                                1f to gradientBaseColor.copy(alpha = 0.75f),
-                                center = size.center,
-                                radius = max(size.width, size.height) / 2,
-                            )
-                    )
-                }
-            }
-    ) {
-        image?.let { loadedImage ->
+private fun CardBackground(image: ImageBitmap?, modifier: Modifier = Modifier) {
+    Crossfade(targetState = image, modifier = modifier) { imageOrNull ->
+        if (imageOrNull != null) {
             // Loaded art.
+            val gradientBaseColor = MaterialTheme.colorScheme.onSurface
             Image(
-                bitmap = loadedImage,
+                bitmap = imageOrNull,
                 contentDescription = null,
                 contentScale = ContentScale.Crop,
-                modifier = Modifier.matchParentSize(),
+                modifier =
+                    Modifier.fillMaxSize().drawWithContent {
+                        // Draw the content (loaded art).
+                        drawContent()
+
+                        if (image != null) {
+                            // Then draw the overlay.
+                            drawRect(
+                                brush =
+                                    Brush.radialGradient(
+                                        0f to gradientBaseColor.copy(alpha = 0.65f),
+                                        1f to gradientBaseColor.copy(alpha = 0.75f),
+                                        center = size.center,
+                                        radius = max(size.width, size.height) / 2,
+                                    )
+                            )
+                        }
+                    },
             )
+        } else {
+            // Placeholder.
+            Box(Modifier.background(MaterialTheme.colorScheme.onSurface).fillMaxSize())
         }
-            ?: run {
-                // Placeholder.
-                Box(Modifier.background(MaterialTheme.colorScheme.onSurface).matchParentSize())
-            }
     }
 }
 
@@ -449,22 +681,26 @@
  * would otherwise be showing based on the view-model alone. This is meant for callers to decide
  * whether they'd like to show the seek bar in addition to the prev/next buttons or just show the
  * buttons.
+ *
+ * If [areActionsVisible] is `false`, the left/right buttons to the left and right of the seek bar
+ * will not be included in the layout.
  */
 @Composable
 private fun ContentScope.Navigation(
-    viewModel: MediaSeekBarViewModel,
+    viewModel: MediaNavigationViewModel,
     isSeekBarVisible: Boolean,
+    areActionsVisible: Boolean,
     modifier: Modifier = Modifier,
 ) {
     when (viewModel) {
-        is MediaSeekBarViewModel.Showing -> {
+        is MediaNavigationViewModel.Showing -> {
             Row(
                 horizontalArrangement = Arrangement.spacedBy(8.dp),
                 verticalAlignment = Alignment.CenterVertically,
                 modifier = modifier,
             ) {
-                viewModel.previous?.let {
-                    SecondaryAction(viewModel = it, element = Media.Elements.PrevButton)
+                if (areActionsVisible) {
+                    SecondaryAction(viewModel = viewModel.left, element = Media.Elements.PrevButton)
                 }
 
                 val interactionSource = remember { MutableInteractionSource() }
@@ -477,11 +713,20 @@
                 if (isSeekBarVisible) {
                     // To allow the seek bar slider to fade in and out, it's tagged as an element.
                     Element(key = Media.Elements.SeekBarSlider, modifier = Modifier.weight(1f)) {
+                        val sliderDragDelta = remember {
+                            // Not a mutableStateOf - this is never accessed in composition and
+                            // using an anonymous object avoids generics boxing of inline Offset.
+                            object {
+                                var value = Offset.Zero
+                            }
+                        }
                         Slider(
                             interactionSource = interactionSource,
                             value = viewModel.progress,
                             onValueChange = { progress -> viewModel.onScrubChange(progress) },
-                            onValueChangeFinished = { viewModel.onScrubFinished() },
+                            onValueChangeFinished = {
+                                viewModel.onScrubFinished(sliderDragDelta.value)
+                            },
                             colors = colors,
                             thumb = {
                                 SeekBarThumb(interactionSource = interactionSource, colors = colors)
@@ -494,18 +739,58 @@
                                     modifier = Modifier.fillMaxWidth(),
                                 )
                             },
-                            modifier = Modifier.fillMaxWidth(),
+                            modifier =
+                                Modifier.fillMaxWidth()
+                                    .clearAndSetSemantics {
+                                        contentDescription = viewModel.contentDescription
+                                    }
+                                    .pointerInput(Unit) {
+                                        // Track and report the drag delta to the view-model so it
+                                        // can
+                                        // decide whether to accept the next onValueChangeFinished
+                                        // or
+                                        // reject it if the drag was overly vertical.
+                                        awaitPointerEventScope {
+                                            var down: PointerInputChange? = null
+                                            while (true) {
+                                                val event =
+                                                    awaitPointerEvent(PointerEventPass.Initial)
+                                                when (event.type) {
+                                                    PointerEventType.Press -> {
+                                                        // A new gesture has begun. Record the
+                                                        // initial
+                                                        // down input change.
+                                                        down = event.changes.last()
+                                                    }
+
+                                                    PointerEventType.Move -> {
+                                                        // The pointer has moved. If it's the same
+                                                        // pointer as the latest down, calculate and
+                                                        // report the drag delta.
+                                                        val change = event.changes.last()
+                                                        if (change.id == down?.id) {
+                                                            sliderDragDelta.value =
+                                                                change.position - down.position
+                                                        }
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    },
                         )
                     }
                 }
 
-                viewModel.next?.let {
-                    SecondaryAction(viewModel = it, element = Media.Elements.NextButton)
+                if (areActionsVisible) {
+                    SecondaryAction(
+                        viewModel = viewModel.right,
+                        element = Media.Elements.NextButton,
+                    )
                 }
             }
         }
 
-        is MediaSeekBarViewModel.Hidden -> Unit
+        is MediaNavigationViewModel.Hidden -> Unit
     }
 }
 
@@ -647,7 +932,11 @@
 
 /** Renders the internal "guts" of a card. */
 @Composable
-private fun CardGuts(viewModel: MediaCardGutsViewModel, modifier: Modifier = Modifier) {
+private fun CardGuts(
+    viewModel: MediaCardGutsViewModel,
+    colorScheme: AnimatedColorScheme,
+    modifier: Modifier = Modifier,
+) {
     Box(
         modifier =
             modifier.pointerInput(Unit) { detectLongPressGesture { viewModel.onLongClick() } }
@@ -682,7 +971,7 @@
                 ) {
                     Text(
                         text = checkNotNull(viewModel.primaryAction.text),
-                        color = LocalAndroidColorScheme.current.onPrimaryFixed,
+                        color = colorScheme.onPrimary,
                     )
                 }
 
@@ -740,29 +1029,53 @@
 @Composable
 private fun OutputSwitcherChip(
     viewModel: MediaOutputSwitcherChipViewModel,
+    colorScheme: AnimatedColorScheme,
     modifier: Modifier = Modifier,
 ) {
-    PlatformButton(
-        onClick = viewModel.onClick,
-        colors =
-            ButtonDefaults.buttonColors(
-                containerColor = LocalAndroidColorScheme.current.primaryFixed
-            ),
-        contentPadding = PaddingValues(start = 8.dp, end = 12.dp, top = 4.dp, bottom = 4.dp),
-        modifier = modifier.height(24.dp),
+    // For accessibility reasons, the touch area for the chip needs to be at least 48dp in height.
+    // At the same time, the rounded corner chip should only be as tall as it needs to be to contain
+    // its contents and look like a nice design; also, the ripple effect should only be shown within
+    // the bounds of the chip.
+    //
+    // This is achieved by sharing this InteractionSource between the outer and inner composables.
+    //
+    // The outer composable hosts that clickable that writes user events into the InteractionSource.
+    // The inner composable consumes the user events from the InteractionSource and feeds them into
+    // its indication.
+    val clickInteractionSource = remember { MutableInteractionSource() }
+    Box(
+        modifier =
+            modifier
+                .height(48.dp)
+                .clickable(interactionSource = clickInteractionSource, indication = null) {
+                    viewModel.onClick()
+                }
+                .padding(top = 16.dp, end = 16.dp, bottom = 8.dp)
     ) {
-        Icon(
-            icon = viewModel.icon,
-            tint = LocalAndroidColorScheme.current.onPrimaryFixed,
-            modifier = Modifier.size(16.dp),
-        )
-        viewModel.text?.let {
-            Spacer(Modifier.size(4.dp))
-            Text(
-                text = viewModel.text,
-                style = MaterialTheme.typography.bodySmall,
-                color = LocalAndroidColorScheme.current.onPrimaryFixed,
+        Row(
+            horizontalArrangement = Arrangement.spacedBy(4.dp),
+            verticalAlignment = Alignment.CenterVertically,
+            modifier =
+                Modifier.clip(RoundedCornerShape(12.dp))
+                    .background(colorScheme.primary)
+                    .indication(clickInteractionSource, ripple())
+                    .padding(start = 8.dp, end = 12.dp, top = 4.dp, bottom = 4.dp),
+        ) {
+            Icon(
+                icon = viewModel.icon,
+                tint = colorScheme.onPrimary,
+                modifier = Modifier.size(16.dp),
             )
+
+            viewModel.text?.let {
+                Text(
+                    text = viewModel.text,
+                    style = MaterialTheme.typography.bodySmall,
+                    color = colorScheme.onPrimary,
+                    maxLines = 1,
+                    overflow = TextOverflow.Ellipsis,
+                )
+            }
         }
     }
 }
@@ -785,7 +1098,8 @@
     // This element can be animated when switching between scenes inside a media card.
     Element(key = Media.Elements.PlayPauseButton, modifier = modifier) {
         PlatformButton(
-            onClick = viewModel.onClick,
+            onClick = viewModel.onClick ?: {},
+            enabled = viewModel.onClick != null,
             colors = ButtonDefaults.buttonColors(containerColor = buttonColor),
             shape = RoundedCornerShape(cornerRadius),
             modifier = Modifier.size(width = buttonWidth, height = 48.dp),
@@ -793,22 +1107,29 @@
             when (viewModel.state) {
                 is MediaSessionState.Playing,
                 is MediaSessionState.Paused -> {
-                    // TODO(b/399860531): load this expensive-to-load animated vector drawable off
-                    //  the main thread.
-                    val iconResource = checkNotNull(viewModel.icon)
-                    Icon(
-                        painter =
-                            rememberAnimatedVectorPainter(
-                                animatedImageVector =
-                                    AnimatedImageVector.animatedVectorResource(
-                                        id = iconResource.res
-                                    ),
-                                atEnd = viewModel.state == MediaSessionState.Playing,
-                            ),
-                        contentDescription = iconResource.contentDescription?.load(),
-                        tint = iconColor,
-                        modifier = Modifier.size(24.dp),
-                    )
+                    val painterOrNull =
+                        when (viewModel.icon) {
+                            // TODO(b/399860531): load this expensive-to-load animated vector
+                            //  drawable off the main thread.
+                            is Icon.Resource ->
+                                rememberAnimatedVectorPainter(
+                                    animatedImageVector =
+                                        AnimatedImageVector.animatedVectorResource(
+                                            id = viewModel.icon.res
+                                        ),
+                                    atEnd = viewModel.state == MediaSessionState.Playing,
+                                )
+                            is Icon.Loaded -> rememberDrawablePainter(viewModel.icon.drawable)
+                            null -> null
+                        }
+                    painterOrNull?.let { painter ->
+                        Icon(
+                            painter = painter,
+                            contentDescription = viewModel.icon?.contentDescription?.load(),
+                            tint = iconColor,
+                            modifier = Modifier.size(24.dp),
+                        )
+                    }
                 }
                 is MediaSessionState.Buffering -> {
                     CircularProgressIndicator(color = iconColor, modifier = Modifier.size(24.dp))
@@ -831,7 +1152,7 @@
     element: ElementKey? = null,
     iconColor: Color = Color.White,
 ) {
-    if (element != null) {
+    if (viewModel !is MediaSecondaryActionViewModel.None && element != null) {
         Element(key = element, modifier = modifier) {
             SecondaryActionContent(viewModel = viewModel, iconColor = iconColor)
         }
@@ -847,14 +1168,22 @@
     iconColor: Color,
     modifier: Modifier = Modifier,
 ) {
-    PlatformIconButton(
-        onClick = viewModel.onClick,
-        iconResource = (viewModel.icon as Icon.Resource).res,
-        contentDescription = viewModel.icon.contentDescription?.load(),
-        colors = IconButtonDefaults.iconButtonColors(contentColor = iconColor),
-        enabled = viewModel.isEnabled,
-        modifier = modifier.size(48.dp).padding(13.dp),
-    )
+    val sharedModifier = modifier.size(48.dp).padding(13.dp)
+    when (viewModel) {
+        is MediaSecondaryActionViewModel.Action ->
+            PlatformIconButton(
+                onClick = viewModel.onClick ?: {},
+                iconResource = (viewModel.icon as Icon.Resource).res,
+                contentDescription = viewModel.icon.contentDescription?.load(),
+                colors = IconButtonDefaults.iconButtonColors(contentColor = iconColor),
+                enabled = viewModel.onClick != null,
+                modifier = sharedModifier,
+            )
+
+        is MediaSecondaryActionViewModel.ReserveSpace -> Spacer(modifier = sharedModifier)
+
+        is MediaSecondaryActionViewModel.None -> Unit
+    }
 }
 
 /** Enumerates all supported media presentation styles. */
@@ -867,6 +1196,24 @@
     Compact,
 }
 
+data class MediaUiBehavior(
+    val isCarouselDismissible: Boolean = true,
+    val isCarouselScrollingEnabled: Boolean = true,
+    val carouselVisibility: MediaCarouselVisibility = MediaCarouselVisibility.WhenNotEmpty,
+    /**
+     * If provided, this callback will be consulted at the beginning of each carousel scroll gesture
+     * to see if the falsing system thinks that it's a false touch. If it then returns `true`, the
+     * scroll will be canceled.
+     */
+    val isCarouselScrollFalseTouch: (() -> Boolean)? = null,
+)
+
+@Stable
+private interface AnimatedColorScheme {
+    val primary: Color
+    val onPrimary: Color
+}
+
 private object Media {
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCardViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCardViewModel.kt
index ecd6e6d..833a04d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCardViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCardViewModel.kt
@@ -19,6 +19,8 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.graphics.ImageBitmap
 import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.media.remedia.shared.model.MediaCardActionButtonLayout
+import com.android.systemui.media.remedia.shared.model.MediaColorScheme
 
 /** Models UI state for a media card. */
 @Stable
@@ -31,20 +33,19 @@
 
     val icon: Icon
 
-    /**
-     * A callback to load the artwork for the media shown on this card. This callback will be
-     * invoked on the main thread, it's up to the implementation to move the loading off the main
-     * thread.
-     */
-    val artLoader: suspend () -> ImageBitmap
+    val background: ImageBitmap?
+
+    val colorScheme: MediaColorScheme
 
     val title: String
 
     val subtitle: String
 
-    val playPauseAction: MediaPlayPauseActionViewModel
+    val actionButtonLayout: MediaCardActionButtonLayout
 
-    val seekBar: MediaSeekBarViewModel
+    val playPauseAction: MediaPlayPauseActionViewModel?
+
+    val navigation: MediaNavigationViewModel
 
     val additionalActions: List<MediaSecondaryActionViewModel>
 
@@ -53,7 +54,7 @@
     val outputSwitcherChips: List<MediaOutputSwitcherChipViewModel>
 
     /** Simple icon-only version of the output switcher for use in compact UIs. */
-    val outputSwitcherChipButton: MediaSecondaryActionViewModel
+    val outputSwitcherChipButton: MediaSecondaryActionViewModel.Action
 
     val onClick: () -> Unit
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCarouselVisibility.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCarouselVisibility.kt
new file mode 100644
index 0000000..53aa87c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaCarouselVisibility.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.remedia.ui.viewmodel
+
+/** Enumerates the known rules for media carousel visibility. */
+enum class MediaCarouselVisibility {
+
+    /** The carousel should be shown as long as it has at least one card. */
+    WhenNotEmpty,
+
+    /**
+     * The carousel should be shown as long as it has at least one card that represents an active
+     * media session. In other words: if all cards in the carousel represent _inactive_ sessions,
+     * the carousel should _not_ be visible.
+     */
+    WhenAnyCardIsActive,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt
similarity index 67%
rename from packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSeekBarViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt
index f1ced6b..a368992 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt
@@ -17,18 +17,28 @@
 package com.android.systemui.media.remedia.ui.viewmodel
 
 import androidx.annotation.FloatRange
+import androidx.compose.ui.geometry.Offset
 
-/** Models UI state for the seek bar. */
-sealed interface MediaSeekBarViewModel {
+/**
+ * Models UI state for the navigation component of the UI (potentially containing the seek bar and
+ * the buttons to its left and right).
+ */
+sealed interface MediaNavigationViewModel {
 
     /** The seek bar should be showing. */
     data class Showing(
         /** The progress to show on the seek bar, between `0` and `1`. */
         @FloatRange(from = 0.0, to = 1.0) val progress: Float,
-        /** The previous button; or `null` if it should be absent in the UI. */
-        val previous: MediaSecondaryActionViewModel?,
-        /** The next button; or `null` if it should be absent in the UI. */
-        val next: MediaSecondaryActionViewModel?,
+        /**
+         * The action button to the left of the seek bar; or `null` if it should be absent in the
+         * UI.
+         */
+        val left: MediaSecondaryActionViewModel,
+        /**
+         * The action button to the right of the seek bar; or `null` if it should be absent in the
+         * UI.
+         */
+        val right: MediaSecondaryActionViewModel,
         /**
          * Whether the portion of the seek bar track before the thumb should show the squiggle
          * animation.
@@ -49,9 +59,11 @@
          * A callback to invoke once the user finishes "scrubbing" (e.g. stopped moving the thumb of
          * the seek bar). The position/progress should be committed.
          */
-        val onScrubFinished: () -> Unit,
-    ) : MediaSeekBarViewModel
+        val onScrubFinished: (delta: Offset) -> Unit,
+        /** Accessibility string to attach to the seekbar UI element. */
+        val contentDescription: String,
+    ) : MediaNavigationViewModel
 
     /** The seek bar should be hidden. */
-    data object Hidden : MediaSeekBarViewModel
+    data object Hidden : MediaNavigationViewModel
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaPlayPauseActionViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaPlayPauseActionViewModel.kt
index 4cb11bc..ecc92d7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaPlayPauseActionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaPlayPauseActionViewModel.kt
@@ -21,8 +21,7 @@
 
 /** Models UI state for the play/pause action button within media controls. */
 data class MediaPlayPauseActionViewModel(
-    val isVisible: Boolean,
     val state: MediaSessionState,
-    val icon: Icon.Resource?,
-    val onClick: () -> Unit,
+    val icon: Icon?,
+    val onClick: (() -> Unit)?,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSecondaryActionViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSecondaryActionViewModel.kt
index a480680..d28ca7a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSecondaryActionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSecondaryActionViewModel.kt
@@ -19,8 +19,10 @@
 import com.android.systemui.common.shared.model.Icon
 
 /** Models UI state for a secondary action button within media controls. */
-data class MediaSecondaryActionViewModel(
-    val icon: Icon,
-    val isEnabled: Boolean,
-    val onClick: () -> Unit,
-)
+sealed interface MediaSecondaryActionViewModel {
+    data class Action(val icon: Icon, val onClick: (() -> Unit)?) : MediaSecondaryActionViewModel
+
+    data object ReserveSpace : MediaSecondaryActionViewModel
+
+    data object None : MediaSecondaryActionViewModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
new file mode 100644
index 0000000..19b08fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.remedia.ui.viewmodel
+
+import android.content.Context
+import android.icu.text.MeasureFormat
+import android.icu.util.Measure
+import android.icu.util.MeasureUnit
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.ImageBitmap
+import com.android.systemui.classifier.Classifier
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.media.remedia.domain.interactor.MediaInteractor
+import com.android.systemui.media.remedia.domain.model.MediaActionModel
+import com.android.systemui.media.remedia.shared.model.MediaColorScheme
+import com.android.systemui.media.remedia.shared.model.MediaSessionState
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.Locale
+import kotlin.math.abs
+import kotlin.math.roundToLong
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.awaitCancellation
+
+/** Models UI state for a media element. */
+class MediaViewModel
+@AssistedInject
+constructor(
+    private val interactor: MediaInteractor,
+    private val falsingSystem: FalsingSystem,
+    @Assisted private val context: Context,
+    @Assisted private val carouselVisibility: MediaCarouselVisibility,
+) : ExclusiveActivatable() {
+
+    /** Whether the user is actively moving the thumb of the seek bar. */
+    private var isScrubbing: Boolean by mutableStateOf(false)
+    /** The position of the thumb of the seek bar as the user is scrubbing it. */
+    private var seekProgress: Float by mutableFloatStateOf(0f)
+    /** Whether the internal "guts" are visible. */
+    private var isGutsVisible: Boolean by mutableStateOf(false)
+    /** The index of the currently-selected card. */
+    private var selectedCardIndex: Int by mutableIntStateOf(0)
+        private set
+
+    /** The current list of cards to show in the UI. */
+    val cards: List<MediaCardViewModel> by derivedStateOf {
+        interactor.sessions.mapIndexed { sessionIndex, session ->
+            val isCurrentSessionAndScrubbing = isScrubbing && sessionIndex == selectedCardIndex
+            object : MediaCardViewModel {
+                override val key = session.key
+                override val icon = session.appIcon
+                override val background: ImageBitmap?
+                    get() = session.background
+
+                override val colorScheme: MediaColorScheme
+                    get() = session.colorScheme
+
+                override val title = session.title
+                override val subtitle = session.subtitle
+                override val actionButtonLayout = session.actionButtonLayout
+                override val playPauseAction =
+                    session.playPauseAction.toPlayPauseActionViewModel(session.state)
+                override val additionalActions: List<MediaSecondaryActionViewModel>
+                    get() {
+                        return session.additionalActions.map { action ->
+                            action.toSecondaryActionViewModel()
+                        }
+                    }
+
+                override val navigation: MediaNavigationViewModel
+                    get() {
+                        return if (session.canBeScrubbed) {
+                            MediaNavigationViewModel.Showing(
+                                progress =
+                                    if (!isCurrentSessionAndScrubbing) {
+                                        session.positionMs.toFloat() / session.durationMs
+                                    } else {
+                                        seekProgress
+                                    },
+                                left = session.leftAction.toSecondaryActionViewModel(),
+                                right = session.rightAction.toSecondaryActionViewModel(),
+                                isSquiggly =
+                                    session.state != MediaSessionState.Paused &&
+                                        !isCurrentSessionAndScrubbing,
+                                isScrubbing = isCurrentSessionAndScrubbing,
+                                onScrubChange = { progress ->
+                                    check(selectedCardIndex == sessionIndex) {
+                                        "Can't seek on a card that's not the selected card!"
+                                    }
+                                    isScrubbing = true
+                                    seekProgress = progress
+                                },
+                                onScrubFinished = { dragDelta ->
+                                    if (
+                                        dragDelta.isHorizontal() &&
+                                            !falsingSystem.isFalseTouch(Classifier.MEDIA_SEEKBAR)
+                                    ) {
+                                        interactor.seek(
+                                            sessionKey = session.key,
+                                            to = (seekProgress * session.durationMs).roundToLong(),
+                                        )
+                                    }
+                                    isScrubbing = false
+                                },
+                                contentDescription =
+                                    context.getString(
+                                        R.string.controls_media_seekbar_description,
+                                        formatTimeContentDescription(session.positionMs),
+                                        formatTimeContentDescription(session.durationMs),
+                                    ),
+                            )
+                        } else {
+                            MediaNavigationViewModel.Hidden
+                        }
+                    }
+
+                override val guts: MediaCardGutsViewModel
+                    get() {
+                        return MediaCardGutsViewModel(
+                            isVisible = isGutsVisible,
+                            text =
+                                if (session.canBeHidden) {
+                                    context.getString(
+                                        R.string.controls_media_close_session,
+                                        session.appName,
+                                    )
+                                } else {
+                                    context.getString(R.string.controls_media_active_session)
+                                },
+                            primaryAction =
+                                if (session.canBeHidden) {
+                                    MediaGutsButtonViewModel(
+                                        text =
+                                            context.getString(
+                                                R.string.controls_media_dismiss_button
+                                            ),
+                                        onClick = {
+                                            falsingSystem.runIfNotFalseTap(
+                                                FalsingManager.LOW_PENALTY
+                                            ) {
+                                                interactor.hide(session.key)
+                                                isGutsVisible = false
+                                            }
+                                        },
+                                    )
+                                } else {
+                                    MediaGutsButtonViewModel(
+                                        text = context.getString(R.string.cancel),
+                                        onClick = {
+                                            falsingSystem.runIfNotFalseTap(
+                                                FalsingManager.LOW_PENALTY
+                                            ) {
+                                                isGutsVisible = false
+                                            }
+                                        },
+                                    )
+                                },
+                            secondaryAction =
+                                MediaGutsButtonViewModel(
+                                        text = context.getString(R.string.cancel),
+                                        onClick = {
+                                            falsingSystem.runIfNotFalseTap(
+                                                FalsingManager.LOW_PENALTY
+                                            ) {
+                                                isGutsVisible = false
+                                            }
+                                        },
+                                    )
+                                    .takeIf { session.canBeHidden },
+                            settingsButton =
+                                MediaGutsSettingsButtonViewModel(
+                                    icon =
+                                        Icon.Resource(
+                                            res = R.drawable.ic_settings,
+                                            contentDescription =
+                                                ContentDescription.Resource(
+                                                    res = R.string.controls_media_settings_button
+                                                ),
+                                        ),
+                                    onClick = {
+                                        falsingSystem.runIfNotFalseTap(FalsingManager.LOW_PENALTY) {
+                                            interactor.openMediaSettings()
+                                        }
+                                    },
+                                ),
+                            onLongClick = { isGutsVisible = false },
+                        )
+                    }
+
+                override val outputSwitcherChips: List<MediaOutputSwitcherChipViewModel>
+                    get() {
+                        return listOf(
+                            MediaOutputSwitcherChipViewModel(
+                                icon = session.outputDevice.icon,
+                                text = session.outputDevice.name,
+                                onClick = {
+                                    falsingSystem.runIfNotFalseTap(
+                                        FalsingManager.MODERATE_PENALTY
+                                    ) {
+                                        // TODO(b/397989775): tell the UI to show the output
+                                        // switcher.
+                                    }
+                                },
+                            )
+                        )
+                    }
+
+                override val outputSwitcherChipButton: MediaSecondaryActionViewModel.Action
+                    get() {
+                        return MediaSecondaryActionViewModel.Action(
+                            icon = session.outputDevice.icon,
+                            onClick = {
+                                falsingSystem.runIfNotFalseTap(FalsingManager.MODERATE_PENALTY) {
+                                    // TODO(b/397989775): tell the UI to show the output switcher.
+                                }
+                            },
+                        )
+                    }
+
+                override val onClick = {
+                    falsingSystem.runIfNotFalseTap(FalsingManager.LOW_PENALTY) { session.onClick() }
+                }
+                override val onClickLabel =
+                    context.getString(R.string.controls_media_playing_item_description)
+                override val onLongClick = { isGutsVisible = true }
+            }
+        }
+    }
+
+    /** Whether the carousel should be visible. */
+    val isCarouselVisible: Boolean
+        get() =
+            when (carouselVisibility) {
+                MediaCarouselVisibility.WhenNotEmpty -> interactor.sessions.isNotEmpty()
+
+                MediaCarouselVisibility.WhenAnyCardIsActive ->
+                    interactor.sessions.any { session -> session.isActive }
+            }
+
+    /** Notifies that the card at [cardIndex] has been selected in the UI. */
+    fun onCardSelected(cardIndex: Int) {
+        check(cardIndex >= 0 && cardIndex < cards.size)
+        selectedCardIndex = cardIndex
+    }
+
+    override suspend fun onActivated(): Nothing {
+        awaitCancellation()
+    }
+
+    private fun MediaActionModel.toPlayPauseActionViewModel(
+        mediaSessionState: MediaSessionState
+    ): MediaPlayPauseActionViewModel? {
+        return when (this) {
+            is MediaActionModel.Action ->
+                MediaPlayPauseActionViewModel(
+                    state = mediaSessionState,
+                    icon = icon,
+                    onClick =
+                        onClick?.let {
+                            {
+                                falsingSystem.runIfNotFalseTap(FalsingManager.MODERATE_PENALTY) {
+                                    it()
+                                }
+                            }
+                        },
+                )
+            is MediaActionModel.None,
+            is MediaActionModel.ReserveSpace -> null
+        }
+    }
+
+    private fun MediaActionModel.toSecondaryActionViewModel(): MediaSecondaryActionViewModel {
+        return when (this) {
+            is MediaActionModel.Action ->
+                MediaSecondaryActionViewModel.Action(
+                    icon = icon,
+                    onClick =
+                        onClick?.let {
+                            {
+                                falsingSystem.runIfNotFalseTap(FalsingManager.MODERATE_PENALTY) {
+                                    it()
+                                }
+                            }
+                        },
+                )
+            is MediaActionModel.ReserveSpace -> MediaSecondaryActionViewModel.ReserveSpace
+            is MediaActionModel.None -> MediaSecondaryActionViewModel.None
+        }
+    }
+
+    /**
+     * Returns a time string suitable for content description, e.g. "12 minutes 34 seconds"
+     *
+     * Follows same logic as Chronometer#formatDuration
+     */
+    private fun formatTimeContentDescription(milliseconds: Long): String {
+        var seconds = milliseconds.milliseconds.inWholeSeconds
+
+        val hours =
+            if (seconds >= OneHourInSec) {
+                seconds / OneHourInSec
+            } else {
+                0
+            }
+        seconds -= hours * OneHourInSec
+
+        val minutes =
+            if (seconds >= OneMinuteInSec) {
+                seconds / OneMinuteInSec
+            } else {
+                0
+            }
+        seconds -= minutes * OneMinuteInSec
+
+        val measures = arrayListOf<Measure>()
+        if (hours > 0) {
+            measures.add(Measure(hours, MeasureUnit.HOUR))
+        }
+        if (minutes > 0) {
+            measures.add(Measure(minutes, MeasureUnit.MINUTE))
+        }
+        measures.add(Measure(seconds, MeasureUnit.SECOND))
+
+        return MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+            .formatMeasures(*measures.toTypedArray())
+    }
+
+    /**
+     * Returns `true` if this [Offset] is the same or larger on the horizontal axis than the
+     * vertical axis.
+     */
+    private fun Offset.isHorizontal(): Boolean {
+        return abs(x) >= abs(y)
+    }
+
+    interface FalsingSystem {
+        fun runIfNotFalseTap(@FalsingManager.Penalty penalty: Int, block: () -> Unit)
+
+        fun isFalseTouch(@Classifier.InteractionType interactionType: Int): Boolean
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(context: Context, carouselVisibility: MediaCarouselVisibility): MediaViewModel
+    }
+
+    companion object {
+        private const val OneMinuteInSec = 60
+        private const val OneHourInSec = OneMinuteInSec * 60
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
index c6e4db7..324a3ef 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
@@ -150,6 +150,13 @@
             titleTextView.isEnabled = true
         } else {
             errorTextView.visibility = View.VISIBLE
+            if (com.android.systemui.Flags.mediaProjectionGreyErrorText()) {
+                errorTextView.isEnabled = false
+                errorTextView.setTextColor(context.getColorStateList(R.color.menu_item_text))
+                errorTextView.setText(
+                    R.string.media_projection_entry_app_permission_dialog_single_app_not_supported
+                )
+            }
             titleTextView.isEnabled = false
         }
         return view
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt
index 4ff54d4e..42d2761 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepository.kt
@@ -26,7 +26,7 @@
 import android.util.Log
 import android.view.Display
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUIStateChange.kt b/packages/SystemUI/src/com/android/systemui/model/SysUIStateChange.kt
index aaed606..cec846e 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUIStateChange.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUIStateChange.kt
@@ -43,8 +43,6 @@
         return this
     }
 
-    fun hasChanges() = flagsToSet != 0L || flagsToClear != 0L
-
     /**
      * Applies all changed flags to [sysUiState].
      *
@@ -83,6 +81,7 @@
         iterateBits(flagsToSet or flagsToClear) { bit -> sysUiState.setFlag(bit, false) }
     }
 
+    /** Resets all the pending changes. */
     fun clear() {
         flagsToSet = 0
         flagsToClear = 0
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
index e99ee7d..68cd807 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
@@ -17,11 +17,12 @@
 
 import android.util.Log
 import android.view.Display
+import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.display.data.repository.PerDisplayInstanceProviderWithTeardown
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.model.SysUiState.SysUiStateCallback
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
 import com.android.systemui.shared.system.QuickStepContract
 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags
 import dagger.assisted.Assisted
@@ -29,6 +30,7 @@
 import dagger.assisted.AssistedInject
 import dalvik.annotation.optimization.NeverCompile
 import java.io.PrintWriter
+import java.lang.Long.bitCount
 import javax.inject.Inject
 
 /** Contains sysUi state flags and notifies registered listeners whenever changes happen. */
@@ -111,8 +113,7 @@
         get() = _flags
 
     private var _flags: Long = 0
-    private var flagsToSet: Long = 0
-    private var flagsToClear: Long = 0
+    private val stateChange = StateChange()
 
     /**
      * Add listener to be notified of changes made to SysUI state. The callback will also be called
@@ -132,13 +133,11 @@
 
     /** Methods to this call can be chained together before calling [.commitUpdate]. */
     override fun setFlag(@SystemUiStateFlags flag: Long, enabled: Boolean): SysUiState {
-        val toSet = flagWithOptionalOverrides(flag, enabled, displayId, sceneContainerPlugin)
-
-        if (toSet) {
-            flagsToSet = flagsToSet or flag
-        } else {
-            flagsToClear = flagsToClear or flag
+        if (ShadeWindowGoesAround.isEnabled && bitCount(flag) > 1) {
+            error("Flags should be a single bit.")
         }
+        val toSet = flagWithOptionalOverrides(flag, enabled, displayId, sceneContainerPlugin)
+        stateChange.setFlag(flag, toSet)
         return this
     }
 
@@ -147,27 +146,19 @@
         ReplaceWith("commitUpdate()"),
     )
     override fun commitUpdate(displayId: Int) {
-        // TODO b/398011576 - handle updates for different displays.
         commitUpdate()
     }
 
     override fun commitUpdate() {
-        updateFlags()
-        flagsToSet = 0
-        flagsToClear = 0
-    }
-
-    private fun updateFlags() {
-        var newState = flags
-        newState = newState or flagsToSet
-        newState = newState and flagsToClear.inv()
+        val newState = stateChange.applyTo(flags)
         notifyAndSetSystemUiStateChanged(newState, flags)
+        stateChange.clear()
     }
 
     /** Notify all those who are registered that the state has changed. */
     private fun notifyAndSetSystemUiStateChanged(newFlags: Long, oldFlags: Long) {
         if (SysUiState.DEBUG) {
-            Log.d(TAG, "SysUiState changed: old=$oldFlags new=$newFlags")
+            Log.d(TAG, "SysUiState changed for displayId=$displayId: old=$oldFlags new=$newFlags")
         }
         if (newFlags != oldFlags) {
             _flags = newFlags
@@ -185,6 +176,8 @@
         pw.println(QuickStepContract.isBackGestureDisabled(flags, false /* forTrackpad */))
         pw.print("    assistantGestureDisabled=")
         pw.println(QuickStepContract.isAssistantGestureDisabled(flags))
+        pw.print("    pendingStateChanges=")
+        pw.println(stateChange.toString())
     }
 
     override fun destroy() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 49fa3ba..88f679e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -72,4 +72,8 @@
     /** @return {@link NavigationBar} on the default display. */
     @Nullable
     NavigationBar getDefaultNavigationBar();
+
+    /** @return {@link NavigationBar} for a specific display, or null if not available. */
+    @Nullable
+    NavigationBar getNavigationBar(int displayId);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
index 45ff7f4..f096510 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
@@ -54,4 +54,6 @@
     override fun isOverviewEnabled(displayId: Int) = false
 
     override fun getDefaultNavigationBar(): NavigationBar? = null
+
+    override fun getNavigationBar(displayId: Int): NavigationBar? = null
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 50d0a45..8fbf8b60 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -469,7 +469,8 @@
         return (navBar == null) ? null : navBar.getView();
     }
 
-    private @Nullable NavigationBar getNavigationBar(int displayId) {
+    @Override
+    public @Nullable NavigationBar getNavigationBar(int displayId) {
         return mNavigationBars.get(displayId);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index 914e0f7..39482be 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -24,13 +24,16 @@
 import android.view.View;
 import android.view.WindowManager;
 
+import com.android.app.displaylib.PerDisplayRepository;
 import com.android.app.viewcapture.ViewCapture;
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
 import com.android.systemui.navigationbar.views.NavigationBarFrame;
 import com.android.systemui.navigationbar.views.NavigationBarView;
 import com.android.systemui.res.R;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
 
 import dagger.Lazy;
 import dagger.Module;
@@ -71,6 +74,20 @@
         return context.getSystemService(WindowManager.class);
     }
 
+    /** A SysUiState for the navigation bar display. */
+    @Provides
+    @NavigationBarScope
+    @DisplayId
+    static SysUiState provideSysUiState(@DisplayId Context context,
+            SysUiState defaultState,
+            PerDisplayRepository<SysUiState> repository) {
+        if (ShadeWindowGoesAround.isEnabled()) {
+            return repository.get(context.getDisplayId());
+        } else {
+            return defaultState;
+        }
+    }
+
     /** A ViewCaptureAwareWindowManager specific to the display's context. */
     @Provides
     @NavigationBarScope
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index f95f459..8b5b3ad 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -569,7 +569,7 @@
             NavigationModeController navigationModeController,
             StatusBarStateController statusBarStateController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            SysUiState sysUiFlagsContainer,
+            @DisplayId SysUiState sysUiFlagsContainer,
             UserTracker userTracker,
             CommandQueue commandQueue,
             Optional<Pip> pipOptional,
@@ -1694,7 +1694,7 @@
                         (mNavbarFlags & NAVBAR_BACK_DISMISS_IME) != 0)
                 .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
                         allowSystemGestureIgnoringBarVisibility())
-                .commitUpdate(mDisplayId);
+                .commitUpdate();
     }
 
     private void updateAssistantEntrypoints(boolean assistantAvailable,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
index 36cb8fa..cbc4c26 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -740,15 +740,13 @@
 
     /** */
     public void updateDisabledSystemUiStateFlags(SysUiState sysUiState) {
-        int displayId = mContext.getDisplayId();
-
         sysUiState.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
                         (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
                 .setFlag(SYSUI_STATE_HOME_DISABLED,
                         (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0)
                 .setFlag(SYSUI_STATE_SEARCH_DISABLED,
                         (mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0)
-                .commitUpdate(displayId);
+                .commitUpdate();
     }
 
     public void setInScreenPinning(boolean active) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index d8fc52b..8dc27bf4 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -162,10 +162,6 @@
             ): Boolean {
                 return this@NoteTaskInitializer.handleKeyGestureEvent(event)
             }
-
-            override fun isKeyGestureSupported(gestureType: Int): Boolean {
-                return this@NoteTaskInitializer.isKeyGestureSupported(gestureType)
-            }
         }
 
     /**
@@ -225,10 +221,6 @@
         return true
     }
 
-    private fun isKeyGestureSupported(gestureType: Int): Boolean {
-        return gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES
-    }
-
     companion object {
         val MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout().toLong()
         val LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout().toLong()
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 5d54656..fb3271e 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -32,16 +32,16 @@
 import com.android.systemui.qs.shared.model.TileCategory
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.NotesTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.impl.notes.domain.NotesTileMapper
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
 import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileDataInteractor
 import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.impl.notes.ui.mapper.NotesTileMapper
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
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 43bd6aa..faa77e5 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
@@ -24,7 +24,7 @@
 import android.os.PowerManager
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.power.shared.model.DozeScreenStateModel
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
index 297c6af..f368c53 100644
--- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
@@ -61,6 +61,11 @@
             (lastWakeReason == WakeSleepReason.TAP || lastWakeReason == WakeSleepReason.GESTURE)
     }
 
+    fun isAwakeFromMotionOrLift(): Boolean {
+        return isAwake() &&
+            (lastWakeReason == WakeSleepReason.MOTION || lastWakeReason == WakeSleepReason.LIFT)
+    }
+
     override fun logDiffs(prevVal: WakefulnessModel, row: TableRowLogger) {
         row.logChange(columnName = "wakefulness", value = toString())
     }
diff --git a/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java b/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java
deleted file mode 100644
index ea2bf6a..0000000
--- a/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.process.condition;
-
-import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.process.ProcessWrapper;
-import com.android.systemui.shared.condition.Condition;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import javax.inject.Inject;
-
-/**
- * {@link SystemProcessCondition} checks to make sure the current process is being ran by the
- * System User.
- */
-public class SystemProcessCondition extends Condition {
-    private final ProcessWrapper mProcessWrapper;
-
-    @Inject
-    public SystemProcessCondition(@Application CoroutineScope scope,
-            ProcessWrapper processWrapper) {
-        super(scope);
-        mProcessWrapper = processWrapper;
-    }
-
-    @Override
-    protected void start() {
-        updateCondition(mProcessWrapper.isSystemUser());
-    }
-
-    @Override
-    protected void stop() {
-    }
-
-    @Override
-    public int getStartStrategy() {
-        return START_EAGERLY;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.kt b/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.kt
new file mode 100644
index 0000000..9a424c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/process/condition/SystemProcessCondition.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.process.condition
+
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.process.ProcessWrapper
+import com.android.systemui.shared.condition.Condition
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * [SystemProcessCondition] checks to make sure the current process is being ran by the System User.
+ */
+class SystemProcessCondition
+@Inject
+constructor(@Application scope: CoroutineScope, private val processWrapper: ProcessWrapper) :
+    Condition(scope) {
+    override val startStrategy: Int
+        get() = START_EAGERLY
+
+    override suspend fun start() {
+        updateCondition(processWrapper.isSystemUser)
+    }
+
+    override fun stop() {}
+}
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 f1f5b26..05a60a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -58,6 +58,7 @@
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerInputChange
@@ -102,6 +103,7 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.Flags
 import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
+import com.android.systemui.brightness.ui.compose.ContainerColors
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyboard.shortcut.ui.composable.InteractionsConfig
@@ -249,7 +251,7 @@
 
     @Composable
     private fun Content() {
-        PlatformTheme(isDarkTheme = true) {
+        PlatformTheme {
             ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) {
                 // TODO(b/389985793): Make sure that there is no coroutine work or recompositions
                 // happening when alwaysCompose is true but isQsVisibleAndAnyShadeExpanded is false.
@@ -415,7 +417,7 @@
     }
 
     override fun isCustomizing(): Boolean {
-        return viewModel.containerViewModel.editModeViewModel.isEditing.value
+        return viewModel.isEditing
     }
 
     override fun closeCustomizer() {
@@ -651,7 +653,8 @@
                                  */
                                 !alwaysCompose ||
                                     (viewModel.isQsVisibleAndAnyShadeExpanded &&
-                                        viewModel.expansionState.progress < 1f)
+                                        viewModel.expansionState.progress < 1f &&
+                                        !viewModel.isEditing)
                             },
                         )
                     }
@@ -740,17 +743,23 @@
                         )
                         val BrightnessSlider =
                             @Composable {
-                                BrightnessSliderContainer(
-                                    viewModel = containerViewModel.brightnessSliderViewModel,
-                                    modifier =
-                                        Modifier.systemGestureExclusionInShade(
-                                                enabled = {
-                                                    layoutState.transitionState is
-                                                        TransitionState.Idle
-                                                }
-                                            )
-                                            .fillMaxWidth(),
-                                )
+                                Box(
+                                    Modifier.systemGestureExclusionInShade(
+                                        enabled = {
+                                            layoutState.transitionState is TransitionState.Idle
+                                        }
+                                    )
+                                ) {
+                                    BrightnessSliderContainer(
+                                        viewModel = containerViewModel.brightnessSliderViewModel,
+                                        containerColors =
+                                            ContainerColors(
+                                                Color.Transparent,
+                                                ContainerColors.defaultContainerColor,
+                                            ),
+                                        modifier = Modifier.fillMaxWidth(),
+                                    )
+                                }
                             }
                         val TileGrid =
                             @Composable {
@@ -769,7 +778,8 @@
                                              */
                                             !alwaysCompose ||
                                                 (viewModel.isQsVisibleAndAnyShadeExpanded &&
-                                                    viewModel.expansionState.progress > 0f)
+                                                    viewModel.expansionState.progress > 0f &&
+                                                    !viewModel.isEditing)
                                         },
                                     )
                                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index 767acc5..b61fa9c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -35,7 +35,6 @@
 import com.android.systemui.classifier.Classifier
 import com.android.systemui.classifier.domain.interactor.FalsingInteractor
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -307,6 +306,12 @@
     val animateTilesExpansion: Boolean
         get() = inFirstPage && !mediaSuddenlyAppearingInLandscape
 
+    val isEditing by
+        hydrator.hydratedStateOf(
+            traceName = "isEditing",
+            source = containerViewModel.editModeViewModel.isEditing,
+        )
+
     private val inFirstPage: Boolean
         get() = inFirstPageViewModel.inFirstPage
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt
index e2a3916..f1193fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.qs.panels.dagger.PanelsModule
 import com.android.systemui.qs.pipeline.dagger.QSPipelineModule
 import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.qs.tiles.di.QSTilesModule
+import com.android.systemui.qs.tiles.base.ui.model.QSTilesModule
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl
 import dagger.Binds
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/ui/dialog/TileRequestDialogComposeDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/external/ui/dialog/TileRequestDialogComposeDelegate.kt
index 446be9b..59844c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/ui/dialog/TileRequestDialogComposeDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/ui/dialog/TileRequestDialogComposeDelegate.kt
@@ -85,6 +85,7 @@
 
                         LargeStaticTile(
                             uiState = viewModel.uiState,
+                            iconProvider = viewModel.iconProvider,
                             modifier =
                                 Modifier.width(
                                     dimensionResource(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/ui/viewmodel/TileRequestDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/external/ui/viewmodel/TileRequestDialogViewModel.kt
index c756adc..dd281cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/ui/viewmodel/TileRequestDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/ui/viewmodel/TileRequestDialogViewModel.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.external.TileData
+import com.android.systemui.qs.panels.ui.viewmodel.toIconProvider
 import com.android.systemui.qs.panels.ui.viewmodel.toUiState
 import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
 import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
@@ -58,6 +59,8 @@
 
     val uiState by derivedStateOf { state.toUiState(dialogContext.resources) }
 
+    val iconProvider by derivedStateOf { state.toIconProvider() }
+
     override suspend fun onActivated(): Nothing {
         withContext(backgroundDispatcher) {
             tileData.icon
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
index bd9d70c..eb99fec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.qs.footer.data.repository
 
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.FgsManagerController
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
index a2cee3b..ec0a754 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.qs.panels.domain.model.EditTilesModel
 import com.android.systemui.qs.panels.shared.model.EditTileData
 import com.android.systemui.qs.shared.model.TileCategory
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
 import javax.inject.Inject
 
 @SysUISingleton
@@ -50,7 +50,7 @@
                         it,
                         Icon.Resource(
                             config.uiConfig.iconRes,
-                            ContentDescription.Resource(config.uiConfig.labelRes)
+                            ContentDescription.Resource(config.uiConfig.labelRes),
                         ),
                         Text.Resource(config.uiConfig.labelRes),
                         null,
@@ -61,7 +61,7 @@
                         it,
                         Icon.Resource(
                             android.R.drawable.star_on,
-                            ContentDescription.Loaded(it.spec)
+                            ContentDescription.Loaded(it.spec),
                         ),
                         Text.Loaded(it.spec),
                         null,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt
index 00cd96c..46a6e4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt
@@ -19,14 +19,14 @@
 import android.os.UserHandle
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
+import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
-import javax.inject.Inject
 
 /**
  * Uses the [QSTileAvailabilityInteractor] from the new tiles to provide a map of availability, for
@@ -38,23 +38,26 @@
 class NewTilesAvailabilityInteractor
 @Inject
 constructor(
-        private val availabilityInteractors:
-                Map<String, @JvmSuppressWildcards QSTileAvailabilityInteractor>,
-        userRepository: UserRepository,
+    private val availabilityInteractors:
+        Map<String, @JvmSuppressWildcards QSTileAvailabilityInteractor>,
+    userRepository: UserRepository,
 ) {
     val newTilesAvailable: Flow<Map<TileSpec, Boolean>> =
-            userRepository.selectedUserInfo.map { it.id }
-                    .flatMapLatestConflated { userId ->
-                        if (availabilityInteractors.isEmpty()) {
-                            flowOf(emptyMap())
-                        } else {
-                            combine(availabilityInteractors.map { (spec, interactor) ->
-                                interactor.availability(UserHandle.of(userId)).map {
-                                    TileSpec.create(spec) to it
-                                }
-                            }) {
-                                it.toMap()
+        userRepository.selectedUserInfo
+            .map { it.id }
+            .flatMapLatestConflated { userId ->
+                if (availabilityInteractors.isEmpty()) {
+                    flowOf(emptyMap())
+                } else {
+                    combine(
+                        availabilityInteractors.map { (spec, interactor) ->
+                            interactor.availability(UserHandle.of(userId)).map {
+                                TileSpec.create(spec) to it
                             }
                         }
+                    ) {
+                        it.toMap()
                     }
+                }
+            }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 865ae9a..a7ebb22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -85,6 +85,19 @@
 
         val pagerState = rememberPagerState(0) { pages.size }
 
+        LaunchedEffect(listening, pagerState) {
+            snapshotFlow { listening() }
+                .collect {
+                    // Whenever we go from not listening to listening, we should be in the first
+                    // page. If we did this when going from listening to not listening, opening
+                    // edit mode in second page will cause it to go to first page during the
+                    // transition.
+                    if (listening()) {
+                        pagerState.scrollToPage(0)
+                    }
+                }
+        }
+
         // Used to track if this is currently in the first page or not, for animations
         LaunchedEffect(key1 = pagerState) {
             snapshotFlow { pagerState.currentPage == 0 }.collect { viewModel.inFirstPage = it }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index d20b360..728652e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -82,6 +82,7 @@
                         viewModel.tileHapticsViewModelFactoryProvider,
                     // There should be no QuickQuickSettings when the details view is enabled.
                     detailsViewModel = null,
+                    isVisible = listening,
                 )
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
index 701f44e..d40ecc9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
@@ -61,6 +61,9 @@
 
     DisposableEffect(Unit) { onDispose { detailsViewModel.closeDetailedView() } }
 
+    val title = tileDetailedViewModel.title
+    val subTitle = tileDetailedViewModel.subTitle
+
     Column(
         modifier =
             modifier
@@ -90,7 +93,7 @@
                     )
                 }
                 Text(
-                    text = tileDetailedViewModel.getTitle(),
+                    text = title,
                     modifier = Modifier.align(Alignment.CenterVertically),
                     textAlign = TextAlign.Center,
                     style = MaterialTheme.typography.titleLarge,
@@ -110,7 +113,7 @@
                 }
             }
             Text(
-                text = tileDetailedViewModel.getSubTitle(),
+                text = subTitle,
                 modifier = Modifier.fillMaxWidth(),
                 textAlign = TextAlign.Center,
                 style = MaterialTheme.typography.titleSmall,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 50012ab..b3b6cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -45,6 +45,7 @@
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
@@ -102,6 +103,7 @@
     sideDrawable: Drawable?,
     colors: TileColors,
     squishiness: () -> Float,
+    isVisible: () -> Boolean = { true },
     accessibilityUiState: AccessibilityUiState? = null,
     iconShape: RoundedCornerShape = RoundedCornerShape(CommonTileDefaults.InactiveCornerRadius),
     toggleClick: (() -> Unit)? = null,
@@ -158,6 +160,7 @@
             secondaryLabel = secondaryLabel,
             colors = colors,
             accessibilityUiState = accessibilityUiState,
+            isVisible = isVisible,
         )
 
         if (sideDrawable != null) {
@@ -176,6 +179,7 @@
     secondaryLabel: String?,
     colors: TileColors,
     modifier: Modifier = Modifier,
+    isVisible: () -> Boolean = { true },
     accessibilityUiState: AccessibilityUiState? = null,
 ) {
     val animatedLabelColor by animateColorAsState(colors.label, label = "QSTileLabelColor")
@@ -186,12 +190,14 @@
             text = label,
             style = MaterialTheme.typography.labelLarge,
             color = { animatedLabelColor },
+            isVisible = isVisible,
         )
         if (!TextUtils.isEmpty(secondaryLabel)) {
             TileLabel(
                 secondaryLabel ?: "",
                 color = { animatedSecondaryLabelColor },
                 style = MaterialTheme.typography.bodyMedium,
+                isVisible = isVisible,
                 modifier =
                     Modifier.thenIf(
                         accessibilityUiState?.stateDescription?.contains(secondaryLabel ?: "") ==
@@ -277,36 +283,50 @@
     color: ColorProducer,
     style: TextStyle,
     modifier: Modifier = Modifier,
+    isVisible: () -> Boolean = { true },
 ) {
+    var textSize by remember { mutableIntStateOf(0) }
+
     BasicText(
         text = text,
         color = color,
         style = style,
         maxLines = 1,
+        onTextLayout = { textSize = it.size.width },
         modifier =
             modifier
                 .fillMaxWidth()
-                .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
+                .graphicsLayer {
+                    if (textSize > size.width) {
+                        compositingStrategy = CompositingStrategy.Offscreen
+                    }
+                }
                 .drawWithContent {
                     drawContent()
-                    // Draw a blur over the end of the text
-                    val edgeWidthPx = TileLabelBlurWidth.toPx()
-                    drawRect(
-                        topLeft = Offset(size.width - edgeWidthPx, 0f),
-                        size = Size(edgeWidthPx, size.height),
-                        brush =
-                            Brush.horizontalGradient(
-                                colors = listOf(Color.Transparent, Color.Black),
-                                startX = size.width,
-                                endX = size.width - edgeWidthPx,
-                            ),
-                        blendMode = BlendMode.DstIn,
-                    )
+                    if (textSize > size.width) {
+                        // Draw a blur over the end of the text
+                        val edgeWidthPx = TileLabelBlurWidth.toPx()
+                        drawRect(
+                            topLeft = Offset(size.width - edgeWidthPx, 0f),
+                            size = Size(edgeWidthPx, size.height),
+                            brush =
+                                Brush.horizontalGradient(
+                                    colors = listOf(Color.Transparent, Color.Black),
+                                    startX = size.width,
+                                    endX = size.width - edgeWidthPx,
+                                ),
+                            blendMode = BlendMode.DstIn,
+                        )
+                    }
                 }
-                .basicMarquee(
-                    iterations = TILE_MARQUEE_ITERATIONS,
-                    initialDelayMillis = TILE_INITIAL_DELAY_MILLIS,
-                ),
+                .thenIf(isVisible()) {
+                    // Only apply the marquee when the label is visible, which is needed for the
+                    // always composed QS
+                    Modifier.basicMarquee(
+                        iterations = TILE_MARQUEE_ITERATIONS,
+                        initialDelayMillis = TILE_INITIAL_DELAY_MILLIS,
+                    )
+                },
     )
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 69b967a..ccbd8fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -63,6 +63,7 @@
 import androidx.compose.material.icons.automirrored.filled.ArrowBack
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Clear
+import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
@@ -89,6 +90,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.isSpecified
 import androidx.compose.ui.graphics.Color
@@ -113,7 +115,9 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.util.fastMap
+import com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
 import com.android.compose.modifiers.height
+import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.common.ui.compose.load
 import com.android.systemui.qs.panels.shared.model.SizedTile
 import com.android.systemui.qs.panels.shared.model.SizedTileImpl
@@ -131,6 +135,7 @@
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AUTO_SCROLL_SPEED
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.AvailableTilesGridMinHeight
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.GridBackgroundCornerRadius
 import com.android.systemui.qs.panels.ui.compose.selection.InteractiveTileContainer
 import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState
 import com.android.systemui.qs.panels.ui.compose.selection.ResizingState
@@ -163,14 +168,27 @@
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
-
+    val primaryContainerColor = MaterialTheme.colorScheme.primaryContainer
     TopAppBar(
-        colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
-        title = { Text(text = stringResource(id = R.string.qs_edit)) },
+        colors =
+            TopAppBarDefaults.topAppBarColors(
+                containerColor = Color.Transparent,
+                titleContentColor = MaterialTheme.colorScheme.onSurface,
+            ),
+        title = {
+            Text(
+                text = stringResource(id = R.string.qs_edit),
+                modifier = Modifier.padding(start = 24.dp),
+            )
+        },
         navigationIcon = {
-            IconButton(onClick = onStopEditing) {
+            IconButton(
+                onClick = onStopEditing,
+                modifier = Modifier.drawBehind { drawCircle(primaryContainerColor) },
+            ) {
                 Icon(
                     Icons.AutoMirrored.Filled.ArrowBack,
+                    tint = MaterialTheme.colorScheme.onSurface,
                     contentDescription =
                         stringResource(id = com.android.internal.R.string.action_bar_up_description),
                 )
@@ -178,11 +196,19 @@
         },
         actions = {
             if (onReset != null) {
-                TextButton(onClick = onReset) {
+                TextButton(
+                    onClick = onReset,
+                    colors =
+                        ButtonDefaults.textButtonColors(
+                            containerColor = MaterialTheme.colorScheme.primary,
+                            contentColor = MaterialTheme.colorScheme.onPrimary,
+                        ),
+                ) {
                     Text(stringResource(id = com.android.internal.R.string.reset))
                 }
             }
         },
+        modifier = Modifier.padding(vertical = 8.dp),
     )
 }
 
@@ -215,7 +241,9 @@
         containerColor = Color.Transparent,
         topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) },
     ) { innerPadding ->
-        CompositionLocalProvider(LocalOverscrollFactory provides null) {
+        CompositionLocalProvider(
+            LocalOverscrollFactory provides rememberOffsetOverscrollEffectFactory()
+        ) {
             val scrollState = rememberScrollState()
 
             AutoScrollGrid(listState, scrollState, innerPadding)
@@ -244,7 +272,7 @@
                     targetState = listState.dragInProgress || selectionState.selected,
                     label = "QSEditHeader",
                     contentAlignment = Alignment.Center,
-                    modifier = Modifier.fillMaxWidth().heightIn(min = 80.dp),
+                    modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp),
                 ) { showRemoveTarget ->
                     EditGridHeader {
                         if (showRemoveTarget) {
@@ -289,10 +317,6 @@
                                 spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
                             modifier = modifier.fillMaxSize(),
                         ) {
-                            EditGridHeader {
-                                Text(text = stringResource(id = R.string.drag_to_add_tiles))
-                            }
-
                             val availableTiles = remember {
                                 mutableStateListOf<AvailableTileGridCell>().apply {
                                     addAll(toAvailableTiles(listState.tiles, otherTiles))
@@ -371,9 +395,7 @@
     modifier: Modifier = Modifier,
     content: @Composable BoxScope.() -> Unit,
 ) {
-    CompositionLocalProvider(
-        LocalContentColor provides MaterialTheme.colorScheme.onBackground.copy(alpha = .5f)
-    ) {
+    CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
         Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxWidth()) { content() }
     }
 }
@@ -420,6 +442,7 @@
             listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) }
         }
 
+    val primaryColor = MaterialTheme.colorScheme.primary
     TileLazyGrid(
         state = gridState,
         columns = GridCells.Fixed(columns),
@@ -428,9 +451,9 @@
             Modifier.fillMaxWidth()
                 .height { totalHeight.roundToPx() }
                 .border(
-                    width = 1.dp,
-                    color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
-                    shape = RoundedCornerShape((TileHeight / 2) + CurrentTilesGridPadding),
+                    width = 2.dp,
+                    color = primaryColor,
+                    shape = RoundedCornerShape(GridBackgroundCornerRadius),
                 )
                 .dragAndDropTileList(gridState, { gridContentOffset }, listState) { spec ->
                     onSetTiles(currentListState.tileSpecs())
@@ -439,6 +462,13 @@
                 .onGloballyPositioned { coordinates ->
                     gridContentOffset = coordinates.positionInRoot()
                 }
+                .drawBehind {
+                    drawRoundRect(
+                        primaryColor,
+                        cornerRadius = CornerRadius(GridBackgroundCornerRadius.toPx()),
+                        alpha = .15f,
+                    )
+                }
                 .testTag(CURRENT_TILES_GRID_TEST_TAG),
     ) {
         EditTiles(cells, listState, selectionState, coroutineScope, largeTilesSpan, onRemoveTile) {
@@ -469,7 +499,6 @@
         remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
             groupAndSort(tiles)
         }
-    val labelColors = EditModeTileDefaults.editTileColors()
 
     // Available tiles
     Column(
@@ -480,32 +509,45 @@
     ) {
         groupedTiles.forEach { (category, tiles) ->
             key(category) {
-                Text(
-                    text = category.label.load() ?: "",
-                    fontSize = 20.sp,
-                    color = labelColors.label,
+                val surfaceColor = MaterialTheme.colorScheme.surface
+                Column(
+                    verticalArrangement = spacedBy(16.dp),
                     modifier =
-                        Modifier.fillMaxWidth().padding(start = 16.dp, bottom = 8.dp, top = 8.dp),
-                )
-                tiles.chunked(columns).forEach { row ->
-                    Row(
-                        horizontalArrangement = spacedBy(TileArrangementPadding),
-                        modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
-                    ) {
-                        row.forEach { tileGridCell ->
-                            key(tileGridCell.key) {
-                                AvailableTileGridCell(
-                                    cell = tileGridCell,
-                                    dragAndDropState = dragAndDropState,
-                                    selectionState = selectionState,
-                                    onAddTile = onAddTile,
-                                    modifier = Modifier.weight(1f).fillMaxHeight(),
+                        Modifier.drawBehind {
+                                drawRoundRect(
+                                    surfaceColor,
+                                    cornerRadius = CornerRadius(GridBackgroundCornerRadius.toPx()),
+                                    alpha = .32f,
                                 )
                             }
-                        }
+                            .padding(16.dp),
+                ) {
+                    Text(
+                        text = category.label.load() ?: "",
+                        fontSize = 20.sp,
+                        color = MaterialTheme.colorScheme.onSurface,
+                        modifier = Modifier.fillMaxWidth().padding(start = 8.dp, bottom = 16.dp),
+                    )
+                    tiles.chunked(columns).forEach { row ->
+                        Row(
+                            horizontalArrangement = spacedBy(TileArrangementPadding),
+                            modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
+                        ) {
+                            row.forEach { tileGridCell ->
+                                key(tileGridCell.key) {
+                                    AvailableTileGridCell(
+                                        cell = tileGridCell,
+                                        dragAndDropState = dragAndDropState,
+                                        selectionState = selectionState,
+                                        onAddTile = onAddTile,
+                                        modifier = Modifier.weight(1f).fillMaxHeight(),
+                                    )
+                                }
+                            }
 
-                        // Spacers for incomplete rows
-                        repeat(columns - row.size) { Spacer(modifier = Modifier.weight(1f)) }
+                            // Spacers for incomplete rows
+                            repeat(columns - row.size) { Spacer(modifier = Modifier.weight(1f)) }
+                        }
                     }
                 }
             }
@@ -761,7 +803,7 @@
                 color = colors.label,
                 overflow = TextOverflow.Ellipsis,
                 textAlign = TextAlign.Center,
-                modifier = Modifier.align(Alignment.Center),
+                modifier = Modifier.align(Alignment.TopCenter),
             )
         }
     }
@@ -861,15 +903,16 @@
     const val AUTO_SCROLL_SPEED = 2 // 2ms per pixel
     val CurrentTilesGridPadding = 10.dp
     val AvailableTilesGridMinHeight = 200.dp
+    val GridBackgroundCornerRadius = 42.dp
 
     @Composable
     fun editTileColors(): TileColors =
         TileColors(
-            background = MaterialTheme.colorScheme.surfaceVariant,
-            iconBackground = MaterialTheme.colorScheme.surfaceVariant,
-            label = MaterialTheme.colorScheme.onSurfaceVariant,
-            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
-            icon = MaterialTheme.colorScheme.onSurfaceVariant,
+            background = LocalAndroidColorScheme.current.surfaceEffect2,
+            iconBackground = Color.Transparent,
+            label = MaterialTheme.colorScheme.onSurface,
+            secondaryLabel = MaterialTheme.colorScheme.onSurface,
+            icon = MaterialTheme.colorScheme.onSurface,
         )
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 6236ada..984343a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -78,8 +78,15 @@
             }
 
         val columns = columnsWithMediaViewModel.columns
+        val largeTiles by iconTilesViewModel.largeTilesState
         val largeTilesSpan by iconTilesViewModel.largeTilesSpanState
-        val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width(largeTilesSpan)) }
+        // Tiles or largeTiles may be updated while this is composed, so listen to any changes
+        val sizedTiles =
+            remember(tiles, largeTiles, largeTilesSpan) {
+                tiles.map {
+                    SizedTileImpl(it, if (largeTiles.contains(it.spec)) largeTilesSpan else 1)
+                }
+            }
         val bounceables =
             remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
         val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
@@ -112,6 +119,7 @@
                             isLastInRow = isLastInColumn,
                         ),
                     detailsViewModel = detailsViewModel,
+                    isVisible = listening,
                 )
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index a56fabc..e247182 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -20,10 +20,12 @@
 
 import android.content.Context
 import android.content.res.Resources
+import android.os.Trace
 import android.service.quicksettings.Tile.STATE_ACTIVE
 import android.service.quicksettings.Tile.STATE_INACTIVE
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.combinedClickable
@@ -47,8 +49,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
@@ -58,8 +60,9 @@
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalResources
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.role
@@ -69,10 +72,13 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.trace
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.animation.Expandable
 import com.android.compose.animation.bounceable
+import com.android.compose.animation.rememberExpandableController
 import com.android.compose.modifiers.thenIf
+import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.Flags
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
@@ -80,7 +86,6 @@
 import com.android.systemui.haptics.msdl.qs.TileHapticsViewModel
 import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider
 import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.flags.QsDetailedView
 import com.android.systemui.qs.panels.ui.compose.BounceableInfo
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
@@ -88,9 +93,12 @@
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileStartPadding
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
+import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState
 import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.IconProvider
 import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toIconProvider
 import com.android.systemui.qs.panels.ui.viewmodel.toUiState
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.ui.compose.borderOnFocus
@@ -119,6 +127,9 @@
     )
 }
 
+private val TileViewModel.traceName
+    get() = spec.toString().takeLast(Trace.MAX_SECTION_NAME_LEN)
+
 @Composable
 fun Tile(
     tile: TileViewModel,
@@ -128,107 +139,120 @@
     bounceableInfo: BounceableInfo,
     tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider,
     modifier: Modifier = Modifier,
+    isVisible: () -> Boolean = { true },
     detailsViewModel: DetailsViewModel?,
 ) {
-    val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
-    val resources = resources()
+    trace(tile.traceName) {
+        val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
+        val resources = resources()
 
-    /*
-     * Use produce state because [QSTile.State] doesn't have well defined equals (due to
-     * inheritance). This way, even if tile.state changes, uiState may not change and lead to
-     * recomposition.
-     */
-    val uiState by
-        produceState(tile.currentState.toUiState(resources), tile, resources) {
-            tile.state.collect { value = it.toUiState(resources) }
-        }
+        /*
+         * Use produce state because [QSTile.State] doesn't have well defined equals (due to
+         * inheritance). This way, even if tile.state changes, uiState may not change and lead to
+         * recomposition.
+         */
+        val uiState by
+            produceState(tile.currentState.toUiState(resources), tile, resources) {
+                tile.state.collect { value = it.toUiState(resources) }
+            }
 
-    val colors = TileDefaults.getColorForState(uiState, iconOnly)
-    val hapticsViewModel: TileHapticsViewModel? =
-        rememberViewModel(traceName = "TileHapticsViewModel") {
-            tileHapticsViewModelFactoryProvider.getHapticsViewModelFactory()?.create(tile)
-        }
+        val icon by
+            produceState(tile.currentState.toIconProvider(), tile) {
+                tile.state.collect { value = it.toIconProvider() }
+            }
 
-    // TODO(b/361789146): Draw the shapes instead of clipping
-    val tileShape by TileDefaults.animateTileShapeAsState(uiState.state)
-    val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor")
+        val colors = TileDefaults.getColorForState(uiState, iconOnly)
+        val hapticsViewModel: TileHapticsViewModel? =
+            rememberViewModel(traceName = "TileHapticsViewModel") {
+                tileHapticsViewModelFactoryProvider.getHapticsViewModelFactory()?.create(tile)
+            }
 
-    TileExpandable(
-        color = { animatedColor },
-        shape = tileShape,
-        squishiness = squishiness,
-        hapticsViewModel = hapticsViewModel,
-        modifier =
-            modifier
-                .borderOnFocus(color = MaterialTheme.colorScheme.secondary, tileShape.topEnd)
-                .fillMaxWidth()
-                .bounceable(
-                    bounceable = currentBounceableInfo.bounceable,
-                    previousBounceable = currentBounceableInfo.previousTile,
-                    nextBounceable = currentBounceableInfo.nextTile,
-                    orientation = Orientation.Horizontal,
-                    bounceEnd = currentBounceableInfo.bounceEnd,
-                ),
-    ) { expandable ->
-        val longClick: (() -> Unit)? =
-            {
-                    hapticsViewModel?.setTileInteractionState(
-                        TileHapticsViewModel.TileInteractionState.LONG_CLICKED
+        // TODO(b/361789146): Draw the shapes instead of clipping
+        val tileShape by TileDefaults.animateTileShapeAsState(uiState.state)
+        val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor")
+        val animatedAlpha by animateFloatAsState(colors.alpha, label = "QSTileAlpha")
+
+        TileExpandable(
+            color = { animatedColor },
+            shape = tileShape,
+            squishiness = squishiness,
+            hapticsViewModel = hapticsViewModel,
+            modifier =
+                modifier
+                    .borderOnFocus(color = MaterialTheme.colorScheme.secondary, tileShape.topEnd)
+                    .fillMaxWidth()
+                    .bounceable(
+                        bounceable = currentBounceableInfo.bounceable,
+                        previousBounceable = currentBounceableInfo.previousTile,
+                        nextBounceable = currentBounceableInfo.nextTile,
+                        orientation = Orientation.Horizontal,
+                        bounceEnd = currentBounceableInfo.bounceEnd,
                     )
-                    tile.onLongClick(expandable)
-                }
-                .takeIf { uiState.handlesLongClick }
-        TileContainer(
-            onClick = {
-                var hasDetails = false
-                if (QsDetailedView.isEnabled) {
-                    hasDetails = detailsViewModel?.onTileClicked(tile.spec) == true
-                }
-                if (!hasDetails) {
-                    // For those tile's who doesn't have a detailed view, process with their
-                    // `onClick` behavior.
-                    tile.onClick(expandable)
-                    hapticsViewModel?.setTileInteractionState(
-                        TileHapticsViewModel.TileInteractionState.CLICKED
-                    )
-                    if (uiState.accessibilityUiState.toggleableState != null) {
-                        coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() }
+                    .graphicsLayer { alpha = animatedAlpha },
+        ) { expandable ->
+            val longClick: (() -> Unit)? =
+                {
+                        hapticsViewModel?.setTileInteractionState(
+                            TileHapticsViewModel.TileInteractionState.LONG_CLICKED
+                        )
+                        tile.onLongClick(expandable)
                     }
-                }
-            },
-            onLongClick = longClick,
-            uiState = uiState,
-            iconOnly = iconOnly,
-        ) {
-            val iconProvider: Context.() -> Icon = { getTileIcon(icon = uiState.icon) }
-            if (iconOnly) {
-                SmallTileContent(
-                    iconProvider = iconProvider,
-                    color = colors.icon,
-                    modifier = Modifier.align(Alignment.Center),
-                )
-            } else {
-                val iconShape by TileDefaults.animateIconShapeAsState(uiState.state)
-                val secondaryClick: (() -> Unit)? =
-                    {
-                            hapticsViewModel?.setTileInteractionState(
-                                TileHapticsViewModel.TileInteractionState.CLICKED
-                            )
-                            tile.onSecondaryClick()
+                    .takeIf { uiState.handlesLongClick }
+            TileContainer(
+                onClick = {
+                    var hasDetails = false
+                    if (QsDetailedView.isEnabled) {
+                        hasDetails = detailsViewModel?.onTileClicked(tile.spec) == true
+                    }
+                    if (!hasDetails) {
+                        // For those tile's who doesn't have a detailed view, process with their
+                        // `onClick` behavior.
+                        tile.onClick(expandable)
+                        hapticsViewModel?.setTileInteractionState(
+                            TileHapticsViewModel.TileInteractionState.CLICKED
+                        )
+                        if (uiState.accessibilityUiState.toggleableState != null) {
+                            coroutineScope.launch {
+                                currentBounceableInfo.bounceable.animateBounce()
+                            }
                         }
-                        .takeIf { uiState.handlesSecondaryClick }
-                LargeTileContent(
-                    label = uiState.label,
-                    secondaryLabel = uiState.secondaryLabel,
-                    iconProvider = iconProvider,
-                    sideDrawable = uiState.sideDrawable,
-                    colors = colors,
-                    iconShape = iconShape,
-                    toggleClick = secondaryClick,
-                    onLongClick = longClick,
-                    accessibilityUiState = uiState.accessibilityUiState,
-                    squishiness = squishiness,
-                )
+                    }
+                },
+                onLongClick = longClick,
+                accessibilityUiState = uiState.accessibilityUiState,
+                iconOnly = iconOnly,
+            ) {
+                val iconProvider: Context.() -> Icon = { getTileIcon(icon = icon) }
+                if (iconOnly) {
+                    SmallTileContent(
+                        iconProvider = iconProvider,
+                        color = colors.icon,
+                        modifier = Modifier.align(Alignment.Center),
+                    )
+                } else {
+                    val iconShape by TileDefaults.animateIconShapeAsState(uiState.state)
+                    val secondaryClick: (() -> Unit)? =
+                        {
+                                hapticsViewModel?.setTileInteractionState(
+                                    TileHapticsViewModel.TileInteractionState.CLICKED
+                                )
+                                tile.onSecondaryClick()
+                            }
+                            .takeIf { uiState.handlesSecondaryClick }
+                    LargeTileContent(
+                        label = uiState.label,
+                        secondaryLabel = uiState.secondaryLabel,
+                        iconProvider = iconProvider,
+                        sideDrawable = uiState.sideDrawable,
+                        colors = colors,
+                        iconShape = iconShape,
+                        toggleClick = secondaryClick,
+                        onLongClick = longClick,
+                        accessibilityUiState = uiState.accessibilityUiState,
+                        squishiness = squishiness,
+                        isVisible = isVisible,
+                    )
+                }
             }
         }
     }
@@ -244,8 +268,7 @@
     content: @Composable (Expandable) -> Unit,
 ) {
     Expandable(
-        color = color(),
-        shape = shape,
+        controller = rememberExpandableController(color = color, shape = shape),
         modifier = modifier.clip(shape).verticalSquish(squishiness),
         useModifierBasedImplementation = true,
     ) {
@@ -257,7 +280,7 @@
 fun TileContainer(
     onClick: () -> Unit,
     onLongClick: (() -> Unit)?,
-    uiState: TileUiState,
+    accessibilityUiState: AccessibilityUiState,
     iconOnly: Boolean,
     content: @Composable BoxScope.() -> Unit,
 ) {
@@ -268,7 +291,7 @@
                 .tileCombinedClickable(
                     onClick = onClick,
                     onLongClick = onLongClick,
-                    uiState = uiState,
+                    accessibilityUiState = accessibilityUiState,
                     iconOnly = iconOnly,
                 )
                 .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE)
@@ -278,7 +301,11 @@
 }
 
 @Composable
-fun LargeStaticTile(uiState: TileUiState, modifier: Modifier = Modifier) {
+fun LargeStaticTile(
+    uiState: TileUiState,
+    iconProvider: IconProvider,
+    modifier: Modifier = Modifier,
+) {
     val colors = TileDefaults.getColorForState(uiState = uiState, iconOnly = false)
 
     Box(
@@ -291,7 +318,7 @@
         LargeTileContent(
             label = uiState.label,
             secondaryLabel = "",
-            iconProvider = { getTileIcon(icon = uiState.icon) },
+            iconProvider = { getTileIcon(icon = iconProvider) },
             sideDrawable = null,
             colors = colors,
             squishiness = { 1f },
@@ -299,8 +326,8 @@
     }
 }
 
-private fun Context.getTileIcon(icon: QSTile.Icon?): Icon {
-    return icon?.let {
+private fun Context.getTileIcon(icon: IconProvider): Icon {
+    return icon.icon?.let {
         if (it is QSTileImpl.ResourceIcon) {
             Icon.Resource(it.resId, null)
         } else {
@@ -321,28 +348,26 @@
 fun Modifier.tileCombinedClickable(
     onClick: () -> Unit,
     onLongClick: (() -> Unit)?,
-    uiState: TileUiState,
+    accessibilityUiState: AccessibilityUiState,
     iconOnly: Boolean,
 ): Modifier {
     val longPressLabel = longPressLabel()
     return combinedClickable(
             onClick = onClick,
             onLongClick = onLongClick,
-            onClickLabel = uiState.accessibilityUiState.clickLabel,
+            onClickLabel = accessibilityUiState.clickLabel,
             onLongClickLabel = longPressLabel,
             hapticFeedbackEnabled = !Flags.msdlFeedback(),
         )
         .semantics {
-            role = uiState.accessibilityUiState.accessibilityRole
-            if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) {
-                uiState.accessibilityUiState.toggleableState?.let { toggleableState = it }
+            role = accessibilityUiState.accessibilityRole
+            if (accessibilityUiState.accessibilityRole == Role.Switch) {
+                accessibilityUiState.toggleableState?.let { toggleableState = it }
             }
-            stateDescription = uiState.accessibilityUiState.stateDescription
+            stateDescription = accessibilityUiState.stateDescription
         }
         .thenIf(iconOnly) {
-            Modifier.semantics {
-                contentDescription = uiState.accessibilityUiState.contentDescription
-            }
+            Modifier.semantics { contentDescription = accessibilityUiState.contentDescription }
         }
 }
 
@@ -352,6 +377,7 @@
     val label: Color,
     val secondaryLabel: Color,
     val icon: Color,
+    val alpha: Float = 1f,
 )
 
 private object TileDefaults {
@@ -375,10 +401,10 @@
     @ReadOnlyComposable
     fun activeDualTargetTileColors(): TileColors =
         TileColors(
-            background = MaterialTheme.colorScheme.surfaceVariant,
+            background = LocalAndroidColorScheme.current.surfaceEffect2,
             iconBackground = MaterialTheme.colorScheme.primary,
-            label = MaterialTheme.colorScheme.onSurfaceVariant,
-            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
+            label = MaterialTheme.colorScheme.onSurface,
+            secondaryLabel = MaterialTheme.colorScheme.onSurface,
             icon = MaterialTheme.colorScheme.onPrimary,
         )
 
@@ -386,30 +412,19 @@
     @ReadOnlyComposable
     fun inactiveDualTargetTileColors(): TileColors =
         TileColors(
-            background = MaterialTheme.colorScheme.surfaceVariant,
-            iconBackground = MaterialTheme.colorScheme.surfaceContainerHighest,
-            label = MaterialTheme.colorScheme.onSurfaceVariant,
-            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
-            icon = MaterialTheme.colorScheme.onSurfaceVariant,
+            background = LocalAndroidColorScheme.current.surfaceEffect2,
+            iconBackground = LocalAndroidColorScheme.current.surfaceEffect3,
+            label = MaterialTheme.colorScheme.onSurface,
+            secondaryLabel = MaterialTheme.colorScheme.onSurface,
+            icon = MaterialTheme.colorScheme.onSurface,
         )
 
     @Composable
     @ReadOnlyComposable
     fun inactiveTileColors(): TileColors =
         TileColors(
-            background = MaterialTheme.colorScheme.surfaceVariant,
-            iconBackground = MaterialTheme.colorScheme.surfaceVariant,
-            label = MaterialTheme.colorScheme.onSurfaceVariant,
-            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
-            icon = MaterialTheme.colorScheme.onSurfaceVariant,
-        )
-
-    @Composable
-    @ReadOnlyComposable
-    fun unavailableTileColors(): TileColors =
-        TileColors(
-            background = MaterialTheme.colorScheme.surface,
-            iconBackground = MaterialTheme.colorScheme.surface,
+            background = LocalAndroidColorScheme.current.surfaceEffect2,
+            iconBackground = Color.Transparent,
             label = MaterialTheme.colorScheme.onSurface,
             secondaryLabel = MaterialTheme.colorScheme.onSurface,
             icon = MaterialTheme.colorScheme.onSurface,
@@ -417,6 +432,19 @@
 
     @Composable
     @ReadOnlyComposable
+    fun unavailableTileColors(): TileColors {
+        return TileColors(
+            background = LocalAndroidColorScheme.current.surfaceEffect2,
+            iconBackground = LocalAndroidColorScheme.current.surfaceEffect2,
+            label = MaterialTheme.colorScheme.onSurface,
+            secondaryLabel = MaterialTheme.colorScheme.onSurface,
+            icon = MaterialTheme.colorScheme.onSurface,
+            alpha = .38f,
+        )
+    }
+
+    @Composable
+    @ReadOnlyComposable
     fun getColorForState(uiState: TileUiState, iconOnly: Boolean): TileColors {
         return when (uiState.state) {
             STATE_ACTIVE -> {
@@ -474,14 +502,15 @@
                 label = label,
             )
 
-        val corner = remember {
-            object : CornerSize {
-                override fun toPx(shapeSize: Size, density: Density): Float {
-                    return with(density) { animatedCornerRadius.toPx() }
+        return remember {
+            val corner =
+                object : CornerSize {
+                    override fun toPx(shapeSize: Size, density: Density): Float {
+                        return with(density) { animatedCornerRadius.toPx() }
+                    }
                 }
-            }
+            mutableStateOf(RoundedCornerShape(corner))
         }
-        return remember { derivedStateOf { RoundedCornerShape(corner) } }
     }
 }
 
@@ -493,5 +522,5 @@
 @ReadOnlyComposable
 private fun resources(): Resources {
     LocalConfiguration.current
-    return LocalContext.current.resources
+    return LocalResources.current
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
index 153238fc..57f63c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -59,12 +59,13 @@
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
 import androidx.compose.ui.unit.toSize
 import androidx.compose.ui.zIndex
-import com.android.compose.modifiers.size
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
 import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BADGE_ANGLE_RAD
+import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BadgeIconSize
 import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BadgeSize
 import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BadgeXOffset
 import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.BadgeYOffset
@@ -149,16 +150,15 @@
                         onClick = onClick,
                     )
             ) {
+                val size = with(LocalDensity.current) { BadgeIconSize.toDp() }
                 Icon(
                     Icons.Default.Remove,
                     contentDescription = null,
+                    tint = MaterialTheme.colorScheme.onPrimaryContainer,
                     modifier =
-                        Modifier.size(
-                                width = { decorationSize.width.roundToInt() },
-                                height = { decorationSize.height.roundToInt() },
-                            )
-                            .align(Alignment.Center)
-                            .graphicsLayer { this.alpha = badgeIconAlpha },
+                        Modifier.size(size).align(Alignment.Center).graphicsLayer {
+                            this.alpha = badgeIconAlpha
+                        },
                 )
             }
         }
@@ -218,13 +218,15 @@
                     )
                 }
         ) {
-            val secondaryColor = MaterialTheme.colorScheme.secondary
+            val size = with(LocalDensity.current) { BadgeIconSize.toDp() }
+            val primaryColor = MaterialTheme.colorScheme.primary
             Icon(
                 icon,
                 contentDescription = contentDescription,
+                tint = MaterialTheme.colorScheme.onPrimary,
                 modifier =
-                    Modifier.size(BadgeSize).align(Alignment.Center).drawBehind {
-                        drawCircle(secondaryColor)
+                    Modifier.size(size).align(Alignment.Center).drawBehind {
+                        drawCircle(primaryColor, radius = BadgeSize.toPx() / 2)
                     },
             )
         }
@@ -290,7 +292,7 @@
     return animateColor { state ->
         when (state) {
             None -> Color.Transparent
-            Removable -> MaterialTheme.colorScheme.secondary
+            Removable -> MaterialTheme.colorScheme.primaryContainer
             Selected -> MaterialTheme.colorScheme.primary
         }
     }
@@ -338,6 +340,7 @@
 private object SelectionDefaults {
     val SelectedBorderWidth = 2.dp
     val BadgeSize = 24.dp
+    val BadgeIconSize = 16.sp
     val BadgeXOffset = -4.dp
     val BadgeYOffset = 4.dp
     val ResizingPillWidth = 8.dp
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt
index 99f52c2..3ae90d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/toolbar/Toolbar.kt
@@ -16,14 +16,20 @@
 
 package com.android.systemui.qs.panels.ui.compose.toolbar
 
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
 import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.development.ui.compose.BuildNumber
 import com.android.systemui.qs.footer.ui.compose.IconButton
 import com.android.systemui.qs.panels.ui.viewmodel.toolbar.ToolbarViewModel
+import com.android.systemui.qs.ui.compose.borderOnFocus
 
 @Composable
 fun Toolbar(viewModel: ToolbarViewModel, modifier: Modifier = Modifier) {
@@ -44,7 +50,18 @@
             Modifier.sysuiResTag("settings_button_container"),
         )
 
-        Spacer(modifier = Modifier.weight(1f))
+        Box(modifier = Modifier.weight(1f), contentAlignment = Alignment.Center) {
+            BuildNumber(
+                viewModelFactory = viewModel.buildNumberViewModelFactory,
+                textColor = MaterialTheme.colorScheme.onSurface,
+                modifier =
+                    Modifier.borderOnFocus(
+                            color = MaterialTheme.colorScheme.secondary,
+                            cornerSize = CornerSize(1.dp),
+                        )
+                        .wrapContentSize(),
+            )
+        }
 
         IconButton(
             { viewModel.powerButtonViewModel },
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
index 03f0297..3287443 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.panels.ui.viewmodel
 
+import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import com.android.systemui.dagger.SysUISingleton
@@ -25,6 +26,7 @@
 import javax.inject.Inject
 
 @SysUISingleton
+@Stable
 class DetailsViewModel @Inject constructor(val currentTilesInteractor: CurrentTilesInteractor) {
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
index a9d673a..d6705a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
@@ -35,6 +35,9 @@
     private val hydrator = Hydrator("DynamicIconTilesViewModel")
     private val interactor = interactorFactory.create()
 
+    val largeTilesState =
+        hydrator.hydratedStateOf(traceName = "largeTiles", source = iconTilesViewModel.largeTiles)
+
     val largeTilesSpanState =
         hydrator.hydratedStateOf(
             traceName = "largeTilesSpan",
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
index 19e542e..15e71c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
@@ -22,12 +22,18 @@
 import android.text.TextUtils
 import android.widget.Switch
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.state.ToggleableState
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
 import com.android.systemui.res.R
+import java.util.function.Supplier
 
+/**
+ * Ui State for the tiles. It doesn't contain the icon to be able to invalidate the icon part
+ * separately. For the icon, use [IconProvider].
+ */
 @Immutable
 data class TileUiState(
     val label: String,
@@ -35,7 +41,6 @@
     val state: Int,
     val handlesLongClick: Boolean,
     val handlesSecondaryClick: Boolean,
-    val icon: QSTile.Icon?,
     val sideDrawable: Drawable?,
     val accessibilityUiState: AccessibilityUiState,
 )
@@ -90,7 +95,6 @@
         state = if (disabledByPolicy) Tile.STATE_UNAVAILABLE else state,
         handlesLongClick = handlesLongClick,
         handlesSecondaryClick = handlesSecondaryClick,
-        icon = icon ?: iconSupplier?.get(),
         sideDrawable = sideViewCustomDrawable,
         AccessibilityUiState(
             contentDescription?.toString() ?: "",
@@ -104,6 +108,14 @@
     )
 }
 
+fun QSTile.State.toIconProvider(): IconProvider {
+    return when {
+        icon != null -> IconProvider.ConstantIcon(icon)
+        iconSupplier != null -> IconProvider.IconSupplier(iconSupplier)
+        else -> IconProvider.Empty
+    }
+}
+
 private fun QSTile.State.getStateText(resources: Resources): CharSequence {
     val arrayResId = SubtitleArrayMapping.getSubtitleId(spec)
     val array = resources.getStringArray(arrayResId)
@@ -114,3 +126,21 @@
     val arrayResId = SubtitleArrayMapping.getSubtitleId(spec)
     return resources.getStringArray(arrayResId)[Tile.STATE_UNAVAILABLE]
 }
+
+@Stable
+sealed interface IconProvider {
+
+    val icon: QSTile.Icon?
+
+    data class ConstantIcon(override val icon: QSTile.Icon) : IconProvider
+
+    data class IconSupplier(val supplier: Supplier<QSTile.Icon?>) : IconProvider {
+        override val icon: QSTile.Icon?
+            get() = supplier.get()
+    }
+
+    data object Empty : IconProvider {
+        override val icon: QSTile.Icon?
+            get() = null
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt
index e54bfa2..10d7871 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModel.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.classifier.domain.interactor.FalsingInteractor
 import com.android.systemui.classifier.domain.interactor.runIfNotFalseTap
+import com.android.systemui.development.ui.viewmodel.BuildNumberViewModel
 import com.android.systemui.globalactions.GlobalActionsDialogLite
 import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.lifecycle.Hydrator
@@ -46,6 +47,7 @@
 @AssistedInject
 constructor(
     editModeButtonViewModelFactory: EditModeButtonViewModel.Factory,
+    val buildNumberViewModelFactory: BuildNumberViewModel.Factory,
     private val footerActionsInteractor: FooterActionsInteractor,
     private val globalActionsDialogLiteProvider: Provider<GlobalActionsDialogLite>,
     private val falsingInteractor: FalsingInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index 16aa99e..f155629 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -35,8 +35,8 @@
 import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractorImpl
 import com.android.systemui.qs.pipeline.domain.startable.QSPipelineCoreStartable
 import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractorImpl
+import com.android.systemui.qs.tiles.base.domain.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.DisabledByPolicyInteractorImpl
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
index 1cd5d10..e3a8ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
@@ -6,7 +6,7 @@
 import android.provider.Settings
 import android.util.Log
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.common.shared.model.PackageChangeModel.Empty.user
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddable.kt
index 88a49ee..53d2554 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddable.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.qs.pipeline.domain.autoaddable
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
 import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
 import com.android.systemui.qs.pipeline.domain.model.AutoAddable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddable.kt
index 76bfad9..a3b4c71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddable.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.qs.pipeline.domain.autoaddable
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
 import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt
index e9c91ca..66af6d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt
@@ -19,7 +19,7 @@
 import android.content.Context
 import android.hardware.display.ColorDisplayManager
 import android.hardware.display.NightDisplayListener
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.NightDisplayListenerModule
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt
index 88d7f06..ff3fd37 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddable.kt
@@ -21,7 +21,7 @@
 import android.content.res.Resources
 import android.text.TextUtils
 import com.android.systemui.res.R
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
index 3f619c0..c66c9dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
@@ -18,7 +18,7 @@
 
 import android.content.pm.UserInfo
 import android.os.UserHandle
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.pipeline.data.restoreprocessors.WorkTileRestoreProcessor
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index c70a854..a3846e3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -42,7 +42,7 @@
 import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
-import com.android.systemui.qs.tiles.di.NewQSTileFactory
+import com.android.systemui.qs.tiles.base.ui.model.NewQSTileFactory
 import com.android.systemui.qs.toProto
 import com.android.systemui.retail.data.repository.RetailModeRepository
 import com.android.systemui.settings.UserTracker
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index c6fc868..0db05c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -92,6 +92,7 @@
 
     private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS;
     protected static final Object ARG_SHOW_TRANSIENT_ENABLING = new Object();
+    protected static final Object ARG_SHOW_TRANSIENT_DISABLING = new Object();
 
     private static final int READY_STATE_NOT_READY = 0;
     private static final int READY_STATE_READYING = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
index 61a8fa3..cd0b70e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SubtitleArrayMapping.kt
@@ -27,6 +27,7 @@
         subtitleIdsMap["cell"] = R.array.tile_states_cell
         subtitleIdsMap["battery"] = R.array.tile_states_battery
         subtitleIdsMap["dnd"] = R.array.tile_states_dnd
+        subtitleIdsMap["modes_dnd"] = R.array.tile_states_modes_dnd
         subtitleIdsMap["flashlight"] = R.array.tile_states_flashlight
         subtitleIdsMap["rotation"] = R.array.tile_states_rotation
         subtitleIdsMap["bt"] = R.array.tile_states_bt
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 4b1c90f..9aeaa22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -56,6 +56,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.flags.QsInCompose;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.res.R;
@@ -196,11 +197,18 @@
     protected void handleUpdateState(BooleanState state, Object arg) {
         checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_BLUETOOTH);
         final boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
-        final boolean enabled = transientEnabling || mController.isBluetoothEnabled();
+        final boolean transientDisabling =
+                QsInCompose.isEnabled() && arg == ARG_SHOW_TRANSIENT_DISABLING;
+        final boolean enabled =
+                transientEnabling || (mController.isBluetoothEnabled() && !transientDisabling);
         final boolean connected = mController.isBluetoothConnected();
         final boolean connecting = mController.isBluetoothConnecting();
-        state.isTransient = transientEnabling || connecting ||
-                mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
+        state.isTransient = transientEnabling || transientDisabling || connecting
+                || mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
+        if (QsInCompose.isEnabled()) {
+            state.isTransient = state.isTransient
+                    || mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_OFF;
+        }
         if (!enabled || !connected || state.isTransient) {
             stopListeningToStaleDeviceMetadata();
         }
@@ -208,7 +216,8 @@
         state.value = enabled;
         state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
         state.secondaryLabel = TextUtils.emptyIfNull(
-                getSecondaryLabel(enabled, connecting, connected, state.isTransient));
+                getSecondaryLabel(enabled, connecting, connected,
+                        state.isTransient && transientEnabling));
         state.contentDescription = mContext.getString(
                 R.string.accessibility_quick_settings_bluetooth);
         state.stateDescription = "";
@@ -241,8 +250,13 @@
 
     private void toggleBluetooth() {
         final boolean isEnabled = mState.value;
-        // Immediately enter transient enabling state when turning bluetooth on.
-        refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
+        if (QsInCompose.isEnabled()) {
+            // Immediately enter transient enabling state when toggling bluetooth state.
+            refreshState(isEnabled ? ARG_SHOW_TRANSIENT_DISABLING : ARG_SHOW_TRANSIENT_ENABLING);
+        } else {
+            // Immediately enter transient enabling state when turning bluetooth on.
+            refreshState(isEnabled ? ARG_SHOW_TRANSIENT_DISABLING : null);
+        }
         mController.setBluetoothEnabled(!isEnabled);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt
new file mode 100644
index 0000000..43e3de2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import androidx.annotation.DrawableRes
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.coroutineScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.animation.Expandable
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.modes.shared.ModesUi
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QSTile.BooleanState
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.asQSTileIcon
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileDataInteractor
+import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
+import com.android.systemui.qs.tiles.impl.modes.ui.ModesDndTileMapper
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.runBlocking
+
+/**
+ * Standalone tile used to control the DND Mode. Contrast to [ModesTile] (the tile that opens a
+ * dialog showing the list of all modes) and [DndTile] (the tile used to toggle interruption
+ * filtering in the pre-MODES_UI world).
+ */
+class ModesDndTile
+@Inject
+constructor(
+    host: QSHost,
+    uiEventLogger: QsEventLogger,
+    @Background backgroundLooper: Looper,
+    @Main mainHandler: Handler,
+    falsingManager: FalsingManager,
+    metricsLogger: MetricsLogger,
+    statusBarStateController: StatusBarStateController,
+    activityStarter: ActivityStarter,
+    qsLogger: QSLogger,
+    qsTileConfigProvider: QSTileConfigProvider,
+    private val dataInteractor: ModesDndTileDataInteractor,
+    private val tileMapper: ModesDndTileMapper,
+    private val userActionInteractor: ModesDndTileUserActionInteractor,
+) :
+    QSTileImpl<BooleanState>(
+        host,
+        uiEventLogger,
+        backgroundLooper,
+        mainHandler,
+        falsingManager,
+        metricsLogger,
+        statusBarStateController,
+        activityStarter,
+        qsLogger,
+    ) {
+
+    private lateinit var tileState: QSTileState
+    private val config = qsTileConfigProvider.getConfig(TILE_SPEC)
+
+    init {
+        lifecycle.coroutineScope.launch {
+            lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+                dataInteractor.tileData().collect { refreshState(it) }
+            }
+        }
+    }
+
+    override fun isAvailable(): Boolean = ModesUi.isEnabled && android.app.Flags.modesUiDndTile()
+
+    override fun getTileLabel(): CharSequence =
+        mContext.getString(R.string.quick_settings_dnd_label)
+
+    override fun newTileState(): BooleanState = BooleanState()
+
+    override fun handleClick(expandable: Expandable?) = runBlocking {
+        userActionInteractor.handleClick()
+    }
+
+    override fun getLongClickIntent(): Intent? = userActionInteractor.getSettingsIntent()
+
+    @VisibleForTesting
+    public override fun handleUpdateState(state: BooleanState?, arg: Any?) {
+        val model = arg as? ModesDndTileModel ?: dataInteractor.getCurrentTileModel()
+
+        tileState = tileMapper.map(config, model)
+        state?.apply {
+            value = model.isActivated
+            this.state = tileState.activationState.legacyState
+            icon =
+                tileState.icon?.asQSTileIcon()
+                    ?: maybeLoadResourceIcon(iconResId(model.isActivated))
+            label = tileLabel
+            secondaryLabel = tileState.secondaryLabel
+            contentDescription = tileState.contentDescription
+            stateDescription = tileState.stateDescription
+            expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
+        }
+    }
+
+    @DrawableRes
+    private fun iconResId(activated: Boolean): Int =
+        if (activated) R.drawable.qs_dnd_icon_on else R.drawable.qs_dnd_icon_off
+
+    companion object {
+        const val TILE_SPEC = "modes_dnd"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index ad5dd27..fcfa46f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -42,13 +42,13 @@
 import com.android.systemui.qs.asQSTileIcon
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
 import com.android.systemui.qs.tiles.dialog.ModesDetailsViewModel
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.impl.modes.ui.ModesTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.impl.modes.ui.mapper.ModesTileMapper
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
index 5ba1527..c021b22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
@@ -33,12 +33,12 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.qs.tiles.impl.notes.domain.NotesTileMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
 import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileDataInteractor
 import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.impl.notes.ui.mapper.NotesTileMapper
 import com.android.systemui.res.R
 import javax.inject.Inject
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandler.kt
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandler.kt
index 972b20e..386e1a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandler.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.qs.tiles.base.domain.actions
 
 import android.app.PendingIntent
 import android.content.Intent
@@ -36,14 +36,14 @@
     fun handle(
         expandable: Expandable?,
         intent: Intent,
-        dismissShadeShowOverLockScreenWhenLocked: Boolean = false
+        dismissShadeShowOverLockScreenWhenLocked: Boolean = false,
     )
 
     /** @param requestLaunchingDefaultActivity used in case !pendingIndent.isActivity */
     fun handle(
         expandable: Expandable?,
         pendingIntent: PendingIntent,
-        requestLaunchingDefaultActivity: Boolean = false
+        requestLaunchingDefaultActivity: Boolean = false,
     )
 }
 
@@ -59,7 +59,7 @@
     override fun handle(
         expandable: Expandable?,
         intent: Intent,
-        dismissShadeShowOverLockScreenWhenLocked: Boolean
+        dismissShadeShowOverLockScreenWhenLocked: Boolean,
     ) {
         val animationController: ActivityTransitionAnimator.Controller? =
             expandable?.activityTransitionController(
@@ -70,7 +70,7 @@
                 intent,
                 true /* dismissShade */,
                 animationController,
-                true /* showOverLockscreenWhenLocked */
+                true, /* showOverLockscreenWhenLocked */
             )
         } else {
             activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController)
@@ -81,7 +81,7 @@
     override fun handle(
         expandable: Expandable?,
         pendingIntent: PendingIntent,
-        requestLaunchingDefaultActivity: Boolean
+        requestLaunchingDefaultActivity: Boolean,
     ) {
         if (pendingIntent.isActivity) {
             val animationController: ActivityTransitionAnimator.Controller? =
@@ -101,7 +101,7 @@
                 packageManager.queryIntentActivitiesAsUser(
                     intent,
                     PackageManager.ResolveInfoFlags.of(0L),
-                    userHandle.identifier
+                    userHandle.identifier,
                 )
             intents
                 .firstOrNull { it.activityInfo.exported }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractor.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractor.kt
index 4577527..da4d3d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractor.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
 
 import android.content.Context
 import android.os.UserHandle
@@ -26,7 +26,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor.PolicyResult
+import com.android.systemui.qs.tiles.base.domain.interactor.DisabledByPolicyInteractor.PolicyResult
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
@@ -110,17 +110,9 @@
 
     @WorkerThread
     fun hasBaseUserRestriction(userId: Int, userRestriction: String?): Boolean =
-        RestrictedLockUtilsInternal.hasBaseUserRestriction(
-            context,
-            userRestriction,
-            userId,
-        )
+        RestrictedLockUtilsInternal.hasBaseUserRestriction(context, userRestriction, userId)
 
     @WorkerThread
     fun getEnforcedAdmin(userId: Int, userRestriction: String?): EnforcedAdmin? =
-        RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
-            context,
-            userRestriction,
-            userId,
-        )
+        RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context, userRestriction, userId)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileDataInteractor.kt
similarity index 82%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileDataInteractor.kt
index 9a776f2..1eb5315 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileDataInteractor.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
 
 import android.os.UserHandle
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
@@ -49,10 +50,11 @@
     fun availability(user: UserHandle): Flow<Boolean>
 
     companion object {
-        val AlwaysAvailableInteractor = object : QSTileAvailabilityInteractor {
-            override fun availability(user: UserHandle): Flow<Boolean> {
-                return flowOf(true)
+        val AlwaysAvailableInteractor =
+            object : QSTileAvailabilityInteractor {
+                override fun availability(user: UserHandle): Flow<Boolean> {
+                    return flowOf(true)
+                }
             }
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileUserActionInteractor.kt
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileUserActionInteractor.kt
index 8ad4e16..e49616c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileUserActionInteractor.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
 
 import android.annotation.WorkerThread
-import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
 
 interface QSTileUserActionInteractor<DATA_TYPE> {
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DataUpdateTrigger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/DataUpdateTrigger.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DataUpdateTrigger.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/DataUpdateTrigger.kt
index 4f25d3c..a77e551 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DataUpdateTrigger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/DataUpdateTrigger.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.model
 
 /** Event that triggers data update */
 sealed interface DataUpdateTrigger {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileInput.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInput.kt
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileInput.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInput.kt
index 77ff609..59236be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileInput.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInput.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.model
 
 import android.os.UserHandle
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 
 /** @see QSTileUserActionInteractor.handleInput */
-data class QSTileInput<T>(
-    val user: UserHandle,
-    val action: QSTileUserAction,
-    val data: T,
-)
+data class QSTileInput<T>(val user: UserHandle, val action: QSTileUserAction, val data: T)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLogger.kt
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLogger.kt
index 8ec8a6d..04d40ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLogger.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.logging
+package com.android.systemui.qs.tiles.base.shared.logging
 
 import androidx.annotation.GuardedBy
 import com.android.systemui.dagger.SysUISingleton
@@ -24,8 +24,8 @@
 import com.android.systemui.log.dagger.QSTilesLogBuffers
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.statusbar.StatusBarState
 import javax.inject.Inject
 
@@ -65,22 +65,19 @@
                         "statusBarState=${StatusBarState.toString(int1)}, " +
                         "hasState=$bool1, " +
                         "hasData=$bool2"
-                }
+                },
             )
     }
 
     /** Tracks user action when it's rejected by false gestures */
-    fun logUserActionRejectedByFalsing(
-        userAction: QSTileUserAction,
-        tileSpec: TileSpec,
-    ) {
+    fun logUserActionRejectedByFalsing(userAction: QSTileUserAction, tileSpec: TileSpec) {
         tileSpec
             .getLogBuffer()
             .log(
                 tileSpec.getLogTag(),
                 LogLevel.DEBUG,
                 { str1 = userAction.toLogString() },
-                { "tile $str1: rejected by falsing" }
+                { "tile $str1: rejected by falsing" },
             )
     }
 
@@ -96,7 +93,7 @@
                 tileSpec.getLogTag(),
                 LogLevel.DEBUG,
                 { str1 = userAction.toLogString() },
-                { "tile $str1: rejected by policy, restriction: $restriction" }
+                { "tile $str1: rejected by policy, restriction: $restriction" },
             )
     }
 
@@ -124,7 +121,7 @@
                         "statusBarState=${StatusBarState.toString(int1)}, " +
                         "state=$str2, " +
                         "data=$str3"
-                }
+                },
             )
     }
 
@@ -141,11 +138,7 @@
     }
 
     /** Tracks state changes based on the data and trigger event. */
-    fun <T> logStateUpdate(
-        tileSpec: TileSpec,
-        tileState: QSTileState,
-        data: T,
-    ) {
+    fun <T> logStateUpdate(tileSpec: TileSpec, tileState: QSTileState, data: T) {
         tileSpec
             .getLogBuffer()
             .log(
@@ -155,41 +148,23 @@
                     str1 = tileState.toLogString()
                     str2 = data.toString().take(DATA_MAX_LENGTH)
                 },
-                { "tile state update: state=$str1, data=$str2" }
+                { "tile state update: state=$str1, data=$str2" },
             )
     }
 
-    fun logError(
-        tileSpec: TileSpec,
-        message: String,
-        error: Throwable,
-    ) {
-        tileSpec
-            .getLogBuffer()
-            .log(
-                tileSpec.getLogTag(),
-                LogLevel.ERROR,
-                {},
-                { message },
-                error,
-            )
+    fun logError(tileSpec: TileSpec, message: String, error: Throwable) {
+        tileSpec.getLogBuffer().log(tileSpec.getLogTag(), LogLevel.ERROR, {}, { message }, error)
     }
 
     /** Log with level [LogLevel.WARNING] */
-    fun logWarning(
-        tileSpec: TileSpec,
-        message: String,
-    ) {
+    fun logWarning(tileSpec: TileSpec, message: String) {
         tileSpec
             .getLogBuffer()
             .log(tileSpec.getLogTag(), LogLevel.WARNING, { str1 = message }, { str1!! })
     }
 
     /** Log with level [LogLevel.INFO] */
-    fun logInfo(
-        tileSpec: TileSpec,
-        message: String,
-    ) {
+    fun logInfo(tileSpec: TileSpec, message: String) {
         tileSpec
             .getLogBuffer()
             .log(tileSpec.getLogTag(), LogLevel.INFO, { str1 = message }, { str1!! })
@@ -214,7 +189,7 @@
                 factory.create(
                     this.getLogTag(),
                     BUFFER_MAX_SIZE /* maxSize */,
-                    false /* systrace */
+                    false, /* systrace */
                 )
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfig.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfig.kt
index 3a9cb17..7c2a08f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfig.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
 
 import android.content.res.Resources
 import androidx.annotation.DrawableRes
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProvider.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProvider.kt
index 4dbddd9..e2d1605 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProvider.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
 
 import com.android.internal.util.Preconditions
 import com.android.systemui.dagger.SysUISingleton
@@ -51,7 +51,7 @@
             val keyTileSpec = entry.key
             Preconditions.checkArgument(
                 configTileSpec == keyTileSpec,
-                "A wrong config is injected keySpec=$keyTileSpec configSpec=$configTileSpec"
+                "A wrong config is injected keySpec=$keyTileSpec configSpec=$configTileSpec",
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileCoroutineScopeFactory.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileCoroutineScopeFactory.kt
index 5f476ea..018a06f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileCoroutineScopeFactory.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
 
 import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileScope.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileScope.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileScope.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileScope.kt
index a412de3..a0caedb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileScope.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileScope.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.di
+package com.android.systemui.qs.tiles.base.shared.model
 
 import javax.inject.Scope
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileState.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileState.kt
index c6af729..5a19eae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileState.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
 
 import android.content.res.Resources
 import android.content.res.Resources.Theme
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileUserAction.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileUserAction.kt
index bf3bc73..bbf04f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileUserAction.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
 
 import com.android.systemui.animation.Expandable
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalytics.kt
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalytics.kt
index 1d42777..8699148 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalytics.kt
@@ -14,22 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.analytics
+package com.android.systemui.qs.tiles.base.ui.analytics
 
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.QSEvent
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import javax.inject.Inject
 
 /** Tracks QS tiles analytic events to [UiEventLogger]. */
 @SysUISingleton
-class QSTileAnalytics
-@Inject
-constructor(
-    private val uiEventLogger: UiEventLogger,
-) {
+class QSTileAnalytics @Inject constructor(private val uiEventLogger: UiEventLogger) {
 
     fun trackUserAction(config: QSTileConfig, action: QSTileUserAction) {
         logAction(config, action)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactory.kt
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactory.kt
index f65fdb5..fca7abe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactory.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,18 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.di
+package com.android.systemui.qs.tiles.base.ui.model
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.qs.QSFactory
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelAdapter
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
 import javax.inject.Inject
 import javax.inject.Provider
 
@@ -58,8 +58,7 @@
                 is TileSpec.PlatformTileSpec ->
                     tileMap[tileSpec]?.get()?.takeIf { it !is StubQSTileViewModel }
                 is TileSpec.Invalid -> null
-            }
-                ?: return null
+            } ?: return null
         return adapterFactory.create(viewModel)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileComponent.kt
similarity index 78%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileComponent.kt
index 5f692f2..f63c2db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileComponent.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,12 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.di
+package com.android.systemui.qs.tiles.base.ui.model
 
-import com.android.systemui.plugins.qs.TileDetailsViewModel
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
 import kotlinx.coroutines.CoroutineScope
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileDataToStateMapper.kt
similarity index 79%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileDataToStateMapper.kt
index 2bc6643..7be2ed1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileDataToStateMapper.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.ui.model
 
 import androidx.annotation.WorkerThread
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
 
 interface QSTileDataToStateMapper<DATA_TYPE> {
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTilesModule.kt
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTilesModule.kt
index 222fa3e..6b31539 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTilesModule.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,20 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.di
+package com.android.systemui.qs.tiles.base.ui.model
 
 import android.content.Context
 import android.content.res.Resources.Theme
 import com.android.systemui.qs.external.CustomTileStatePersister
 import com.android.systemui.qs.external.CustomTileStatePersisterImpl
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerImpl
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProviderImpl
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerImpl
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProviderImpl
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.impl.custom.ui.model.CustomTileComponent
 import com.android.systemui.shade.ShadeDisplayAware
 import dagger.Binds
 import dagger.Module
@@ -35,12 +35,7 @@
 import dagger.multibindings.Multibinds
 
 /** Module listing subcomponents */
-@Module(
-    subcomponents =
-        [
-            CustomTileComponent::class,
-        ]
-)
+@Module(subcomponents = [CustomTileComponent::class])
 interface QSTilesModule {
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModel.kt
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModel.kt
index 7a53388..51ebf14 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,10 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
 
 import android.os.UserHandle
 import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import kotlinx.coroutines.flow.StateFlow
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapter.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapter.kt
index e607eae..f604de6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapter.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
 
 import android.content.Context
 import android.os.UserHandle
@@ -31,6 +31,10 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
 import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
 import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelFactory.kt
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelFactory.kt
index 7f475f3..7404710 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelFactory.kt
@@ -14,26 +14,25 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
 
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.UiBackground
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.TileDetailsViewModel
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
-import com.android.systemui.qs.tiles.impl.custom.di.QSTileConfigModule
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileComponent
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.domain.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileCoroutineScopeFactory
+import com.android.systemui.qs.tiles.base.ui.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.model.QSTileComponent
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.shared.model.QSTileConfigModule
+import com.android.systemui.qs.tiles.impl.custom.ui.model.CustomTileComponent
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImpl.kt
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImpl.kt
index 3866c17..f8723e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImpl.kt
@@ -14,26 +14,26 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
 
+import android.annotation.SuppressLint
 import android.os.UserHandle
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Dumpable
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.TileDetailsViewModel
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.domain.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.base.ui.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.kotlin.throttle
 import com.android.systemui.util.time.SystemClock
@@ -89,8 +89,10 @@
     private val users: MutableStateFlow<UserHandle> =
         MutableStateFlow(userRepository.getSelectedUserInfo().userHandle)
 
+    @SuppressLint("SharedFlowCreation")
     private val userInputs: MutableSharedFlow<QSTileUserAction> = MutableSharedFlow()
 
+    @SuppressLint("SharedFlowCreation")
     private val forceUpdates: MutableSharedFlow<Unit> = MutableSharedFlow()
     private val spec
         get() = config.tileSpec
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/StubQSTileViewModel.kt
similarity index 80%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/StubQSTileViewModel.kt
index bdd5c73..304b804 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/StubQSTileViewModel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,9 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
 
 import android.os.UserHandle
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import kotlinx.coroutines.flow.StateFlow
 
 object StubQSTileViewModel : QSTileViewModel {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContent.kt
index 7d396c5..8ffba1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContent.kt
@@ -19,26 +19,14 @@
 import android.view.LayoutInflater
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.viewinterop.AndroidView
 import com.android.systemui.res.R
 
 @Composable
 fun InternetDetailsContent(viewModel: InternetDetailsViewModel) {
     val coroutineScope = rememberCoroutineScope()
-    val context = LocalContext.current
-
-    val internetDetailsContentManager = remember {
-        viewModel.contentManagerFactory.create(
-            canConfigMobileData = viewModel.getCanConfigMobileData(),
-            canConfigWifi = viewModel.getCanConfigWifi(),
-            coroutineScope = coroutineScope,
-            context = context,
-        )
-    }
 
     AndroidView(
         modifier = Modifier.fillMaxSize(),
@@ -46,11 +34,11 @@
             // Inflate with the existing dialog xml layout and bind it with the manager
             val view =
                 LayoutInflater.from(context).inflate(R.layout.internet_connectivity_dialog, null)
-            internetDetailsContentManager.bind(view)
+            viewModel.internetDetailsContentManager.bind(view, coroutineScope)
 
             view
             // TODO: b/377388104 - Polish the internet details view UI
         },
-        onRelease = { internetDetailsContentManager.unBind() },
+        onRelease = { viewModel.internetDetailsContentManager.unBind() },
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
index 659488b..d8e1755 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
@@ -43,6 +43,9 @@
 import android.widget.TextView
 import androidx.annotation.MainThread
 import androidx.annotation.WorkerThread
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.LifecycleRegistry
@@ -79,8 +82,6 @@
     private val internetDetailsContentController: InternetDetailsContentController,
     @Assisted(CAN_CONFIG_MOBILE_DATA) private val canConfigMobileData: Boolean,
     @Assisted(CAN_CONFIG_WIFI) private val canConfigWifi: Boolean,
-    @Assisted private val coroutineScope: CoroutineScope,
-    @Assisted private var context: Context,
     private val uiEventLogger: UiEventLogger,
     @Main private val handler: Handler,
     @Background private val backgroundExecutor: Executor,
@@ -121,26 +122,29 @@
     private lateinit var shareWifiButton: Button
     private lateinit var airplaneModeButton: Button
     private var alertDialog: AlertDialog? = null
-
-    private val canChangeWifiState =
-        WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context)
+    private var canChangeWifiState = false
     private var wifiNetworkHeight = 0
     private var backgroundOn: Drawable? = null
     private var backgroundOff: Drawable? = null
     private var clickJob: Job? = null
     private var defaultDataSubId = internetDetailsContentController.defaultDataSubscriptionId
-    @VisibleForTesting
-    internal var adapter = InternetAdapter(internetDetailsContentController, coroutineScope)
+    @VisibleForTesting internal lateinit var adapter: InternetAdapter
     @VisibleForTesting internal var wifiEntriesCount: Int = 0
     @VisibleForTesting internal var hasMoreWifiEntries: Boolean = false
+    private lateinit var context: Context
+    private lateinit var coroutineScope: CoroutineScope
+
+    var title by mutableStateOf("")
+        private set
+
+    var subTitle by mutableStateOf("")
+        private set
 
     @AssistedFactory
     interface Factory {
         fun create(
             @Assisted(CAN_CONFIG_MOBILE_DATA) canConfigMobileData: Boolean,
             @Assisted(CAN_CONFIG_WIFI) canConfigWifi: Boolean,
-            coroutineScope: CoroutineScope,
-            context: Context,
         ): InternetDetailsContentManager
     }
 
@@ -152,12 +156,16 @@
      *
      * @param contentView The view to which the content manager should be bound.
      */
-    fun bind(contentView: View) {
+    fun bind(contentView: View, coroutineScope: CoroutineScope) {
         if (DEBUG) {
             Log.d(TAG, "Bind InternetDetailsContentManager")
         }
 
         this.contentView = contentView
+        context = contentView.context
+        this.coroutineScope = coroutineScope
+        adapter = InternetAdapter(internetDetailsContentController, coroutineScope)
+        canChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context)
 
         initializeLifecycle()
         initializeViews()
@@ -323,11 +331,11 @@
         }
     }
 
-    fun getTitleText(): String {
+    private fun getTitleText(): String {
         return internetDetailsContentController.getDialogTitleText().toString()
     }
 
-    fun getSubtitleText(): String {
+    private fun getSubtitleText(): String {
         return internetDetailsContentController.getSubtitleText(isProgressBarVisible).toString()
     }
 
@@ -336,6 +344,13 @@
             Log.d(TAG, "updateDetailsUI ")
         }
 
+        if (!::context.isInitialized) {
+            return
+        }
+
+        title = getTitleText()
+        subTitle = getSubtitleText()
+
         airplaneModeButton.visibility =
             if (internetContent.isAirplaneModeEnabled) View.VISIBLE else View.GONE
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
index 6709fd2..8dc73e3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
@@ -19,7 +19,7 @@
 import android.content.Intent
 import android.provider.Settings
 import com.android.systemui.plugins.qs.TileDetailsViewModel
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
 import com.android.systemui.statusbar.connectivity.AccessPointController
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -28,9 +28,22 @@
 @AssistedInject
 constructor(
     private val accessPointController: AccessPointController,
-    val contentManagerFactory: InternetDetailsContentManager.Factory,
+    private val contentManagerFactory: InternetDetailsContentManager.Factory,
     private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
-) : TileDetailsViewModel() {
+) : TileDetailsViewModel {
+    val internetDetailsContentManager by lazy {
+        contentManagerFactory.create(
+            canConfigMobileData = accessPointController.canConfigMobileData(),
+            canConfigWifi = accessPointController.canConfigWifi(),
+        )
+    }
+
+    override val title: String
+        get() = internetDetailsContentManager.title
+
+    override val subTitle: String
+        get() = internetDetailsContentManager.subTitle
+
     override fun clickOnSettingsButton() {
         qsTileIntentUserActionHandler.handle(
             /* expandable= */ null,
@@ -38,30 +51,6 @@
         )
     }
 
-    override fun getTitle(): String {
-        // TODO: b/377388104 make title and sub title mutable states of string
-        // by internetDetailsContentManager.getTitleText()
-        // TODO: test title change between airplane mode and not airplane mode
-        // TODO: b/377388104 Update the placeholder text
-        return "Internet"
-    }
-
-    override fun getSubTitle(): String {
-        // TODO: b/377388104 make title and sub title mutable states of string
-        // by internetDetailsContentManager.getSubtitleText()
-        // TODO: test subtitle change between airplane mode and not airplane mode
-        // TODO: b/377388104 Update the placeholder text
-        return "Tab a network to connect"
-    }
-
-    fun getCanConfigMobileData(): Boolean {
-        return accessPointController.canConfigMobileData()
-    }
-
-    fun getCanConfigWifi(): Boolean {
-        return accessPointController.canConfigWifi()
-    }
-
     @AssistedFactory
     fun interface Factory {
         fun create(): InternetDetailsViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt
index 9a39c3c..4f7e03b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt
@@ -23,18 +23,14 @@
 class ModesDetailsViewModel(
     private val onSettingsClick: () -> Unit,
     val viewModel: ModesDialogViewModel,
-) : TileDetailsViewModel() {
+) : TileDetailsViewModel {
     override fun clickOnSettingsButton() {
         onSettingsClick()
     }
 
-    override fun getTitle(): String {
-        // TODO(b/388321032): Replace this string with a string in a translatable xml file.
-        return "Modes"
-    }
+    // TODO(b/388321032): Replace this string with a string in a translatable xml file.
+    override val title = "Modes"
 
-    override fun getSubTitle(): String {
-        // TODO(b/388321032): Replace this string with a string in a translatable xml file.
-        return "Silences interruptions from people and apps in different circumstances"
-    }
+    // TODO(b/388321032): Replace this string with a string in a translatable xml file.
+    override val subTitle = "Silences interruptions from people and apps in different circumstances"
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt
index c84ddb6..59f209e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt
@@ -23,19 +23,15 @@
 class ScreenRecordDetailsViewModel(
     val recordingController: RecordingController,
     val onStartRecordingClicked: Runnable,
-) : TileDetailsViewModel() {
+) : TileDetailsViewModel {
 
     override fun clickOnSettingsButton() {
         // No settings button in this tile.
     }
 
-    override fun getTitle(): String {
-        // TODO(b/388321032): Replace this string with a string in a translatable xml file,
-        return "Screen recording"
-    }
+    // TODO(b/388321032): Replace this string with a string in a translatable xml file,
+    override val title = "Screen recording"
 
-    override fun getSubTitle(): String {
-        // No sub-title in this tile.
-        return ""
-    }
+    // No sub-title in this tile.
+    override val subTitle = ""
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractor.kt
index 4f01a04..63dd9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractor.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.qs.tiles.impl.airplane.domain.interactor
 
 import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
 import javax.inject.Inject
@@ -29,13 +29,12 @@
 /** Observes airplane mode state changes providing the [AirplaneModeTileModel]. */
 class AirplaneModeTileDataInteractor
 @Inject
-constructor(
-    private val airplaneModeRepository: AirplaneModeRepository,
-) : QSTileDataInteractor<AirplaneModeTileModel> {
+constructor(private val airplaneModeRepository: AirplaneModeRepository) :
+    QSTileDataInteractor<AirplaneModeTileModel> {
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<AirplaneModeTileModel> =
         airplaneModeRepository.isAirplaneMode.map { AirplaneModeTileModel(it) }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
index 5053291..e86d951 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
@@ -19,11 +19,11 @@
 import android.content.Intent
 import android.provider.Settings
 import android.telephony.TelephonyManager
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import javax.inject.Inject
 
@@ -54,7 +54,7 @@
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
-                        Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS)
+                        Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapper.kt
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapper.kt
index 80d429c..b0b7b4e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapper.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,21 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.airplane.domain
+package com.android.systemui.qs.tiles.impl.airplane.ui.mapper
 
 import android.content.res.Resources
 import android.content.res.Resources.Theme
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 
 /** Maps [AirplaneModeTileModel] to [QSTileState]. */
-class AirplaneModeMapper
+class AirplaneModeTileMapper
 @Inject
 constructor(@ShadeDisplayAware private val resources: Resources, val theme: Theme) :
     QSTileDataToStateMapper<AirplaneModeTileModel> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractor.kt
index 51cd501..1f113d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractor.kt
@@ -18,8 +18,8 @@
 
 import android.os.UserHandle
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
 import com.android.systemui.statusbar.policy.NextAlarmController
 import com.android.systemui.util.time.DateFormatUtil
@@ -33,12 +33,12 @@
 @Inject
 constructor(
     private val alarmController: NextAlarmController,
-    private val dateFormatUtil: DateFormatUtil
+    private val dateFormatUtil: DateFormatUtil,
 ) : QSTileDataInteractor<AlarmTileModel> {
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<AlarmTileModel> =
         ConflatedCallbackFlow.conflatedCallbackFlow {
             val alarmCallback =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
index 79fcd37..f6fcf25 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
@@ -18,19 +18,18 @@
 
 import android.content.Intent
 import android.provider.AlarmClock
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import javax.inject.Inject
 
 /** Handles alarm tile clicks. */
 class AlarmTileUserActionInteractor
 @Inject
-constructor(
-    private val inputHandler: QSTileIntentUserInputHandler,
-) : QSTileUserActionInteractor<AlarmTileModel> {
+constructor(private val inputHandler: QSTileIntentUserInputHandler) :
+    QSTileUserActionInteractor<AlarmTileModel> {
     override suspend fun handleInput(input: QSTileInput<AlarmTileModel>): Unit =
         with(input) {
             when (action) {
@@ -44,7 +43,7 @@
                     } else {
                         inputHandler.handle(
                             action.expandable,
-                            Intent(AlarmClock.ACTION_SHOW_ALARMS)
+                            Intent(AlarmClock.ACTION_SHOW_ALARMS),
                         )
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapper.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapper.kt
index d56d994..8a726ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapper.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.alarm.domain
+package com.android.systemui.qs.tiles.impl.alarm.ui.mapper
 
 import android.content.res.Resources
 import android.content.res.Resources.Theme
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.util.time.SystemClock
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileDataInteractor.kt
index 22bbbbb..ed55449 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileDataInteractor.kt
@@ -18,11 +18,10 @@
 
 import android.os.UserHandle
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
 import com.android.systemui.statusbar.policy.BatteryController
-import com.android.systemui.util.kotlin.combine
 import com.android.systemui.util.kotlin.getBatteryLevel
 import com.android.systemui.util.kotlin.isBatteryPowerSaveEnabled
 import com.android.systemui.util.kotlin.isDevicePluggedIn
@@ -44,7 +43,7 @@
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<BatterySaverTileModel> =
         combine(
             batteryController.isDevicePluggedIn().distinctUntilChanged().flowOn(bgCoroutineContext),
@@ -56,8 +55,8 @@
         ) {
             isPluggedIn: Boolean,
             isPowerSaverEnabled: Boolean,
-            _, // we are only interested in battery level change, not the actual level
-            ->
+            _ // we are only interested in battery level change, not the actual level
+             ->
             BatterySaverTileModel.Standard(isPluggedIn, isPowerSaverEnabled)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
index 3bbb9aa..2eb05a2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
@@ -18,11 +18,11 @@
 
 import android.content.Intent
 import android.provider.Settings
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.policy.BatteryController
 import javax.inject.Inject
 
@@ -31,7 +31,7 @@
 @Inject
 constructor(
     private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
-    private val batteryController: BatteryController
+    private val batteryController: BatteryController,
 ) : QSTileUserActionInteractor<BatterySaverTileModel> {
 
     override suspend fun handleInput(input: QSTileInput<BatterySaverTileModel>) =
@@ -45,7 +45,7 @@
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
-                        Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS)
+                        Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapper.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapper.kt
index 72759c5..7bc3d2b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapper.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.battery.ui
+package com.android.systemui.qs.tiles.impl.battery.ui.mapper
 
 import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt
index cd33d45..1d173fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt
@@ -18,8 +18,8 @@
 
 import android.os.UserHandle
 import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -29,15 +29,15 @@
 /** Observes color correction state changes providing the [ColorCorrectionTileModel]. */
 class ColorCorrectionTileDataInteractor
 @Inject
-constructor(
-    private val colorCorrectionRepository: ColorCorrectionRepository,
-) : QSTileDataInteractor<ColorCorrectionTileModel> {
+constructor(private val colorCorrectionRepository: ColorCorrectionRepository) :
+    QSTileDataInteractor<ColorCorrectionTileModel> {
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<ColorCorrectionTileModel> {
         return colorCorrectionRepository.isEnabled(user).map { ColorCorrectionTileModel(it) }
     }
+
     override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
index b774643..dea1f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
@@ -20,11 +20,11 @@
 import android.provider.Settings
 import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository
 import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import javax.inject.Inject
 
 /** Handles color correction tile clicks. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapper.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapper.kt
index e5a0fe8..93d81f0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapper.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.colorcorrection.domain
+package com.android.systemui.qs.tiles.impl.colorcorrection.ui.mapper
 
 import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/model/CustomTileDefaults.kt
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/model/CustomTileDefaults.kt
index dfeb65b..4015106 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/model/CustomTileDefaults.kt
@@ -14,15 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.data.entity
+package com.android.systemui.qs.tiles.impl.custom.data.model
 
 import android.graphics.drawable.Icon
 
 sealed interface CustomTileDefaults {
 
     data object Error : CustomTileDefaults
-    data class Result(
-        val icon: Icon,
-        val label: CharSequence,
-    ) : CustomTileDefaults
+
+    data class Result(val icon: Icon, val label: CharSequence) : CustomTileDefaults
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
index 32fb1d1..34894f5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
@@ -24,8 +24,8 @@
 import android.os.UserHandle
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
@@ -58,11 +58,7 @@
      *
      * Listen to [defaults] to get the loaded result
      */
-    fun requestNewDefaults(
-        user: UserHandle,
-        componentName: ComponentName,
-        force: Boolean = false,
-    )
+    fun requestNewDefaults(user: UserHandle, componentName: ComponentName, force: Boolean = false)
 }
 
 @QSTileScope
@@ -77,7 +73,7 @@
     private val defaultsRequests =
         MutableSharedFlow<DefaultsRequest>(
             replay = 1,
-            onBufferOverflow = BufferOverflow.DROP_OLDEST
+            onBufferOverflow = BufferOverflow.DROP_OLDEST,
         )
 
     private val defaults: SharedFlow<DefaultsResult> =
@@ -106,7 +102,7 @@
 
     private suspend fun loadDefaults(
         user: UserHandle,
-        componentName: ComponentName
+        componentName: ComponentName,
     ): CustomTileDefaults =
         withContext(backgroundDispatcher) {
             try {
@@ -120,16 +116,14 @@
 
                 CustomTileDefaults.Result(
                     Icon.createWithResource(componentName.packageName, iconRes),
-                    info.loadLabel(userContext.packageManager)
+                    info.loadLabel(userContext.packageManager),
                 )
             } catch (e: PackageManager.NameNotFoundException) {
                 CustomTileDefaults.Error
             }
         }
 
-    private fun ComponentName.getServiceInfo(
-        packageManager: PackageManager,
-    ): ServiceInfo {
+    private fun ComponentName.getServiceInfo(packageManager: PackageManager): ServiceInfo {
         val isSystemApp = packageManager.getApplicationInfo(packageName, 0).isSystemApp
         var flags =
             (PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
index cd4938f..da811a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
@@ -23,12 +23,12 @@
 import android.content.IntentFilter
 import android.os.UserHandle
 import androidx.annotation.GuardedBy
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
 import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
@@ -40,7 +40,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onCompletion
 import kotlinx.coroutines.flow.shareIn
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 interface CustomTilePackageUpdatesRepository {
 
@@ -79,7 +78,7 @@
         "RegisterReceiverViaContext",
     )
     private fun createPackageChangesFlowForUser(user: UserHandle): Flow<Unit> =
-        ConflatedCallbackFlow.conflatedCallbackFlow {
+        conflatedCallbackFlow {
                 val receiver =
                     object : BroadcastReceiver() {
                         override fun onReceive(context: Context?, intent: Intent?) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
index 0aaea8f..aabe7a9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
@@ -27,10 +27,10 @@
 import com.android.systemui.qs.external.PackageManagerAdapter
 import com.android.systemui.qs.external.TileServiceKey
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.impl.custom.commons.copy
-import com.android.systemui.qs.tiles.impl.custom.commons.setFrom
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.shared.model.copy
+import com.android.systemui.qs.tiles.impl.custom.shared.model.setFrom
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.channels.BufferOverflow
@@ -78,11 +78,7 @@
      * [user] differs from the cached one. [isPersistable] tile will be persisted to be possibly
      * loaded when the [restoreForTheUserIfNeeded].
      */
-    suspend fun updateWithTile(
-        user: UserHandle,
-        newTile: Tile,
-        isPersistable: Boolean,
-    )
+    suspend fun updateWithTile(user: UserHandle, newTile: Tile, isPersistable: Boolean)
 
     /**
      * Updates tile with the values from [defaults]. Overwrites the current cache when [user]
@@ -114,11 +110,7 @@
         if (isPersistable && getCurrentTileWithUser()?.user != user) {
             withContext(backgroundContext) {
                 customTileStatePersister.readState(user.getKey())?.let {
-                    updateWithTile(
-                        user,
-                        it,
-                        true,
-                    )
+                    updateWithTile(user, it, true)
                 }
             }
         }
@@ -137,11 +129,8 @@
         }
     }
 
-    override suspend fun updateWithTile(
-        user: UserHandle,
-        newTile: Tile,
-        isPersistable: Boolean,
-    ) = updateTile(user, isPersistable) { setFrom(newTile) }
+    override suspend fun updateWithTile(user: UserHandle, newTile: Tile, isPersistable: Boolean) =
+        updateTile(user, isPersistable) { setFrom(newTile) }
 
     override suspend fun updateWithDefaults(
         user: UserHandle,
@@ -182,7 +171,7 @@
                     packageManagerAdapter.getServiceInfo(
                         tileSpec.componentName,
                         META_DATA_QUERY_FLAGS,
-                        getCurrentTileWithUser()?.user?.identifier ?: UserHandle.USER_CURRENT
+                        getCurrentTileWithUser()?.user?.identifier ?: UserHandle.USER_CURRENT,
                     )
                 info?.metaData?.getBoolean(TileService.META_DATA_TOGGLEABLE_TILE, false) == true
             } catch (e: RemoteException) {
@@ -193,7 +182,7 @@
     private suspend fun updateTile(
         user: UserHandle,
         isPersistable: Boolean,
-        update: Tile.() -> Unit
+        update: Tile.() -> Unit,
     ): Unit =
         tileUpdateMutex.withLock {
             val currentTileWithUser = getCurrentTileWithUser()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
index 0b0f2fe..e64675f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
@@ -20,14 +20,14 @@
 import android.service.quicksettings.Tile
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
index 6f1cb3c..c587e3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
@@ -18,11 +18,12 @@
 
 import android.os.UserHandle
 import android.service.quicksettings.Tile
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
@@ -34,7 +35,6 @@
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
-import com.android.app.tracing.coroutines.launchTraced as launch
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 
@@ -100,7 +100,7 @@
             launchUpdates(user)
             customTileRepository.restoreForTheUserIfNeeded(
                 user,
-                customTileRepository.isTileActive()
+                customTileRepository.isTileActive(),
             )
             // Suspend to make sure it gets the tile from one of the sources: restoration, defaults,
             // or
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
index c0fc93f..56fd325 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
@@ -31,8 +31,8 @@
 import com.android.systemui.qs.external.TileServiceManager
 import com.android.systemui.qs.external.TileServices
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
 import com.android.systemui.user.data.repository.UserRepository
 import dagger.Lazy
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
index 1153b5c..df2759a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
@@ -34,13 +34,13 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
 import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.shade.ShadeDisplayAware
 import java.util.concurrent.atomic.AtomicReference
@@ -80,10 +80,7 @@
             qsTileLogger.logCustomTileUserActionDelivered(tileSpec)
         }
 
-    private suspend fun click(
-        expandable: Expandable?,
-        activityLaunchForClick: PendingIntent?,
-    ) {
+    private suspend fun click(expandable: Expandable?, activityLaunchForClick: PendingIntent?) {
         grantToken()
         try {
             // Bind active tile to deliver user action
@@ -133,7 +130,7 @@
                         token,
                         WindowManager.LayoutParams.TYPE_QS_DIALOG,
                         displayTracker.defaultDisplayId,
-                        null /* options */
+                        null, /* options */
                     )
                 } catch (e: RemoteException) {
                     qsTileLogger.logError(tileSpec, "Failed to grant a window token", e)
@@ -147,7 +144,7 @@
         user: UserHandle,
         expandable: Expandable?,
         componentName: ComponentName,
-        state: Int
+        state: Int,
     ) {
         val resolvedIntent: Intent? =
             resolveIntent(
@@ -166,7 +163,7 @@
                 Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                     .setData(
                         Uri.fromParts(IntentFilter.SCHEME_PACKAGE, componentName.packageName, null)
-                    )
+                    ),
             )
         } else {
             qsTileIntentUserInputHandler.handle(expandable, resolvedIntent)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/model/CustomTileDataModel.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/model/CustomTileDataModel.kt
index 5b6ff1e..4393e9e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/model/CustomTileDataModel.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.domain.entity
+package com.android.systemui.qs.tiles.impl.custom.domain.model
 
 import android.content.ComponentName
 import android.graphics.drawable.Icon
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/QSTileConfigModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/QSTileConfigModule.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/QSTileConfigModule.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/QSTileConfigModule.kt
index 558fb64..c950888 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/QSTileConfigModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/QSTileConfigModule.kt
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di
+package com.android.systemui.qs.tiles.impl.custom.shared.model
 
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
 import dagger.Module
 import dagger.Provides
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/TileExt.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/TileExt.kt
index 869f6f32..022894e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/TileExt.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.commons
+package com.android.systemui.qs.tiles.impl.custom.shared.model
 
 import android.service.quicksettings.Tile
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapper.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapper.kt
index c446865..dfaaea1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapper.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.domain
+package com.android.systemui.qs.tiles.impl.custom.ui.mapper
 
 import android.annotation.SuppressLint
 import android.app.IUriGrantsManager
@@ -26,10 +26,10 @@
 import android.widget.Switch
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 
@@ -38,9 +38,8 @@
 @Inject
 constructor(
     @ShadeDisplayAware private val context: Context,
-    private val uriGrantsManager: IUriGrantsManager
-) :
-    QSTileDataToStateMapper<CustomTileDataModel> {
+    private val uriGrantsManager: IUriGrantsManager,
+) : QSTileDataToStateMapper<CustomTileDataModel> {
 
     override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
         val userContext =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileComponent.kt
similarity index 77%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileComponent.kt
index 7b099c2..4f23033 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileComponent.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di
+package com.android.systemui.qs.tiles.impl.custom.ui.model
 
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.base.ui.model.QSTileComponent
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
 import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileInteractor
 import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileServiceInteractor
-import com.android.systemui.qs.tiles.impl.di.QSTileComponent
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.shared.model.QSTileConfigModule
 import dagger.Subcomponent
 
 @QSTileScope
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileModule.kt
similarity index 79%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileModule.kt
index 196fa12..ee7908a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileModule.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,23 +14,23 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di
+package com.android.systemui.qs.tiles.impl.custom.ui.model
 
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileCoroutineScopeFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileCoroutineScopeFactory
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepositoryImpl
-import com.android.systemui.qs.tiles.impl.custom.domain.CustomTileMapper
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
 import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileDataInteractor
 import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.ui.mapper.CustomTileMapper
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt
index 1544804..9720d15 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt
@@ -17,11 +17,11 @@
 package com.android.systemui.qs.tiles.impl.flashlight.domain.interactor
 
 import android.os.UserHandle
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
 import com.android.systemui.statusbar.policy.FlashlightController
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
@@ -30,22 +30,23 @@
 /** Observes flashlight state changes providing the [FlashlightTileModel]. */
 class FlashlightTileDataInteractor
 @Inject
-constructor(
-    private val flashlightController: FlashlightController,
-) : QSTileDataInteractor<FlashlightTileModel> {
+constructor(private val flashlightController: FlashlightController) :
+    QSTileDataInteractor<FlashlightTileModel> {
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<FlashlightTileModel> = conflatedCallbackFlow {
         val callback =
             object : FlashlightController.FlashlightListener {
                 override fun onFlashlightChanged(enabled: Boolean) {
                     trySend(FlashlightTileModel.FlashlightAvailable(enabled))
                 }
+
                 override fun onFlashlightError() {
                     trySend(FlashlightTileModel.FlashlightAvailable(false))
                 }
+
                 override fun onFlashlightAvailabilityChanged(available: Boolean) {
                     trySend(
                         if (available)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
index 13afc15..8a76b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
@@ -17,19 +17,18 @@
 package com.android.systemui.qs.tiles.impl.flashlight.domain.interactor
 
 import android.app.ActivityManager
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.policy.FlashlightController
 import javax.inject.Inject
 
 /** Handles flashlight tile clicks. */
 class FlashlightTileUserActionInteractor
 @Inject
-constructor(
-    private val flashlightController: FlashlightController,
-) : QSTileUserActionInteractor<FlashlightTileModel> {
+constructor(private val flashlightController: FlashlightController) :
+    QSTileUserActionInteractor<FlashlightTileModel> {
 
     override suspend fun handleInput(input: QSTileInput<FlashlightTileModel>) =
         with(input) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapper.kt
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapper.kt
index 32ccba6..0be4a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapper.kt
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.flashlight.domain
+package com.android.systemui.qs.tiles.impl.flashlight.ui.mapper
 
 import android.content.res.Resources
 import android.content.res.Resources.Theme
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt
index 745e6a3..3464741 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor
 
 import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -29,7 +29,7 @@
     QSTileDataInteractor<FontScalingTileModel> {
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<FontScalingTileModel> = flowOf(FontScalingTileModel)
 
     override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
index 0ebb51e..65db0f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
@@ -25,11 +25,11 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapper.kt
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapper.kt
index c571b13..659e1fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapper.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.fontscaling.domain
+package com.android.systemui.qs.tiles.impl.fontscaling.ui.mapper
 
 import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
index 33b7feb..50ea30d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
@@ -19,8 +19,8 @@
 import android.os.UserHandle
 import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
 import com.android.systemui.statusbar.policy.BluetoothController
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
index 5e7172e..049d061 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
@@ -21,11 +21,12 @@
 import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
 import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.withContext
@@ -37,6 +38,7 @@
     @Main private val mainContext: CoroutineContext,
     private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
     private val hearingDevicesDialogManager: HearingDevicesDialogManager,
+    private val settingsPackageRepository: QSSettingsPackageRepository,
 ) : QSTileUserActionInteractor<HearingDevicesTileModel> {
 
     override suspend fun handleInput(input: QSTileInput<HearingDevicesTileModel>) =
@@ -53,7 +55,8 @@
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
-                        Intent(Settings.ACTION_HEARING_DEVICES_SETTINGS),
+                        Intent(Settings.ACTION_HEARING_DEVICES_SETTINGS)
+                            .setPackage(settingsPackageRepository.getSettingsPackageName()),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapper.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapper.kt
index 12f7149..d89c535 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapper.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.hearingdevices.domain
+package com.android.systemui.qs.tiles.impl.hearingdevices.ui.mapper
 
 import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
index 871c051..786ac47 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
@@ -24,8 +24,8 @@
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
index 0431e36..7fd282c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
@@ -19,12 +19,12 @@
 import android.content.Intent
 import android.provider.Settings
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.connectivity.AccessPointController
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapper.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapper.kt
index 8d58805..a96ad38 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapper.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.internet.domain
+package com.android.systemui.qs.tiles.impl.internet.ui.mapper
 
 import android.content.Context
 import android.content.res.Resources
@@ -25,10 +25,10 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text.Companion.loadText
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt
index 7f3dd3e..fea9e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt
@@ -18,8 +18,8 @@
 
 import android.os.UserHandle
 import com.android.systemui.accessibility.data.repository.ColorInversionRepository
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -29,15 +29,15 @@
 /** Observes color inversion state changes providing the [ColorInversionTileModel]. */
 class ColorInversionTileDataInteractor
 @Inject
-constructor(
-    private val colorInversionRepository: ColorInversionRepository,
-) : QSTileDataInteractor<ColorInversionTileModel> {
+constructor(private val colorInversionRepository: ColorInversionRepository) :
+    QSTileDataInteractor<ColorInversionTileModel> {
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<ColorInversionTileModel> {
         return colorInversionRepository.isEnabled(user).map { ColorInversionTileModel(it) }
     }
+
     override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
index f783497..12530bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
@@ -20,11 +20,11 @@
 import android.provider.Settings
 import com.android.systemui.accessibility.data.repository.ColorInversionRepository
 import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import javax.inject.Inject
 
 /** Handles color inversion tile clicks. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapper.kt
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapper.kt
index 05590e8..868abc0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapper.kt
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.inversion.domain
+package com.android.systemui.qs.tiles.impl.inversion.ui.mapper
 
 import android.content.res.Resources
 import android.content.res.Resources.Theme
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/data/model/IssueRecordingModel.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/data/model/IssueRecordingModel.kt
index 260729b..704102b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/data/model/IssueRecordingModel.kt
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.data.model
 
 @JvmInline value class IssueRecordingModel(val isRecording: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractor.kt
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractor.kt
index 09a6ce8..95fe191 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractor.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,13 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.domain.interactor
 
 import android.os.UserHandle
 import com.android.systemui.Flags
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
 import com.android.systemui.recordissue.IssueRecordingState
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractor.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractor.kt
index fceee5a..7182a37 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractor.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.domain.interactor
 
 import android.app.AlertDialog
 import android.app.BroadcastOptions
@@ -30,9 +30,10 @@
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
 import com.android.systemui.qs.tiles.DELAY_MS
 import com.android.systemui.qs.tiles.INTERVAL_MS
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
 import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent
 import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent
 import com.android.systemui.recordissue.IssueRecordingState
@@ -47,8 +48,6 @@
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.withContext
 
-private const val TAG = "IssueRecordingActionInteractor"
-
 class IssueRecordingUserActionInteractor
 @Inject
 constructor(
@@ -128,4 +127,8 @@
             action,
             PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
         )
+
+    companion object {
+        private const val TAG = "IssueRecordingUserActionInteractor"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapper.kt
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapper.kt
index afb137e..bd51bbe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapper.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.ui.mapper
 
 import android.content.res.Resources
 import android.content.res.Resources.Theme
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt
index bd2f2c9..052d062 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.qs.tiles.impl.location.domain.interactor
 
 import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
 import com.android.systemui.statusbar.policy.LocationController
 import com.android.systemui.util.kotlin.isLocationEnabledFlow
@@ -30,13 +30,12 @@
 /** Observes location state changes providing the [LocationTileModel]. */
 class LocationTileDataInteractor
 @Inject
-constructor(
-    private val locationController: LocationController,
-) : QSTileDataInteractor<LocationTileModel> {
+constructor(private val locationController: LocationController) :
+    QSTileDataInteractor<LocationTileModel> {
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<LocationTileModel> =
         locationController.isLocationEnabledFlow().map { LocationTileModel(it) }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
index d46bcfc..f5d1400 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
@@ -18,21 +18,21 @@
 
 import android.content.Intent
 import android.provider.Settings
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.policy.LocationController
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 import kotlinx.coroutines.withContext
 
 /** Handles location tile clicks. */
@@ -68,7 +68,7 @@
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
-                        Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
+                        Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapper.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapper.kt
index ced5a4f..d2c9116 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapper.kt
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.location.domain
+package com.android.systemui.qs.tiles.impl.location.ui.mapper
 
 import android.content.res.Resources
 import android.content.res.Resources.Theme
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractor.kt
new file mode 100644
index 0000000..c3e7bea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractor.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.modes.domain.interactor
+
+import android.content.Context
+import android.os.UserHandle
+import android.text.TextUtils
+import com.android.app.tracing.coroutines.flow.flowName
+import com.android.settingslib.notification.modes.ZenMode
+import com.android.settingslib.notification.modes.ZenModeDescriptions
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.modes.shared.ModesUi
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
+import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+class ModesDndTileDataInteractor
+@Inject
+constructor(
+    @ShadeDisplayAware val context: Context,
+    val zenModeInteractor: ZenModeInteractor,
+    @Background val bgDispatcher: CoroutineDispatcher,
+) : QSTileDataInteractor<ModesDndTileModel> {
+
+    private val zenModeDescriptions = ZenModeDescriptions(context)
+
+    override fun tileData(
+        user: UserHandle,
+        triggers: Flow<DataUpdateTrigger>,
+    ): Flow<ModesDndTileModel> = tileData()
+
+    /**
+     * An adapted version of the base class' [tileData] method for use in an old-style tile.
+     *
+     * TODO(b/299909989): Remove after the transition.
+     */
+    fun tileData() =
+        zenModeInteractor.dndMode
+            .filterNotNull()
+            .map { dndMode -> buildTileData(dndMode) }
+            .flowName("tileData")
+            .flowOn(bgDispatcher)
+            .distinctUntilChanged()
+
+    fun getCurrentTileModel() = buildTileData(zenModeInteractor.getDndMode())
+
+    private fun buildTileData(dndMode: ZenMode): ModesDndTileModel {
+        return ModesDndTileModel(
+            isActivated = dndMode.isActive,
+            extraStatus = TextUtils.nullIfEmpty(zenModeDescriptions.getTriggerDescription(dndMode)),
+        )
+    }
+
+    override fun availability(user: UserHandle): Flow<Boolean> =
+        flowOf(ModesUi.isEnabled && android.app.Flags.modesUiDndTile())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractor.kt
new file mode 100644
index 0000000..012ae03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractor.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.modes.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS
+import android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID
+import android.util.Log
+import com.android.systemui.animation.Expandable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogEventLogger
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+class ModesDndTileUserActionInteractor
+@Inject
+constructor(
+    @Main private val mainContext: CoroutineContext,
+    private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
+    // TODO(b/353896370): The domain layer should not have to depend on the UI layer.
+    private val dialogDelegate: ModesDialogDelegate,
+    private val zenModeInteractor: ZenModeInteractor,
+    private val dialogEventLogger: ModesDialogEventLogger,
+    private val settingsPackageRepository: QSSettingsPackageRepository,
+) : QSTileUserActionInteractor<ModesDndTileModel> {
+
+    override suspend fun handleInput(input: QSTileInput<ModesDndTileModel>) {
+        with(input) {
+            when (action) {
+                is QSTileUserAction.Click,
+                is QSTileUserAction.ToggleClick -> {
+                    handleClick()
+                }
+                is QSTileUserAction.LongClick -> {
+                    handleLongClick(action.expandable)
+                }
+            }
+        }
+    }
+
+    suspend fun handleClick() {
+        val dnd = zenModeInteractor.dndMode.value
+        if (dnd == null) {
+            Log.wtf(TAG, "No DND!?")
+            return
+        }
+
+        if (!dnd.isActive) {
+            if (zenModeInteractor.shouldAskForZenDuration(dnd)) {
+                dialogEventLogger.logOpenDurationDialog(dnd)
+                withContext(mainContext) {
+                    // NOTE: The dialog handles turning on the mode itself.
+                    val dialog = dialogDelegate.makeDndDurationDialog()
+                    dialog.show()
+                }
+            } else {
+                dialogEventLogger.logModeOn(dnd)
+                zenModeInteractor.activateMode(dnd)
+            }
+        } else {
+            dialogEventLogger.logModeOff(dnd)
+            zenModeInteractor.deactivateMode(dnd)
+        }
+    }
+
+    private fun handleLongClick(expandable: Expandable?) {
+        val intent = getSettingsIntent()
+        if (intent != null) {
+            qsTileIntentUserInputHandler.handle(expandable, intent)
+        }
+    }
+
+    fun getSettingsIntent(): Intent? {
+        val dnd = zenModeInteractor.dndMode.value
+        if (dnd == null) {
+            Log.wtf(TAG, "No DND!?")
+            return null
+        }
+
+        return Intent(ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
+            .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, dnd.id)
+            .setPackage(settingsPackageRepository.getSettingsPackageName())
+    }
+
+    companion object {
+        const val TAG = "ModesDndTileUserActionInteractor"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 479f618..e97985c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -25,8 +25,8 @@
 import com.android.systemui.modes.shared.ModesUi
 import com.android.systemui.modes.shared.ModesUiIcons
 import com.android.systemui.qs.tiles.ModesTile
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
index ab1326a..5240a18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
@@ -23,11 +23,11 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.qs.flags.QSComposeFragment
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
 import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
 import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogEventLogger
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesDndTileModel.kt
similarity index 73%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesDndTileModel.kt
index 5d14631..e13cc65 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesDndTileModel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package com.android.systemui.qs.tiles.impl.modes.domain.model
 
-parcelable PictureProfileHandle;
+data class ModesDndTileModel(val isActivated: Boolean, val extraStatus: String?)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapper.kt
new file mode 100644
index 0000000..632c572
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapper.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.modes.ui
+
+import android.content.res.Resources
+import android.widget.Switch
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
+import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
+import javax.inject.Inject
+
+class ModesDndTileMapper
+@Inject
+constructor(@ShadeDisplayAware private val resources: Resources, val theme: Resources.Theme) :
+    QSTileDataToStateMapper<ModesDndTileModel> {
+    override fun map(config: QSTileConfig, data: ModesDndTileModel): QSTileState =
+        QSTileState.build(resources, theme, config.uiConfig) {
+            val iconResource =
+                if (data.isActivated) R.drawable.qs_dnd_icon_on else R.drawable.qs_dnd_icon_off
+            icon =
+                Icon.Loaded(
+                    resources.getDrawable(iconResource, theme),
+                    res = iconResource,
+                    contentDescription = null,
+                )
+
+            activationState =
+                if (data.isActivated) {
+                    QSTileState.ActivationState.ACTIVE
+                } else {
+                    QSTileState.ActivationState.INACTIVE
+                }
+            label = resources.getString(R.string.quick_settings_dnd_label)
+            secondaryLabel = data.extraStatus
+            contentDescription = label
+            stateDescription = data.extraStatus
+            supportedActions =
+                setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+            expandedAccessibilityClass = Switch::class
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapper.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapper.kt
index 99ae3b8..4668350 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapper.kt
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.modes.ui
+package com.android.systemui.qs.tiles.impl.modes.ui.mapper
 
 import android.content.res.Resources
 import android.icu.text.MessageFormat
 import android.widget.Button
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import java.util.Locale
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt
index e8e43e8..f6f267f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt
@@ -20,8 +20,8 @@
 import android.hardware.display.ColorDisplayManager
 import android.os.UserHandle
 import com.android.systemui.accessibility.data.repository.NightDisplayRepository
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.util.time.DateFormatUtil
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt
index 7076a8f..4173942 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt
@@ -22,12 +22,12 @@
 import com.android.systemui.accessibility.data.repository.NightDisplayRepository
 import com.android.systemui.accessibility.qs.QSAccessibilityModule
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import javax.inject.Inject
 
 /** Handles night display tile clicks. */
@@ -52,7 +52,7 @@
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
-                        Intent(Settings.ACTION_NIGHT_DISPLAY_SETTINGS)
+                        Intent(Settings.ACTION_NIGHT_DISPLAY_SETTINGS),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapper.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapper.kt
index 16b3628..3e47875 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapper.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.night.ui
+package com.android.systemui.qs.tiles.impl.night.ui.mapper
 
 import android.content.res.Resources
 import android.service.quicksettings.Tile
@@ -23,11 +23,11 @@
 import com.android.systemui.accessibility.qs.QSAccessibilityModule
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import java.time.DateTimeException
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt
index a501b85..157da8a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt
@@ -19,8 +19,8 @@
 import android.os.UserHandle
 import com.android.systemui.Flags
 import com.android.systemui.notetask.NoteTaskEnabledKey
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt
index df01d99..527f9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt
@@ -20,15 +20,16 @@
 import com.android.systemui.notetask.NoteTaskController
 import com.android.systemui.notetask.NoteTaskEntryPoint
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import javax.inject.Inject
 
 class NotesTileUserActionInteractor
-@Inject constructor(
+@Inject
+constructor(
     private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
     private val panelInteractor: PanelInteractor,
     private val noteTaskController: NoteTaskController,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapper.kt
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapper.kt
index ecdd711..36b28df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapper.kt
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.notes.domain
+package com.android.systemui.qs.tiles.impl.notes.ui.mapper
 
 import android.content.res.Resources
 import android.widget.Button
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileDataInteractor.kt
index 8c0fd2c..39f3976 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileDataInteractor.kt
@@ -18,8 +18,8 @@
 
 import android.os.UserHandle
 import com.android.systemui.accessibility.data.repository.OneHandedModeRepository
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
 import com.android.wm.shell.onehanded.OneHanded
 import javax.inject.Inject
@@ -30,16 +30,16 @@
 /** Observes one handed mode state changes providing the [OneHandedModeTileModel]. */
 class OneHandedModeTileDataInteractor
 @Inject
-constructor(
-    private val oneHandedModeRepository: OneHandedModeRepository,
-) : QSTileDataInteractor<OneHandedModeTileModel> {
+constructor(private val oneHandedModeRepository: OneHandedModeRepository) :
+    QSTileDataInteractor<OneHandedModeTileModel> {
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<OneHandedModeTileModel> {
         return oneHandedModeRepository.isEnabled(user).map { OneHandedModeTileModel(it) }
     }
+
     override fun availability(user: UserHandle): Flow<Boolean> =
         flowOf(OneHanded.sIsSupportOneHandedMode)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt
index 0a0f0a6..88f9d84 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt
@@ -19,11 +19,11 @@
 import android.content.Intent
 import android.provider.Settings
 import com.android.systemui.accessibility.data.repository.OneHandedModeRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import javax.inject.Inject
 
 /** Handles one handed mode tile clicks. */
@@ -38,15 +38,12 @@
         with(input) {
             when (action) {
                 is QSTileUserAction.Click -> {
-                    oneHandedModeRepository.setIsEnabled(
-                        !data.isEnabled,
-                        user,
-                    )
+                    oneHandedModeRepository.setIsEnabled(!data.isEnabled, user)
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
-                        Intent(Settings.ACTION_ONE_HANDED_SETTINGS)
+                        Intent(Settings.ACTION_ONE_HANDED_SETTINGS),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapper.kt
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapper.kt
index 5b3ea93..ace36ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapper.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.onehanded.ui
+package com.android.systemui.qs.tiles.impl.onehanded.ui.mapper
 
 import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
index 233e913..0b4752b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
@@ -21,8 +21,8 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
@@ -44,7 +44,7 @@
 ) : QSTileDataInteractor<QRCodeScannerTileModel> {
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<QRCodeScannerTileModel> =
         conflatedCallbackFlow {
                 qrController.registerQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
index bb5df02..eb47d0b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
@@ -16,19 +16,18 @@
 
 package com.android.systemui.qs.tiles.impl.qr.domain.interactor
 
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import javax.inject.Inject
 
 /** Handles qr tile clicks. */
 class QRCodeScannerTileUserActionInteractor
 @Inject
-constructor(
-    private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
-) : QSTileUserActionInteractor<QRCodeScannerTileModel> {
+constructor(private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler) :
+    QSTileUserActionInteractor<QRCodeScannerTileModel> {
 
     override suspend fun handleInput(input: QSTileInput<QRCodeScannerTileModel>): Unit =
         with(input) {
@@ -39,7 +38,7 @@
                             qsTileIntentUserActionHandler.handle(
                                 action.expandable,
                                 data.intent,
-                                true
+                                true,
                             )
                         is QRCodeScannerTileModel.TemporarilyUnavailable -> {} // no-op
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapper.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapper.kt
index 21e92d3..120961b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapper.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.qr.ui
+package com.android.systemui.qs.tiles.impl.qr.ui.mapper
 
 import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/model/QRCodeScannerModule.kt
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/model/QRCodeScannerModule.kt
index ef1f834..c3c6be3 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/model/QRCodeScannerModule.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qrcodescanner.dagger
+package com.android.systemui.qs.tiles.impl.qr.ui.model
 
 import com.android.systemui.Flags
 import com.android.systemui.qs.QsEventLogger
@@ -22,16 +22,16 @@
 import com.android.systemui.qs.shared.model.TileCategory
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.QRCodeScannerTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
 import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor
 import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
-import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.impl.qr.ui.mapper.QRCodeScannerTileMapper
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
@@ -81,7 +81,7 @@
             factory: QSTileViewModelFactory.Static<QRCodeScannerTileModel>,
             mapper: QRCodeScannerTileMapper,
             stateInteractor: QRCodeScannerTileDataInteractor,
-            userActionInteractor: QRCodeScannerTileUserActionInteractor
+            userActionInteractor: QRCodeScannerTileUserActionInteractor,
         ): QSTileViewModel =
             if (Flags.qsNewTilesFuture())
                 factory.create(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
index 536c5f1..46ed2ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
@@ -20,8 +20,8 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.qs.ReduceBrightColorsController
 import com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
 import com.android.systemui.util.kotlin.isEnabled
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
index eff5f8f..0bc3dec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
@@ -21,11 +21,11 @@
 import android.provider.Settings
 import com.android.systemui.accessibility.extradim.ExtraDimDialogManager
 import com.android.systemui.qs.ReduceBrightColorsController
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapper.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapper.kt
index 66759cd..2e74399 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapper.kt
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.reducebrightness.ui
+package com.android.systemui.qs.tiles.impl.reducebrightness.ui.mapper
 
 import android.content.res.Resources
 import android.service.quicksettings.Tile
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt
index 7f17a3a..f89154d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt
@@ -22,8 +22,8 @@
 import android.os.UserHandle
 import com.android.systemui.camera.data.repository.CameraAutoRotateRepository
 import com.android.systemui.camera.data.repository.CameraSensorPrivacyRepository
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.policy.BatteryController
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
index 65712c7..71aaa93 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
@@ -18,11 +18,11 @@
 
 import android.content.Intent
 import android.provider.Settings
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.policy.RotationLockController
 import javax.inject.Inject
 
@@ -43,7 +43,7 @@
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
-                        Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS)
+                        Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
index 000c702..d1db499 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
@@ -19,10 +19,10 @@
 import android.content.res.Resources
 import android.hardware.devicestate.DeviceStateManager
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.policy.DevicePostureController
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt
index 91e049b..1c19444 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt
@@ -18,8 +18,8 @@
 
 import android.os.UserHandle
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
 import com.android.systemui.statusbar.policy.DataSaverController
 import javax.inject.Inject
@@ -30,13 +30,12 @@
 /** Observes data saver state changes providing the [DataSaverTileModel]. */
 class DataSaverTileDataInteractor
 @Inject
-constructor(
-    private val dataSaverController: DataSaverController,
-) : QSTileDataInteractor<DataSaverTileModel> {
+constructor(private val dataSaverController: DataSaverController) :
+    QSTileDataInteractor<DataSaverTileModel> {
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<DataSaverTileModel> =
         ConflatedCallbackFlow.conflatedCallbackFlow {
             val initialValue = dataSaverController.isDataSaverEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
index 63a9f59..16fb9f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
@@ -24,12 +24,12 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.saver.domain.DataSaverDialogDelegate
 import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapper.kt
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapper.kt
index 1d5cf29..e15c098 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapper.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.saver.domain
+package com.android.systemui.qs.tiles.impl.saver.ui.mapper
 
 import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt
index 597825c..7e2f1bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
 
 import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
 import javax.inject.Inject
@@ -28,13 +28,12 @@
 /** Observes screen record state changes providing the [ScreenRecordModel]. */
 class ScreenRecordTileDataInteractor
 @Inject
-constructor(
-    private val screenRecordRepository: ScreenRecordRepository,
-) : QSTileDataInteractor<ScreenRecordModel> {
+constructor(private val screenRecordRepository: ScreenRecordRepository) :
+    QSTileDataInteractor<ScreenRecordModel> {
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<ScreenRecordModel> = screenRecordRepository.screenRecordState
 
     override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
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..b7dc632 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
@@ -28,9 +28,9 @@
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.screenrecord.RecordingController
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/mapper/ScreenRecordTileMapper.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/mapper/ScreenRecordTileMapper.kt
index 0a61e3c..b0c9d12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/mapper/ScreenRecordTileMapper.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.screenrecord.domain.ui
+package com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.mapper
 
 import android.content.res.Resources
 import android.text.TextUtils
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.shade.ShadeDisplayAware
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/SensorPrivacyToggleTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractor.kt
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/SensorPrivacyToggleTileDataInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractor.kt
index 7117629..f033e6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/SensorPrivacyToggleTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractor.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.sensorprivacy
+package com.android.systemui.qs.tiles.impl.sensorprivacy.domain.interactor
 
 import android.hardware.SensorPrivacyManager.Sensors.CAMERA
 import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE
@@ -22,12 +22,12 @@
 import android.os.UserHandle
 import android.provider.DeviceConfig
 import android.util.Log
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -55,7 +55,7 @@
 
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<SensorPrivacyToggleTileModel> =
         conflatedCallbackFlow {
                 val callback =
@@ -85,14 +85,14 @@
                 return@withContext DeviceConfig.getBoolean(
                     DeviceConfig.NAMESPACE_PRIVACY,
                     deviceConfigName,
-                    true
+                    true,
                 )
             } catch (exception: IllegalArgumentException) {
                 Log.w(
                     TAG,
                     "isDeviceConfigSet for sensorId $sensorId: " +
                         "Defaulting to true due to exception. ",
-                    exception
+                    exception,
                 )
                 return@withContext true
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractor.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractor.kt
index d7f64d1..decf432 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractor.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.sensorprivacy.domain
+package com.android.systemui.qs.tiles.impl.sensorprivacy.domain.interactor
 
 import android.content.Intent
 import android.hardware.SensorPrivacyManager.Sensors.Sensor
@@ -23,11 +23,11 @@
 import android.safetycenter.SafetyCenterManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapper.kt
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapper.kt
index f54f46c..e9d5f52 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapper.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.sensorprivacy.ui
+package com.android.systemui.qs.tiles.impl.sensorprivacy.ui.mapper
 
 import android.content.res.Resources
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model.SensorPrivacyTileResources
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import dagger.assisted.Assisted
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyTileResources.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/model/SensorPrivacyTileResources.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyTileResources.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/model/SensorPrivacyTileResources.kt
index 2a9fd07..e65ae00 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyTileResources.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/model/SensorPrivacyTileResources.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,12 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.sensorprivacy.ui
+package com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model
 
 import com.android.systemui.res.R
 
 sealed interface SensorPrivacyTileResources {
     fun getIconRes(isBlocked: Boolean): Int
+
     fun getTileLabelRes(): Int
 
     data object CameraPrivacyTileResources : SensorPrivacyTileResources {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt
index 925b913..17c2f83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt
@@ -21,9 +21,9 @@
 import android.content.res.Configuration
 import android.os.UserHandle
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
index 8897828..8af5fed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
@@ -20,11 +20,11 @@
 import android.content.Intent
 import android.provider.Settings
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.withContext
@@ -51,7 +51,7 @@
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
-                        Intent(Settings.ACTION_DARK_THEME_SETTINGS)
+                        Intent(Settings.ACTION_DARK_THEME_SETTINGS),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/model/UiModeNightTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/model/UiModeNightTileModel.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/model/UiModeNightTileModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/model/UiModeNightTileModel.kt
index 4fa1306..edb899b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/model/UiModeNightTileModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/model/UiModeNightTileModel.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.uimodenight.domain.model
+package com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model
 
 import java.time.LocalTime
 
@@ -31,5 +31,5 @@
     val nightModeCustomType: Int,
     val is24HourFormat: Boolean,
     val customNightModeEnd: LocalTime,
-    val customNightModeStart: LocalTime
+    val customNightModeStart: LocalTime,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapper.kt
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapper.kt
index 5933d65..91c0451 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapper.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.uimodenight.domain
+package com.android.systemui.qs.tiles.impl.uimodenight.ui.mapper
 
 import android.app.UiModeManager
 import android.content.res.Resources
 import android.content.res.Resources.Theme
 import android.text.TextUtils
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import java.time.LocalTime
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractor.kt
index a2a9e87a..eabeb5d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractor.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.qs.tiles.impl.work.domain.interactor
 
 import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
 import com.android.systemui.statusbar.phone.ManagedProfileController
 import com.android.systemui.util.kotlin.hasActiveWorkProfile
@@ -29,12 +29,11 @@
 /** Observes data saver state changes providing the [WorkModeTileModel]. */
 class WorkModeTileDataInteractor
 @Inject
-constructor(
-    private val profileController: ManagedProfileController,
-) : QSTileDataInteractor<WorkModeTileModel> {
+constructor(private val profileController: ManagedProfileController) :
+    QSTileDataInteractor<WorkModeTileModel> {
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<WorkModeTileModel> =
         profileController.hasActiveWorkProfile.map { hasActiveWorkProfile: Boolean ->
             if (hasActiveWorkProfile) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
index 45ae09e..42cc996 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
@@ -18,11 +18,11 @@
 
 import android.content.Intent
 import android.provider.Settings
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.phone.ManagedProfileController
 import javax.inject.Inject
 
@@ -45,7 +45,7 @@
                     if (data is WorkModeTileModel.HasActiveProfile) {
                         qsTileIntentUserActionHandler.handle(
                             action.expandable,
-                            Intent(Settings.ACTION_MANAGED_PROFILE_SETTINGS)
+                            Intent(Settings.ACTION_MANAGED_PROFILE_SETTINGS),
                         )
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapper.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapper.kt
index 5b462ba..a67c76d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapper.kt
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.work.ui
+package com.android.systemui.qs.tiles.impl.work.ui.mapper
 
 import android.app.admin.DevicePolicyManager
 import android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_WORK_PROFILE_LABEL
 import android.content.res.Resources
 import android.service.quicksettings.Tile
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeDisplayAware
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
index bc15bbb..263ef09e 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
@@ -20,6 +20,8 @@
 import android.hardware.devicestate.DeviceStateManager
 import android.hardware.devicestate.feature.flags.Flags
 import androidx.annotation.VisibleForTesting
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -28,8 +30,11 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
 
 /**
  * Provides a {@link com.android.systemui.statusbar.phone.SystemUIDialog} to be shown on the inner
@@ -46,6 +51,7 @@
     private val rearDisplayStateInteractor: RearDisplayStateInteractor,
     private val rearDisplayInnerDialogDelegateFactory: RearDisplayInnerDialogDelegate.Factory,
     @Application private val scope: CoroutineScope,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
 ) : CoreStartable, AutoCloseable {
 
     companion object {
@@ -53,6 +59,16 @@
     }
 
     @VisibleForTesting var stateChangeListener: Job? = null
+    private val keyguardVisible = MutableStateFlow(false)
+    private val keyguardVisibleFlow = keyguardVisible.asStateFlow()
+
+    @VisibleForTesting
+    val keyguardCallback =
+        object : KeyguardUpdateMonitorCallback() {
+            override fun onKeyguardVisibilityChanged(visible: Boolean) {
+                keyguardVisible.value = visible
+            }
+        }
 
     override fun close() {
         stateChangeListener?.cancel()
@@ -62,28 +78,39 @@
         if (Flags.deviceStateRdmV2()) {
             var dialog: SystemUIDialog? = null
 
-            stateChangeListener =
-                rearDisplayStateInteractor.state
-                    .map {
-                        when (it) {
-                            is RearDisplayStateInteractor.State.Enabled -> {
-                                val rearDisplayContext =
-                                    context.createDisplayContext(it.innerDisplay)
-                                val delegate =
-                                    rearDisplayInnerDialogDelegateFactory.create(
-                                        rearDisplayContext,
-                                        deviceStateManager::cancelStateRequest,
-                                    )
-                                dialog = delegate.createDialog().apply { show() }
-                            }
+            keyguardUpdateMonitor.registerCallback(keyguardCallback)
 
-                            is RearDisplayStateInteractor.State.Disabled -> {
-                                dialog?.dismiss()
-                                dialog = null
+            stateChangeListener =
+                scope.launch {
+                    combine(rearDisplayStateInteractor.state, keyguardVisibleFlow) {
+                            rearDisplayState,
+                            keyguardVisible ->
+                            Pair(rearDisplayState, keyguardVisible)
+                        }
+                        .collectLatest { (rearDisplayState, keyguardVisible) ->
+                            when (rearDisplayState) {
+                                is RearDisplayStateInteractor.State.Enabled -> {
+                                    if (!keyguardVisible) {
+                                        val rearDisplayContext =
+                                            context.createDisplayContext(
+                                                rearDisplayState.innerDisplay
+                                            )
+                                        val delegate =
+                                            rearDisplayInnerDialogDelegateFactory.create(
+                                                rearDisplayContext,
+                                                deviceStateManager::cancelStateRequest,
+                                            )
+                                        dialog = delegate.createDialog().apply { show() }
+                                    }
+                                }
+
+                                is RearDisplayStateInteractor.State.Disabled -> {
+                                    dialog?.dismiss()
+                                    dialog = null
+                                }
                             }
                         }
-                    }
-                    .launchIn(scope)
+                }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
index 4be35f1..5b97175 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
@@ -78,6 +78,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.app.displaylib.PerDisplayRepository;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
 import com.android.internal.app.IVoiceInteractionSessionListener;
@@ -89,6 +90,7 @@
 import com.android.systemui.contextualeducation.GestureType;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.display.data.repository.DisplayRepository;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardWmStateRefactor;
@@ -109,6 +111,7 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
 import com.android.systemui.shared.recents.ILauncherProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -123,8 +126,6 @@
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellInterface;
 
-import dagger.Lazy;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -136,6 +137,8 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
+import dagger.Lazy;
+
 /**
  * Class to send information from SysUI to Launcher with a binder.
  */
@@ -156,7 +159,9 @@
     private final Executor mMainExecutor;
     private final ShellInterface mShellInterface;
     private final Lazy<ShadeViewController> mShadeViewControllerLazy;
-    private SysUiState mSysUiState;
+    private final PerDisplayRepository<SysUiState> mPerDisplaySysUiStateRepository;
+    private final DisplayRepository mDisplayRepository;
+    private SysUiState mDefaultDisplaySysUIState;
     private final Handler mHandler;
     private final Lazy<NavigationBarController> mNavBarControllerLazy;
     private final ScreenPinningRequest mScreenPinningRequest;
@@ -586,9 +591,12 @@
 
             // Force-update the systemui state flags
             updateSystemUiStateFlags();
-            // TODO b/398011576 - send the state for all displays.
-            notifySystemUiStateFlags(mSysUiState.getFlags(), Display.DEFAULT_DISPLAY);
-
+            if (ShadeWindowGoesAround.isEnabled()) {
+               notifySysUiStateFlagsForAllDisplays();
+            } else {
+                notifySystemUiStateFlags(mDefaultDisplaySysUIState.getFlags(),
+                        Display.DEFAULT_DISPLAY);
+            }
             notifyConnectionChanged();
         }
 
@@ -614,6 +622,18 @@
         }
     };
 
+    /** Propagates the flags for all displays to be notified to Launcher. */
+    @VisibleForTesting
+    public void notifySysUiStateFlagsForAllDisplays() {
+        var displays = mDisplayRepository.getDisplayIds().getValue();
+        for (int displayId : displays) {
+            var state = mPerDisplaySysUiStateRepository.get(displayId);
+            if (state != null) {
+                notifySystemUiStateFlags(state.getFlags(), displayId);
+            }
+        }
+    }
+
     private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
 
     // This is the death handler for the binder from the launcher service
@@ -671,7 +691,7 @@
             ScreenPinningRequest screenPinningRequest,
             NavigationModeController navModeController,
             NotificationShadeWindowController statusBarWinController,
-            SysUiState sysUiState,
+            PerDisplayRepository<SysUiState> perDisplaySysUiStateRepository,
             Provider<SceneInteractor> sceneInteractor,
             Provider<ShadeInteractor> shadeInteractor,
             UserTracker userTracker,
@@ -686,7 +706,8 @@
             Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder,
             BroadcastDispatcher broadcastDispatcher,
             Optional<BackAnimation> backAnimation,
-            ProcessWrapper processWrapper
+            ProcessWrapper processWrapper,
+            DisplayRepository displayRepository
     ) {
         // b/241601880: This component should only be running for primary users or
         // secondaryUsers when visibleBackgroundUsers are supported.
@@ -718,10 +739,10 @@
                 com.android.internal.R.string.config_recentsComponentName));
         mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
                 .setPackage(mRecentsComponentName.getPackageName());
-        // TODO b/398011576 - Here we're still only handling the default display state. We should
-        //  have a callback for any sysuiState change.
-        mSysUiState = sysUiState;
-        mSysUiState.addCallback(mSysUiStateCallback);
+        mPerDisplaySysUiStateRepository = perDisplaySysUiStateRepository;
+        mDisplayRepository = displayRepository;
+        mDefaultDisplaySysUIState = perDisplaySysUiStateRepository.get(Display.DEFAULT_DISPLAY);
+        mDefaultDisplaySysUIState.addCallback(mSysUiStateCallback);
         mUiEventLogger = uiEventLogger;
         mDisplayTracker = displayTracker;
         mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
@@ -770,7 +791,7 @@
                 if (mLauncherProxy != null) {
                     try {
                         if (DesktopModeStatus.canEnterDesktopMode(mContext)
-                                && (sysUiState.getFlags()
+                                && (mDefaultDisplaySysUIState.getFlags()
                                 & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0) {
                             return;
                         }
@@ -795,7 +816,7 @@
     }
 
     public void onVoiceSessionWindowVisibilityChanged(boolean visible) {
-        mSysUiState.setFlag(SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, visible)
+        mDefaultDisplaySysUIState.setFlag(SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, visible)
                 .commitUpdate(mContext.getDisplayId());
     }
 
@@ -804,23 +825,42 @@
         startConnectionToCurrentUser();
     }
 
-    private void updateSystemUiStateFlags() {
+    private void updateSysUIStateForNavbars() {
+        if (ShadeWindowGoesAround.isEnabled()) {
+            var displays = mDisplayRepository.getDisplayIds().getValue();
+            for (int displayId : displays) {
+                updateSysUIStateForNavbarWithDisplayId(displayId);
+            }
+        } else {
+            updateSysUIStateForNavbarWithDisplayId(Display.DEFAULT_DISPLAY);
+        }
+    }
+
+    private void updateSysUIStateForNavbarWithDisplayId(int displayId) {
         final NavigationBar navBarFragment =
-                mNavBarControllerLazy.get().getDefaultNavigationBar();
+                mNavBarControllerLazy.get().getNavigationBar(displayId);
         final NavigationBarView navBarView =
-                mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
+                mNavBarControllerLazy.get().getNavigationBarView(displayId);
         if (SysUiState.DEBUG) {
             Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
                     + " navBarView=" + navBarView
                     + " shadeViewController=" + mShadeViewControllerLazy.get());
         }
 
+        final SysUiState displaySysuiState = mPerDisplaySysUiStateRepository.get(displayId);
+        if (displaySysuiState == null) return;
+
         if (navBarFragment != null) {
             navBarFragment.updateSystemUiStateFlags();
         }
         if (navBarView != null) {
-            navBarView.updateDisabledSystemUiStateFlags(mSysUiState);
+            navBarView.updateDisabledSystemUiStateFlags(displaySysuiState);
         }
+    }
+
+    /** Force updates SystemUI state flags prior to sending them to Launcher. */
+    public void updateSystemUiStateFlags() {
+        updateSysUIStateForNavbars();
         mShadeViewControllerLazy.get().updateSystemUiStateFlags();
         if (mStatusBarWinController != null) {
             mStatusBarWinController.notifyStateChangedCallbacks();
@@ -845,7 +885,7 @@
     private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
             boolean keyguardGoingAway, boolean bouncerShowing, boolean isDozing,
             boolean panelExpanded, boolean isDreaming, boolean communalShowing) {
-        mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
+        mDefaultDisplaySysUIState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
                         keyguardShowing && !keyguardOccluded)
                 .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
                         keyguardShowing && keyguardOccluded)
@@ -1122,7 +1162,7 @@
             new WakefulnessLifecycle.Observer() {
                 @Override
                 public void onStartedWakingUp() {
-                    mSysUiState
+                    mDefaultDisplaySysUIState
                             .setFlag(SYSUI_STATE_AWAKE, true)
                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, true)
                             .commitUpdate(mContext.getDisplayId());
@@ -1130,7 +1170,7 @@
 
                 @Override
                 public void onFinishedWakingUp() {
-                    mSysUiState
+                    mDefaultDisplaySysUIState
                             .setFlag(SYSUI_STATE_AWAKE, true)
                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, false)
                             .commitUpdate(mContext.getDisplayId());
@@ -1138,7 +1178,7 @@
 
                 @Override
                 public void onStartedGoingToSleep() {
-                    mSysUiState
+                    mDefaultDisplaySysUIState
                             .setFlag(SYSUI_STATE_AWAKE, false)
                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, true)
                             .commitUpdate(mContext.getDisplayId());
@@ -1146,7 +1186,7 @@
 
                 @Override
                 public void onFinishedGoingToSleep() {
-                    mSysUiState
+                    mDefaultDisplaySysUIState
                             .setFlag(SYSUI_STATE_AWAKE, false)
                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, false)
                             .commitUpdate(mContext.getDisplayId());
@@ -1247,7 +1287,7 @@
         pw.print("  mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
         pw.print("  mNavBarMode="); pw.println(mNavBarMode);
         pw.print("  mIsPrevServiceCleanedUp="); pw.println(mIsPrevServiceCleanedUp);
-        mSysUiState.dump(pw, args);
+        mDefaultDisplaySysUIState.dump(pw, args);
     }
 
     public interface LauncherProxyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
index c092c2f..9023c62 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
@@ -22,15 +22,15 @@
 import com.android.systemui.qs.shared.model.TileCategory
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.RecordIssueTile
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingDataInteractor
-import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingMapper
-import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingModel
-import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingUserActionInteractor
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
+import com.android.systemui.qs.tiles.impl.irecording.domain.interactor.IssueRecordingDataInteractor
+import com.android.systemui.qs.tiles.impl.irecording.domain.interactor.IssueRecordingUserActionInteractor
+import com.android.systemui.qs.tiles.impl.irecording.ui.mapper.IssueRecordingMapper
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
@@ -59,7 +59,7 @@
                 uiConfig =
                     QSTileUIConfig.Resource(
                         iconRes = R.drawable.qs_record_issue_icon_off,
-                        labelRes = R.string.qs_record_issue_label
+                        labelRes = R.string.qs_record_issue_label,
                     ),
                 instanceId = uiEventLogger.getNewInstanceId(),
                 category = TileCategory.UTILITIES,
@@ -73,7 +73,7 @@
             factory: QSTileViewModelFactory.Static<IssueRecordingModel>,
             mapper: IssueRecordingMapper,
             stateInteractor: IssueRecordingDataInteractor,
-            userActionInteractor: IssueRecordingUserActionInteractor
+            userActionInteractor: IssueRecordingUserActionInteractor,
         ): QSTileViewModel =
             if (Flags.qsNewTilesFuture())
                 factory.create(
diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
index c9712fc..ba5b4ff 100644
--- a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
@@ -20,15 +20,15 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.shared.model.TileCategory
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileDataInteractor
 import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
 import com.android.systemui.qs.tiles.impl.rotation.ui.mapper.RotationLockTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
@@ -73,7 +73,7 @@
             factory: QSTileViewModelFactory.Static<RotationLockTileModel>,
             mapper: RotationLockTileMapper,
             stateInteractor: RotationLockTileDataInteractor,
-            userActionInteractor: RotationLockTileUserActionInteractor
+            userActionInteractor: RotationLockTileUserActionInteractor,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(ROTATION_TILE_SPEC),
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 4753b9a..3ad0867 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -33,7 +33,7 @@
 import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.classifier.FalsingCollectorActual
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.DisplayId
@@ -687,7 +687,7 @@
                 if (!isDeviceEntered) {
                     coroutineScope {
                         launch {
-                            deviceEntryHapticsInteractor.playSuccessHaptic
+                            deviceEntryHapticsInteractor.playSuccessHapticOnDeviceEntry
                                 .sample(sceneInteractor.currentScene)
                                 .collect { currentScene ->
                                     if (Flags.msdlFeedback()) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
index 9a9c576..b4cc055 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
@@ -24,14 +24,14 @@
 import com.android.systemui.qs.shared.model.TileCategory
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.ScreenRecordTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor.ScreenRecordTileDataInteractor
 import com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor.ScreenRecordTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.ScreenRecordTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.mapper.ScreenRecordTileMapper
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
@@ -86,7 +86,7 @@
             factory: QSTileViewModelFactory.Static<ScreenRecordModel>,
             mapper: ScreenRecordTileMapper,
             stateInteractor: ScreenRecordTileDataInteractor,
-            userActionInteractor: ScreenRecordTileUserActionInteractor
+            userActionInteractor: ScreenRecordTileUserActionInteractor,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(SCREEN_RECORD_TILE_SPEC),
diff --git a/packages/SystemUI/src/com/android/systemui/security/data/repository/SecurityRepository.kt b/packages/SystemUI/src/com/android/systemui/security/data/repository/SecurityRepository.kt
index 7e967f4..0b039fe 100644
--- a/packages/SystemUI/src/com/android/systemui/security/data/repository/SecurityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/security/data/repository/SecurityRepository.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.security.data.repository
 
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.security.data.model.SecurityModel
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index eae0ba6..ade62a9 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -164,16 +164,17 @@
         container.setVisibility(View.VISIBLE);
         ViewGroup.MarginLayoutParams lp =
                 (ViewGroup.MarginLayoutParams) container.getLayoutParams();
-        int horizontalMargin =
-                getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
-        lp.leftMargin = horizontalMargin;
-        lp.rightMargin = horizontalMargin;
-
-        int verticalMargin =
-                getResources().getDimensionPixelSize(
-                        R.dimen.notification_guts_option_vertical_padding);
-
-        lp.topMargin = verticalMargin;
+        // Remove the margin. Have the container take all the space. Instead, insert padding.
+        // This allows for the background to be visible around the slider.
+        int margin = 0;
+        lp.topMargin = margin;
+        lp.bottomMargin = margin;
+        lp.leftMargin = margin;
+        lp.rightMargin = margin;
+        int padding = getResources().getDimensionPixelSize(
+                R.dimen.rounded_slider_background_padding
+        );
+        container.setPadding(padding, padding, padding, padding);
         // If in multi-window or freeform, increase the top margin so the brightness dialog
         // doesn't get cut off.
         final int windowingMode = configuration.windowConfiguration.getWindowingMode();
@@ -182,17 +183,15 @@
             lp.topMargin += 50;
         }
 
-        lp.bottomMargin = verticalMargin;
-
         int orientation = configuration.orientation;
         int windowWidth = getWindowAvailableWidth();
 
         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
             boolean shouldBeFullWidth = getIntent()
                     .getBooleanExtra(EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH, false);
-            lp.width = (shouldBeFullWidth ? windowWidth : windowWidth / 2) - horizontalMargin * 2;
+            lp.width = (shouldBeFullWidth ? windowWidth : windowWidth / 2) - margin * 2;
         } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
-            lp.width = windowWidth - horizontalMargin * 2;
+            lp.width = windowWidth - margin * 2;
         }
 
         container.setLayoutParams(lp);
@@ -202,7 +201,7 @@
                     // Exclude this view (and its horizontal margins) from triggering gestures.
                     // This prevents back gesture from being triggered by dragging close to the
                     // edge of the slider (0% or 100%).
-                    bounds.set(-horizontalMargin, 0, right - left + horizontalMargin, bottom - top);
+                    bounds.set(-margin, 0, right - left + margin, bottom - top);
                     v.setSystemGestureExclusionRects(List.of(bounds));
                 });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt
index 9e20055..962a3bd 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ComposeDialogComposableProvider.kt
@@ -17,12 +17,15 @@
 package com.android.systemui.settings.brightness
 
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.unit.dp
 import com.android.compose.theme.PlatformTheme
 import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
+import com.android.systemui.brightness.ui.compose.ContainerColors
 import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
 import com.android.systemui.lifecycle.rememberViewModel
 
@@ -44,7 +47,11 @@
         rememberViewModel(traceName = "BrightnessDialog.viewModel") {
             brightnessSliderViewModelFactory.create(false)
         }
-    BrightnessSliderContainer(viewModel = viewModel, Modifier.fillMaxWidth())
+    BrightnessSliderContainer(
+        viewModel = viewModel,
+        containerColors = ContainerColors.singleColor(ContainerColors.defaultContainerColor),
+        modifier = Modifier.fillMaxWidth().padding(8.dp),
+    )
 }
 
 class ComposableProvider(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 3be2f1b..362b5db 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade
 
 import android.content.Context
+import android.content.res.Configuration
 import android.graphics.Rect
 import android.os.PowerManager
 import android.os.SystemClock
@@ -25,11 +26,13 @@
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewGroup
+import android.view.WindowInsets
 import android.widget.FrameLayout
 import androidx.activity.OnBackPressedDispatcher
 import androidx.activity.OnBackPressedDispatcherOwner
 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
 import androidx.compose.ui.platform.ComposeView
+import androidx.core.view.updateMargins
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleEventObserver
 import androidx.lifecycle.LifecycleObserver
@@ -101,7 +104,10 @@
 ) : LifecycleOwner {
     private val logger = Logger(logBuffer, TAG)
 
-    private class CommunalWrapper(context: Context) : FrameLayout(context) {
+    private class CommunalWrapper(
+        context: Context,
+        private val communalSettingsInteractor: CommunalSettingsInteractor,
+    ) : FrameLayout(context) {
         private val consumers: MutableSet<Consumer<Boolean>> = ArraySet()
 
         override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
@@ -121,6 +127,24 @@
                 consumers.clear()
             }
         }
+
+        override fun onApplyWindowInsets(windowInsets: WindowInsets): WindowInsets {
+            if (
+                !communalSettingsInteractor.isV2FlagEnabled() ||
+                    resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE
+            ) {
+                return super.onApplyWindowInsets(windowInsets)
+            }
+            val type = WindowInsets.Type.displayCutout()
+            val insets = windowInsets.getInsets(type)
+
+            // Reset horizontal margins added by window insets, so hub can be edge to edge.
+            if (insets.left > 0 || insets.right > 0) {
+                val lp = layoutParams as LayoutParams
+                lp.updateMargins(0, lp.topMargin, 0, lp.bottomMargin)
+            }
+            return WindowInsets.CONSUMED
+        }
     }
 
     /** The container view for the hub. This will not be initialized until [initView] is called. */
@@ -443,7 +467,8 @@
         collectFlow(containerView, keyguardInteractor.isDreaming, { isDreaming = it })
         collectFlow(containerView, communalViewModel.swipeToHubEnabled, { swipeToHubEnabled = it })
 
-        communalContainerWrapper = CommunalWrapper(containerView.context)
+        communalContainerWrapper =
+            CommunalWrapper(containerView.context, communalSettingsInteractor)
         communalContainerWrapper?.addView(communalContainerView)
         logger.d("Hub container initialized")
         return communalContainerWrapper!!
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 305444f..8d7cc92 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -30,8 +30,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.Log;
 import android.view.Display;
 import android.view.IWindow;
@@ -75,7 +73,6 @@
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
-import com.android.systemui.util.settings.SecureSettings;
 
 import dagger.Lazy;
 
@@ -134,7 +131,6 @@
 
     private final SysuiColorExtractor mColorExtractor;
     private final NotificationShadeWindowModel mNotificationShadeWindowModel;
-    private final SecureSettings mSecureSettings;
     /**
      * Layout params would be aggregated and dispatched all at once if this is > 0.
      *
@@ -168,7 +164,6 @@
             Lazy<SelectedUserInteractor> userInteractor,
             UserTracker userTracker,
             NotificationShadeWindowModel notificationShadeWindowModel,
-            SecureSettings secureSettings,
             Lazy<CommunalInteractor> communalInteractor,
             @ShadeDisplayAware LayoutParams shadeWindowLayoutParams) {
         mContext = context;
@@ -186,7 +181,6 @@
         mBackgroundExecutor = backgroundExecutor;
         mColorExtractor = colorExtractor;
         mNotificationShadeWindowModel = notificationShadeWindowModel;
-        mSecureSettings = secureSettings;
         // prefix with {slow} to make sure this dumps at the END of the critical section.
         dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this);
         mAuthController = authController;
@@ -424,7 +418,7 @@
                     (long) mLpChanged.preferredMaxDisplayRefreshRate);
         }
 
-        if (state.bouncerShowing && !isSecureWindowsDisabled()) {
+        if (state.bouncerShowing) {
             mLpChanged.flags |= LayoutParams.FLAG_SECURE;
         } else {
             mLpChanged.flags &= ~LayoutParams.FLAG_SECURE;
@@ -437,13 +431,6 @@
         }
     }
 
-    private boolean isSecureWindowsDisabled() {
-        return mSecureSettings.getIntForUser(
-            Settings.Secure.DISABLE_SECURE_WINDOWS,
-            0,
-            UserHandle.USER_CURRENT) == 1;
-    }
-
     private void adjustScreenOrientation(NotificationShadeWindowState state) {
         if (state.bouncerShowing || state.isKeyguardShowingAndNotOccluded() || state.dozing) {
             if (mKeyguardStateController.isKeyguardScreenRotationAllowed()) {
@@ -511,17 +498,18 @@
     }
 
     private boolean isExpanded(NotificationShadeWindowState state) {
+        boolean visForBlur = !Flags.disableBlurredShadeVisible() && state.backgroundBlurRadius > 0;
         boolean isExpanded = !state.forceWindowCollapsed && (state.isKeyguardShowingAndNotOccluded()
                 || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
                 || state.headsUpNotificationShowing
                 || state.scrimsVisibility != ScrimController.TRANSPARENT)
-                || state.backgroundBlurRadius > 0
+                || visForBlur
                 || state.launchingActivityFromNotification;
         mLogger.logIsExpanded(isExpanded, state.forceWindowCollapsed,
                 state.isKeyguardShowingAndNotOccluded(), state.panelVisible,
                 state.keyguardFadingAway, state.bouncerShowing, state.headsUpNotificationShowing,
                 state.scrimsVisibility != ScrimController.TRANSPARENT,
-                state.backgroundBlurRadius > 0, state.launchingActivityFromNotification);
+                visForBlur, state.launchingActivityFromNotification);
         return isExpanded;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt
index 91c92cc8..ce74cb7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt
@@ -20,7 +20,7 @@
 import android.os.UserHandle
 import android.safetycenter.SafetyCenterManager
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 20b44d7..5609326 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -26,6 +26,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.graphics.Color
 import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.compose.animation.scene.OverlayKey
 import com.android.systemui.battery.BatteryMeterViewController
 import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.lifecycle.Hydrator
@@ -86,6 +87,22 @@
         (ViewGroup, StatusBarLocation) -> BatteryMeterViewController =
         batteryMeterViewControllerFactory::create
 
+    val showClock: Boolean by
+        hydrator.hydratedStateOf(
+            traceName = "showClock",
+            initialValue =
+                shouldShowClock(
+                    isShadeLayoutWide = shadeInteractor.isShadeLayoutWide.value,
+                    overlays = sceneInteractor.currentOverlays.value,
+                ),
+            source =
+                combine(
+                    shadeInteractor.isShadeLayoutWide,
+                    sceneInteractor.currentOverlays,
+                    ::shouldShowClock,
+                ),
+        )
+
     val notificationsChipHighlight: HeaderChipHighlight by
         hydrator.hydratedStateOf(
             traceName = "notificationsChipHighlight",
@@ -114,13 +131,6 @@
                 },
         )
 
-    val isShadeLayoutWide: Boolean by
-        hydrator.hydratedStateOf(
-            traceName = "isShadeLayoutWide",
-            initialValue = shadeInteractor.isShadeLayoutWide.value,
-            source = shadeInteractor.isShadeLayoutWide,
-        )
-
     /** True if there is exactly one mobile connection. */
     val isSingleCarrier: StateFlow<Boolean> = mobileIconsInteractor.isSingleCarrier
 
@@ -271,6 +281,11 @@
         }
     }
 
+    private fun shouldShowClock(isShadeLayoutWide: Boolean, overlays: Set<OverlayKey>): Boolean {
+        // Notifications shade on narrow layout renders its own clock. Hide the header clock.
+        return isShadeLayoutWide || Overlays.NotificationsShade !in overlays
+    }
+
     private fun getFormatFromPattern(pattern: String?): DateFormat {
         val format = DateFormat.getInstanceForSkeleton(pattern, Locale.getDefault())
         format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index f45971b..2bacee1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -86,7 +86,7 @@
      */
     fun prepareBlur(viewRootImpl: ViewRootImpl?, radius: Int) {
         if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid ||
-            !supportsBlursOnWindows() || earlyWakeupEnabled
+            !shouldBlur(radius) || earlyWakeupEnabled
         ) {
             return
         }
@@ -113,7 +113,7 @@
             return
         }
         createTransaction().use {
-            if (supportsBlursOnWindows()) {
+            if (shouldBlur(radius)) {
                 it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
                 if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) {
                     Trace.asyncTraceForTrackBegin(
@@ -142,6 +142,14 @@
         return SurfaceControl.Transaction()
     }
 
+    private fun shouldBlur(radius: Int): Boolean {
+        return supportsBlursOnWindows() ||
+                ((Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) &&
+                        supportsBlursOnWindowsBase() &&
+                        lastAppliedBlur > 0 &&
+                        radius == 0)
+    }
+
     /**
      * If this device can render blurs.
      *
@@ -149,8 +157,11 @@
      * @return {@code true} when supported.
      */
     open fun supportsBlursOnWindows(): Boolean {
+        return supportsBlursOnWindowsBase() && crossWindowBlurListeners.isCrossWindowBlurEnabled
+    }
+
+    private fun supportsBlursOnWindowsBase(): Boolean {
         return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() &&
-                crossWindowBlurListeners.isCrossWindowBlurEnabled() &&
                 !SystemProperties.getBoolean("persist.sysui.disableBlur", false)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 657c86b..e66b218 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -1088,14 +1088,18 @@
 
             if (!TextUtils.equals(mTopIndicationView.getText(), newIndication)) {
                 mWakeLock.setAcquired(true);
+                final KeyguardIndication.Builder builder = new KeyguardIndication.Builder()
+                        .setMessage(newIndication)
+                        .setTextColor(ColorStateList.valueOf(
+                                useMisalignmentColor
+                                        ? mContext.getColor(R.color.misalignment_text_color)
+                                        : Color.WHITE));
+                if (mBiometricMessage != null && newIndication == mBiometricMessage) {
+                    builder.setForceAccessibilityLiveRegionAssertive();
+                }
+
                 mTopIndicationView.switchIndication(newIndication,
-                        new KeyguardIndication.Builder()
-                                .setMessage(newIndication)
-                                .setTextColor(ColorStateList.valueOf(
-                                        useMisalignmentColor
-                                                ? mContext.getColor(R.color.misalignment_text_color)
-                                                : Color.WHITE))
-                                .build(),
+                        builder.build(),
                         true, () -> mWakeLock.setAcquired(false));
             }
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 05ef164..d2f424a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -394,7 +394,7 @@
                 // Only drag down on sensitive views, otherwise the ExpandHelper will take this
                 return if (NotificationBundleUi.isEnabled)
                     view.entryAdapter?.isSensitive?.value == true
-                else view.entry.isSensitive.value
+                else view.entryLegacy.isSensitive.value
             }
         }
         return false
@@ -569,7 +569,7 @@
             if (NotificationBundleUi.isEnabled) {
                 userId = expandView.entryAdapter?.sbn?.userId!!
             } else {
-                userId = expandView.entry.sbn.userId
+                userId = expandView.entryLegacy.sbn.userId
             }
         }
         var fullShadeNeedsBouncer =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index c2e355d..03c191e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -22,6 +22,7 @@
 import android.app.Notification;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -31,6 +32,8 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.R;
 import com.android.internal.widget.CachingIconView;
 import com.android.internal.widget.ConversationLayout;
@@ -39,6 +42,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationContentView;
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -214,7 +218,7 @@
         }
         // in case no view is visible we make sure the time is visible
         int timeVisibility = !hasVisibleText
-                || row.getEntry().getSbn().getNotification().showsTime()
+                || showsTime(row)
                 ? View.VISIBLE : View.GONE;
         time.setVisibility(timeVisibility);
         View left = null;
@@ -243,6 +247,17 @@
         }
     }
 
+    @VisibleForTesting
+    boolean showsTime(ExpandableNotificationRow row) {
+        StatusBarNotification sbn;
+        if (NotificationBundleUi.isEnabled()) {
+            sbn = row.getEntryAdapter() != null ? row.getEntryAdapter().getSbn() : null;
+        } else {
+            sbn = row.getEntry().getSbn();
+        }
+        return (sbn != null && sbn.getNotification().showsTime());
+    }
+
     /**
      * Reset the modifications to this row for removing it from the group.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 3180a06..fdcd39d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -34,7 +34,7 @@
             value = {
                     REDACTION_TYPE_NONE,
                     REDACTION_TYPE_PUBLIC,
-                    REDACTION_TYPE_SENSITIVE_CONTENT})
+                    REDACTION_TYPE_OTP})
     @interface RedactionType {}
 
     /**
@@ -52,7 +52,7 @@
      * 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;
+    int REDACTION_TYPE_OTP = 1 << 1;
 
     /**
      * @param userId user Id
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index ec04edb..9bf3d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -47,7 +47,6 @@
 import android.net.Uri;
 import android.os.Looper;
 import android.os.Process;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -78,6 +77,7 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
@@ -795,7 +795,7 @@
         }
 
         if (shouldShowSensitiveContentRedactedView(ent)) {
-            return REDACTION_TYPE_SENSITIVE_CONTENT;
+            return REDACTION_TYPE_OTP;
         }
         return REDACTION_TYPE_NONE;
     }
@@ -920,7 +920,9 @@
     // notification's "when" time, or the notification entry creation time
     private long getEarliestNotificationTime(NotificationEntry notif) {
         long notifWhenWallClock = notif.getSbn().getNotification().getWhen();
-        long creationTimeDelta = SystemClock.elapsedRealtime() - notif.getCreationTime();
+        long creationTimeDelta = UseElapsedRealtimeForCreationTime.getCurrentTime()
+                - notif.getCreationTime();
+
         long creationTimeWallClock = System.currentTimeMillis() - creationTimeDelta;
         return Math.min(notifWhenWallClock, creationTimeWallClock);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 0d34bdc..485d5b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -59,11 +59,13 @@
 import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
 import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -134,20 +136,21 @@
                     view.getTag(com.android.internal.R.id.notification_action_index_tag);
 
             final ExpandableNotificationRow row = getNotificationRowForParent(view.getParent());
-            final NotificationEntry entry = getNotificationForParent(view.getParent());
-            mLogger.logInitialClick(
-                    row != null ? row.getLoggingKey() : null, actionIndex, pendingIntent);
+            if (row == null) {
+                return false;
+            }
+            mLogger.logInitialClick(row.getLoggingKey(), actionIndex, pendingIntent);
 
             if (handleRemoteInput(view, pendingIntent)) {
-                mLogger.logRemoteInputWasHandled(
-                        row != null ? row.getLoggingKey() : null, actionIndex);
+                mLogger.logRemoteInputWasHandled(row.getLoggingKey(), actionIndex);
                 return true;
             }
 
             if (DEBUG) {
                 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
             }
-            logActionClick(view, entry, pendingIntent);
+            Notification.Action action = getActionFromView(view, row, pendingIntent);
+            logActionClick(view, row.getKey(), action);
             // The intent we are sending is for the application, which
             // won't have permission to immediately start an activity after
             // the user switches to home.  We know it is safe to do at this
@@ -156,33 +159,47 @@
                 ActivityManager.getService().resumeAppSwitches();
             } catch (RemoteException e) {
             }
-            Notification.Action action = getActionFromView(view, entry, pendingIntent);
             return mCallback.handleRemoteViewClick(view, pendingIntent,
                     action == null ? false : action.isAuthenticationRequired(), actionIndex, () -> {
                     Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
                     mLogger.logStartingIntentWithDefaultHandler(
-                            row != null ? row.getLoggingKey() : null, pendingIntent, actionIndex);
+                             row.getLoggingKey(), pendingIntent, actionIndex);
                     boolean started = RemoteViews.startPendingIntent(view, pendingIntent, options);
-                    if (started) releaseNotificationIfKeptForRemoteInputHistory(entry);
+                    if (started) {
+                        if (NotificationBundleUi.isEnabled()) {
+                            releaseNotificationIfKeptForRemoteInputHistory(row.getEntryAdapter());
+                        } else {
+                            releaseNotificationIfKeptForRemoteInputHistory(row.getEntryLegacy());
+                        }
+                    }
                     return started;
             });
         }
 
         private @Nullable Notification.Action getActionFromView(View view,
-                NotificationEntry entry, PendingIntent actionIntent) {
+                ExpandableNotificationRow row, PendingIntent actionIntent) {
             Integer actionIndex = (Integer)
                     view.getTag(com.android.internal.R.id.notification_action_index_tag);
             if (actionIndex == null) {
                 return null;
             }
-            if (entry == null) {
+            StatusBarNotification statusBarNotification = null;
+            if (NotificationBundleUi.isEnabled()) {
+                if (row.getEntryAdapter() != null) {
+                    statusBarNotification = row.getEntryAdapter().getSbn();
+                }
+            } else {
+                if (row.getEntryLegacy() != null) {
+                    statusBarNotification = row.getEntryLegacy().getSbn();
+                }
+            }
+            if (statusBarNotification == null) {
                 Log.w(TAG, "Couldn't determine notification for click.");
                 return null;
             }
 
             // Notification may be updated before this function is executed, and thus play safe
             // here and verify that the action object is still the one that where the click happens.
-            StatusBarNotification statusBarNotification = entry.getSbn();
             Notification.Action[] actions = statusBarNotification.getNotification().actions;
             if (actions == null || actionIndex >= actions.length) {
                 Log.w(TAG, "statusBarNotification.getNotification().actions is null or invalid");
@@ -199,14 +216,12 @@
 
         private void logActionClick(
                 View view,
-                NotificationEntry entry,
-                PendingIntent actionIntent) {
-            Notification.Action action = getActionFromView(view, entry, actionIntent);
+                String key,
+                Notification.Action action) {
             if (action == null) {
                 return;
             }
             ViewParent parent = view.getParent();
-            String key = entry.getSbn().getKey();
             int buttonIndex = -1;
             // If this is a default template, determine the index of the button.
             if (view.getId() == com.android.internal.R.id.action0 &&
@@ -214,20 +229,10 @@
                 ViewGroup actionGroup = (ViewGroup) parent;
                 buttonIndex = actionGroup.indexOfChild(view);
             }
-            final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true);
+            final NotificationVisibility nv = mVisibilityProvider.obtain(key, true);
             mClickNotifier.onNotificationActionClick(key, buttonIndex, action, nv, false);
         }
 
-        private NotificationEntry getNotificationForParent(ViewParent parent) {
-            while (parent != null) {
-                if (parent instanceof ExpandableNotificationRow) {
-                    return ((ExpandableNotificationRow) parent).getEntry();
-                }
-                parent = parent.getParent();
-            }
-            return null;
-        }
-
         private @Nullable ExpandableNotificationRow getNotificationRowForParent(ViewParent parent) {
             while (parent != null) {
                 if (parent instanceof ExpandableNotificationRow) {
@@ -394,11 +399,21 @@
         }
     }
 
+    /**
+     * Use {@link com.android.systemui.statusbar.notification.row.NotificationActionClickManager}
+     * instead
+     */
     public void addActionPressListener(Consumer<NotificationEntry> listener) {
+        NotificationBundleUi.assertInLegacyMode();
         mActionPressListeners.addIfAbsent(listener);
     }
 
+    /**
+     * Use {@link com.android.systemui.statusbar.notification.row.NotificationActionClickManager}
+     * instead
+     */
     public void removeActionPressListener(Consumer<NotificationEntry> listener) {
+        NotificationBundleUi.assertInLegacyMode();
         mActionPressListeners.remove(listener);
     }
 
@@ -681,12 +696,30 @@
      * (after unlock, if applicable), and will then wait a short time to allow the app to update the
      * notification in response to the action.
      */
+    private void releaseNotificationIfKeptForRemoteInputHistory(EntryAdapter entryAdapter) {
+        if (entryAdapter == null) {
+            return;
+        }
+        if (mRemoteInputListener != null) {
+            mRemoteInputListener.releaseNotificationIfKeptForRemoteInputHistory(
+                    entryAdapter.getKey());
+        }
+        entryAdapter.onNotificationActionClicked();
+    }
+
+    /**
+     * Checks if the notification is being kept due to the user sending an inline reply, and if
+     * so, releases that hold.  This is called anytime an action on the notification is dispatched
+     * (after unlock, if applicable), and will then wait a short time to allow the app to update the
+     * notification in response to the action.
+     */
     private void releaseNotificationIfKeptForRemoteInputHistory(NotificationEntry entry) {
+        NotificationBundleUi.assertInLegacyMode();
         if (entry == null) {
             return;
         }
         if (mRemoteInputListener != null) {
-            mRemoteInputListener.releaseNotificationIfKeptForRemoteInputHistory(entry);
+            mRemoteInputListener.releaseNotificationIfKeptForRemoteInputHistory(entry.getKey());
         }
         for (Consumer<NotificationEntry> listener : mActionPressListeners) {
             listener.accept(entry);
@@ -866,7 +899,7 @@
         boolean isNotificationKeptForRemoteInputHistory(@NonNull String key);
 
         /** Called on user interaction to end lifetime extension for history */
-        void releaseNotificationIfKeptForRemoteInputHistory(@NonNull NotificationEntry entry);
+        void releaseNotificationIfKeptForRemoteInputHistory(@NonNull String entryKey);
 
         /** Called when the RemoteInputController is attached to the manager */
         void setRemoteInputController(@NonNull RemoteInputController remoteInputController);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index bdfdb81..472dc82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -19,8 +19,6 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
-import android.content.Context
-import android.content.res.Configuration
 import android.os.SystemClock
 import android.util.IndentingPrintWriter
 import android.util.Log
@@ -38,21 +36,20 @@
 import com.android.systemui.Flags.spatialModelAppPushback
 import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.shade.ShadeExpansionChangeEvent
 import com.android.systemui.shade.ShadeExpansionListener
-import com.android.systemui.shared.Flags.ambientAod
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.WallpaperController
+import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
 import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
 import com.android.wm.shell.appzoomout.AppZoomOut
 import java.io.PrintWriter
@@ -78,14 +75,14 @@
     private val keyguardInteractor: KeyguardInteractor,
     private val choreographer: Choreographer,
     private val wallpaperController: WallpaperController,
+    private val wallpaperInteractor: WallpaperInteractor,
     private val notificationShadeWindowController: NotificationShadeWindowController,
     private val dozeParameters: DozeParameters,
-    @ShadeDisplayAware private val context: Context,
-    private val splitShadeStateController: SplitShadeStateController,
+    private val shadeModeInteractor: ShadeModeInteractor,
     private val windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
     private val appZoomOutOptional: Optional<AppZoomOut>,
+    @Application private val applicationScope: CoroutineScope,
     dumpManager: DumpManager,
-    configurationController: ConfigurationController,
 ) : ShadeExpansionListener, Dumpable {
     companion object {
         private const val WAKE_UP_ANIMATION_ENABLED = true
@@ -107,12 +104,13 @@
     private var isOpen: Boolean = false
     private var isBlurred: Boolean = false
     private var listeners = mutableListOf<DepthListener>()
-    private var inSplitShade: Boolean = false
 
     private var prevTracking: Boolean = false
     private var prevTimestamp: Long = -1
     private var prevShadeDirection = 0
     private var prevShadeVelocity = 0f
+    private var prevDozeAmount: Float = 0f
+    @VisibleForTesting var wallpaperSupportsAmbientMode: Boolean = false
     // tracks whether app launch transition is in progress. This involves two independent factors
     // that control blur, shade expansion and app launch animation from outside sysui.
     // They can complete out of order, this flag will be reset by the animation that finishes later.
@@ -228,8 +226,10 @@
             scheduleUpdate()
         }
 
-    /** Blur radius of the wake-up animation on this frame. */
-    private var wakeAndUnlockBlurRadius = 0f
+    private data class WakeAndUnlockBlurData(val radius: Float, val useZoom: Boolean = true)
+
+    /** Blur radius of the wake and unlock animation on this frame, and whether to zoom out. */
+    private var wakeAndUnlockBlurData = WakeAndUnlockBlurData(0f)
         set(value) {
             if (field == value) return
             field = value
@@ -256,14 +256,18 @@
             ShadeInterpolation.getNotificationScrimAlpha(qsPanelExpansion) * shadeExpansion
         combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsExpandedRatio))
         combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress))
-        var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius)
+        var shadeRadius = max(combinedBlur, wakeAndUnlockBlurData.radius)
 
         if (areBlursDisabledForAppLaunch || blursDisabledForUnlock) {
             shadeRadius = 0f
         }
 
         var blur = shadeRadius.toInt()
-        val zoomOut = blurRadiusToZoomOut(blurRadius = shadeRadius)
+        // If the blur comes from waking up, we don't want to zoom out the background
+        val zoomOut =
+            if (shadeRadius != wakeAndUnlockBlurData.radius|| wakeAndUnlockBlurData.useZoom)
+                blurRadiusToZoomOut(blurRadius = shadeRadius)
+            else 0f
         // Make blur be 0 if it is necessary to stop blur effect.
         if (scrimsVisible) {
             if (!Flags.notificationShadeBlur()) {
@@ -283,7 +287,7 @@
 
     private fun blurRadiusToZoomOut(blurRadius: Float): Float {
         var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(blurRadius))
-        if (inSplitShade) {
+        if (shadeModeInteractor.isSplitShade) {
             zoomOut = 0f
         }
 
@@ -348,14 +352,14 @@
                         startDelay = keyguardStateController.keyguardFadingAwayDelay
                         interpolator = Interpolators.FAST_OUT_SLOW_IN
                         addUpdateListener { animation: ValueAnimator ->
-                            wakeAndUnlockBlurRadius =
-                                blurUtils.blurRadiusOfRatio(animation.animatedValue as Float)
+                            wakeAndUnlockBlurData =
+                                WakeAndUnlockBlurData(blurUtils.blurRadiusOfRatio(animation.animatedValue as Float))
                         }
                         addListener(
                             object : AnimatorListenerAdapter() {
                                 override fun onAnimationEnd(animation: Animator) {
                                     keyguardAnimator = null
-                                    wakeAndUnlockBlurRadius = 0f
+                                    wakeAndUnlockBlurData = WakeAndUnlockBlurData(0f)
                                 }
                             }
                         )
@@ -391,15 +395,23 @@
             }
 
             override fun onDozeAmountChanged(linear: Float, eased: Float) {
-                wakeAndUnlockBlurRadius =
-                    if (ambientAod()) {
-                        0f
-                    } else {
-                        blurUtils.blurRadiusOfRatio(eased)
-                    }
+                prevDozeAmount = eased
+                updateWakeBlurRadius(prevDozeAmount)
             }
         }
 
+    private fun updateWakeBlurRadius(ratio: Float) {
+        wakeAndUnlockBlurData = WakeAndUnlockBlurData(getNewWakeBlurRadius(ratio), false)
+    }
+
+    private fun getNewWakeBlurRadius(ratio: Float): Float {
+        return if (!wallpaperSupportsAmbientMode) {
+            0f
+        } else {
+            blurUtils.blurRadiusOfRatio(ratio)
+        }
+    }
+
     init {
         dumpManager.registerCriticalDumpable(javaClass.name, this)
         if (WAKE_UP_ANIMATION_ENABLED) {
@@ -413,14 +425,16 @@
         }
         shadeAnimation.setStiffness(SpringForce.STIFFNESS_LOW)
         shadeAnimation.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
-        updateResources()
-        configurationController.addCallback(
-            object : ConfigurationController.ConfigurationListener {
-                override fun onConfigChanged(newConfig: Configuration?) {
-                    updateResources()
+        applicationScope.launch {
+            wallpaperInteractor.wallpaperSupportsAmbientMode.collect { supported ->
+                wallpaperSupportsAmbientMode = supported
+                if (getNewWakeBlurRadius(prevDozeAmount) == wakeAndUnlockBlurData.radius
+                    && !wakeAndUnlockBlurData.useZoom) {
+                    // Update wake and unlock radius only if the previous value comes from wake-up.
+                    updateWakeBlurRadius(prevDozeAmount)
                 }
             }
-        )
+        }
         initBlurListeners()
     }
 
@@ -440,10 +454,6 @@
         }
     }
 
-    private fun updateResources() {
-        inSplitShade = splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
-    }
-
     fun addListener(listener: DepthListener) {
         listeners.add(listener)
     }
@@ -611,7 +621,8 @@
             it.println("shouldApplyShadeBlur: ${shouldApplyShadeBlur()}")
             it.println("shadeAnimation: ${shadeAnimation.radius}")
             it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}")
-            it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
+            it.println("wakeAndUnlockBlurRadius: ${wakeAndUnlockBlurData.radius}")
+            it.println("wakeAndUnlockBlurUsesZoom: ${wakeAndUnlockBlurData.useZoom}")
             it.println("blursDisabledForAppLaunch: $blursDisabledForAppLaunch")
             it.println("appLaunchTransitionIsInProgress: $appLaunchTransitionIsInProgress")
             it.println("qsPanelExpansion: $qsPanelExpansion")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f88c618..c2a87cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -675,7 +675,7 @@
         }
         StatusBarIconView icon = NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getIcons().getShelfIcon()
-                : row.getEntry().getIcons().getShelfIcon();
+                : row.getEntryLegacy().getIcons().getShelfIcon();
         float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
         if (shelfIconPosition < maxTop && !mAmbientState.isFullyHidden()) {
             int top = (int) (maxTop - shelfIconPosition);
@@ -689,7 +689,7 @@
     private void updateContinuousClipping(final ExpandableNotificationRow row) {
         StatusBarIconView icon = NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getIcons().getShelfIcon()
-                : row.getEntry().getIcons().getShelfIcon();
+                : row.getEntryLegacy().getIcons().getShelfIcon();
         boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDozing();
         boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
         if (needsContinuousClipping && !isContinuousClipping) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index b2764e1..6d3c12d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -18,12 +18,14 @@
 per-file *Notification* = file:notification/OWNERS
 # Files that control blur effects on shade
 per-file *NotificationShadeDepth* = set noparent
-per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com
+per-file *NotificationShadeDepth* = shanh@google.com, rahulbanerjee@google.com, tracyzhou@google.com
 per-file *NotificationShadeDepth* = file:../keyguard/OWNERS
 per-file *Blur* = set noparent
-per-file *Blur* = shanh@google.com, rahulbanerjee@google.com
+per-file *Blur* = shanh@google.com, rahulbanerjee@google.com, tracyzhou@google.com
 # Not setting noparent here, since *Mode* matches many other classes (e.g., *ViewModel*)
 per-file *Mode* = file:notification/OWNERS
+per-file *SmartReply* = set noparent
+per-file *SmartReply* = file:notification/OWNERS
 per-file *RemoteInput* = set noparent
 per-file *RemoteInput* = file:notification/OWNERS
 per-file *EmptyShadeView* = set noparent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerExt.kt
index 6148b40..8fc95092 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerExt.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsReturnAnimations.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsReturnAnimations.kt
new file mode 100644
index 0000000..1764194
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsReturnAnimations.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips
+
+import com.android.systemui.Flags
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
+
+/** Helper for reading or using the status_bar_chips_return_animations flag state. */
+object StatusBarChipsReturnAnimations {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_CHIPS_RETURN_ANIMATIONS
+
+    /** Is the feature enabled. */
+    @JvmStatic
+    inline val isEnabled
+        get() = StatusBarChipsModernization.isEnabled && Flags.statusBarChipsReturnAnimations()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index be3afad..922afc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -16,10 +16,14 @@
 
 package com.android.systemui.statusbar.chips.call.ui.viewmodel
 
+import android.app.PendingIntent
+import android.content.ComponentName
 import android.content.Context
 import android.view.View
 import com.android.internal.jank.Cuj
 import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.ComposableControllerFactory
+import com.android.systemui.animation.DelegateTransitionAnimatorController
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
@@ -31,6 +35,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
 import com.android.systemui.statusbar.chips.StatusBarChipsLog
+import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
 import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
@@ -42,9 +47,14 @@
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+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.first
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.stateIn
 
 /** View model for the ongoing phone call chip shown in the status bar. */
@@ -59,67 +69,168 @@
     private val activityStarter: ActivityStarter,
     @StatusBarChipsLog private val logger: LogBuffer,
 ) : OngoingActivityChipViewModel {
-    override val chip: StateFlow<OngoingActivityChipModel> =
-        interactor.ongoingCallState
-            .map { state ->
-                when (state) {
-                    is OngoingCallModel.NoCall,
-                    is OngoingCallModel.InCallWithVisibleApp -> OngoingActivityChipModel.Inactive()
-                    is OngoingCallModel.InCall -> {
-                        val key = state.notificationKey
-                        val contentDescription = getContentDescription(state.appName)
-                        val icon =
-                            if (state.notificationIconView != null) {
-                                StatusBarConnectedDisplays.assertInLegacyMode()
-                                OngoingActivityChipModel.ChipIcon.StatusBarView(
-                                    state.notificationIconView,
-                                    contentDescription,
-                                )
-                            } else if (StatusBarConnectedDisplays.isEnabled) {
-                                OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(
-                                    state.notificationKey,
-                                    contentDescription,
-                                )
-                            } else {
-                                OngoingActivityChipModel.ChipIcon.SingleColorIcon(phoneIcon)
-                            }
+    /** The transition cookie used to register and unregister launch and return animations. */
+    private val cookie =
+        ActivityTransitionAnimator.TransitionCookie("${CallChipViewModel::class.java}")
 
-                        val colors = ColorsModel.AccentThemed
+    /**
+     * Used internally to determine when a launch or return animation is in progress, as these
+     * require special handling.
+     */
+    private val transitionState: MutableStateFlow<TransitionState> =
+        MutableStateFlow(TransitionState.NoTransition)
 
-                        // This block mimics OngoingCallController#updateChip.
-                        if (state.startTimeMs <= 0L) {
-                            // If the start time is invalid, don't show a timer and show just an
-                            // icon. See b/192379214.
-                            OngoingActivityChipModel.Active.IconOnly(
-                                key = key,
-                                icon = icon,
-                                colors = colors,
-                                onClickListenerLegacy = getOnClickListener(state),
-                                clickBehavior = getClickBehavior(state),
+    // Since we're combining the chip state and the transition state flows, getting the old value by
+    // using [pairwise()] would confuse things. This is because if the calculation is triggered by
+    // a change in transition state, the chip state will still show the previous and current values,
+    // making it difficult to figure out what actually changed. Instead we cache the old value here,
+    // so that at each update we can keep track of what actually changed.
+    private var latestState: OngoingCallModel = OngoingCallModel.NoCall
+    private var latestTransitionState: TransitionState = TransitionState.NoTransition
+
+    private val chipWithReturnAnimation: StateFlow<OngoingActivityChipModel> =
+        if (StatusBarChipsReturnAnimations.isEnabled) {
+            combine(interactor.ongoingCallState, transitionState) { newState, newTransitionState ->
+                    val oldState = latestState
+                    latestState = newState
+                    val oldTransitionState = latestTransitionState
+                    latestTransitionState = newTransitionState
+
+                    logger.log(
+                        TAG,
+                        LogLevel.DEBUG,
+                        {},
+                        {
+                            "Call chip state updated: oldState=$oldState newState=$newState " +
+                                "oldTransitionState=$oldTransitionState " +
+                                "newTransitionState=$newTransitionState"
+                        },
+                    )
+
+                    when (newState) {
+                        is OngoingCallModel.NoCall ->
+                            OngoingActivityChipModel.Inactive(
+                                transitionManager = getTransitionManager(newState)
                             )
-                        } else {
-                            val startTimeInElapsedRealtime =
-                                state.startTimeMs - systemClock.currentTimeMillis() +
-                                    systemClock.elapsedRealtime()
-                            OngoingActivityChipModel.Active.Timer(
-                                key = key,
-                                icon = icon,
-                                colors = colors,
-                                startTimeMs = startTimeInElapsedRealtime,
-                                onClickListenerLegacy = getOnClickListener(state),
-                                clickBehavior = getClickBehavior(state),
+
+                        is OngoingCallModel.InCall ->
+                            prepareChip(
+                                newState,
+                                systemClock,
+                                isHidden =
+                                    shouldChipBeHidden(
+                                        oldState = oldState,
+                                        newState = newState,
+                                        oldTransitionState = oldTransitionState,
+                                        newTransitionState = newTransitionState,
+                                    ),
+                                transitionState = newTransitionState,
                             )
-                        }
                     }
                 }
-            }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Inactive())
-
-    private fun getOnClickListener(state: OngoingCallModel.InCall): View.OnClickListener? {
-        if (state.intent == null) {
-            return null
+                .stateIn(
+                    scope,
+                    SharingStarted.WhileSubscribed(),
+                    OngoingActivityChipModel.Inactive(),
+                )
+        } else {
+            MutableStateFlow(OngoingActivityChipModel.Inactive()).asStateFlow()
         }
 
+    private val chipLegacy: StateFlow<OngoingActivityChipModel> =
+        if (!StatusBarChipsReturnAnimations.isEnabled) {
+            interactor.ongoingCallState
+                .map { state ->
+                    when (state) {
+                        is OngoingCallModel.NoCall -> OngoingActivityChipModel.Inactive()
+                        is OngoingCallModel.InCall ->
+                            if (state.isAppVisible) {
+                                OngoingActivityChipModel.Inactive()
+                            } else {
+                                prepareChip(state, systemClock, isHidden = false)
+                            }
+                    }
+                }
+                .stateIn(
+                    scope,
+                    SharingStarted.WhileSubscribed(),
+                    OngoingActivityChipModel.Inactive(),
+                )
+        } else {
+            MutableStateFlow(OngoingActivityChipModel.Inactive()).asStateFlow()
+        }
+
+    override val chip: StateFlow<OngoingActivityChipModel> =
+        if (StatusBarChipsReturnAnimations.isEnabled) {
+            chipWithReturnAnimation
+        } else {
+            chipLegacy
+        }
+
+    /**
+     * The controller factory that the call chip uses to register and unregister its transition
+     * animations.
+     */
+    private var transitionControllerFactory: ComposableControllerFactory? = null
+
+    /** Builds an [OngoingActivityChipModel.Active] from all the relevant information. */
+    private fun prepareChip(
+        state: OngoingCallModel.InCall,
+        systemClock: SystemClock,
+        isHidden: Boolean,
+        transitionState: TransitionState = TransitionState.NoTransition,
+    ): OngoingActivityChipModel.Active {
+        val key = state.notificationKey
+        val contentDescription = getContentDescription(state.appName)
+        val icon =
+            if (state.notificationIconView != null) {
+                StatusBarConnectedDisplays.assertInLegacyMode()
+                OngoingActivityChipModel.ChipIcon.StatusBarView(
+                    state.notificationIconView,
+                    contentDescription,
+                )
+            } else if (StatusBarConnectedDisplays.isEnabled) {
+                OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(
+                    state.notificationKey,
+                    contentDescription,
+                )
+            } else {
+                OngoingActivityChipModel.ChipIcon.SingleColorIcon(phoneIcon)
+            }
+
+        val colors = ColorsModel.AccentThemed
+
+        // This block mimics OngoingCallController#updateChip.
+        if (state.startTimeMs <= 0L) {
+            // If the start time is invalid, don't show a timer and show just an icon.
+            // See b/192379214.
+            return OngoingActivityChipModel.Active.IconOnly(
+                key = key,
+                icon = icon,
+                colors = colors,
+                onClickListenerLegacy = getOnClickListener(state.intent),
+                clickBehavior = getClickBehavior(state.intent),
+                isHidden = isHidden,
+                transitionManager = getTransitionManager(state, transitionState),
+            )
+        } else {
+            val startTimeInElapsedRealtime =
+                state.startTimeMs - systemClock.currentTimeMillis() + systemClock.elapsedRealtime()
+            return OngoingActivityChipModel.Active.Timer(
+                key = key,
+                icon = icon,
+                colors = colors,
+                startTimeMs = startTimeInElapsedRealtime,
+                onClickListenerLegacy = getOnClickListener(state.intent),
+                clickBehavior = getClickBehavior(state.intent),
+                isHidden = isHidden,
+                transitionManager = getTransitionManager(state, transitionState),
+            )
+        }
+    }
+
+    private fun getOnClickListener(intent: PendingIntent?): View.OnClickListener? {
+        if (intent == null) return null
         return View.OnClickListener { view ->
             StatusBarChipsModernization.assertInLegacyMode()
             logger.log(TAG, LogLevel.INFO, {}, { "Chip clicked" })
@@ -127,7 +238,7 @@
                 view.requireViewById<ChipBackgroundContainer>(R.id.ongoing_activity_chip_background)
             // This mimics OngoingCallController#updateChipClickListener.
             activityStarter.postStartActivityDismissingKeyguard(
-                state.intent,
+                intent,
                 ActivityTransitionAnimator.Controller.fromView(
                     backgroundView,
                     Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
@@ -136,23 +247,30 @@
         }
     }
 
-    private fun getClickBehavior(
-        state: OngoingCallModel.InCall
-    ): OngoingActivityChipModel.ClickBehavior =
-        if (state.intent == null) {
+    private fun getClickBehavior(intent: PendingIntent?): OngoingActivityChipModel.ClickBehavior =
+        if (intent == null) {
             OngoingActivityChipModel.ClickBehavior.None
         } else {
             OngoingActivityChipModel.ClickBehavior.ExpandAction(
                 onClick = { expandable ->
                     StatusBarChipsModernization.unsafeAssertInNewMode()
                     val animationController =
-                        expandable.activityTransitionController(
-                            Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP
-                        )
-                    activityStarter.postStartActivityDismissingKeyguard(
-                        state.intent,
-                        animationController,
-                    )
+                        if (
+                            !StatusBarChipsReturnAnimations.isEnabled ||
+                                transitionControllerFactory == null
+                        ) {
+                            expandable.activityTransitionController(
+                                Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP
+                            )
+                        } else {
+                            // When return animations are enabled, we use a long-lived registration
+                            // with controllers created on-demand by the animation library instead
+                            // of explicitly creating one at the time of the click. By not passing
+                            // a controller here, we let the framework do its work. Otherwise, the
+                            // explicit controller would take precedence and override the other one.
+                            null
+                        }
+                    activityStarter.postStartActivityDismissingKeyguard(intent, animationController)
                 }
             )
         }
@@ -168,6 +286,124 @@
         )
     }
 
+    private fun getTransitionManager(
+        state: OngoingCallModel,
+        transitionState: TransitionState = TransitionState.NoTransition,
+    ): OngoingActivityChipModel.TransitionManager? {
+        if (!StatusBarChipsReturnAnimations.isEnabled) return null
+        return if (state is OngoingCallModel.NoCall) {
+            OngoingActivityChipModel.TransitionManager(
+                unregisterTransition = { activityStarter.unregisterTransition(cookie) }
+            )
+        } else {
+            val component = (state as OngoingCallModel.InCall).intent?.intent?.component
+            if (component != null) {
+                val factory = getTransitionControllerFactory(component)
+                OngoingActivityChipModel.TransitionManager(
+                    factory,
+                    registerTransition = {
+                        activityStarter.registerTransition(cookie, factory, scope)
+                    },
+                    // Make the chip invisible at the beginning of the return transition to avoid
+                    // it flickering.
+                    hideChipForTransition = transitionState is TransitionState.ReturnRequested,
+                )
+            } else {
+                // Without a component we can't instantiate a controller factory, and without a
+                // factory registering an animation is impossible. In this case, the transition
+                // manager is empty and inert.
+                OngoingActivityChipModel.TransitionManager()
+            }
+        }
+    }
+
+    private fun getTransitionControllerFactory(
+        component: ComponentName
+    ): ComposableControllerFactory {
+        var factory = transitionControllerFactory
+        if (factory?.component == component) return factory
+
+        factory =
+            object :
+                ComposableControllerFactory(
+                    cookie,
+                    component,
+                    launchCujType = Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+                ) {
+                override suspend fun createController(
+                    forLaunch: Boolean
+                ): ActivityTransitionAnimator.Controller {
+                    transitionState.value =
+                        if (forLaunch) {
+                            TransitionState.LaunchRequested
+                        } else {
+                            TransitionState.ReturnRequested
+                        }
+
+                    val controller =
+                        expandable
+                            .mapNotNull {
+                                it?.activityTransitionController(
+                                    launchCujType,
+                                    cookie,
+                                    component,
+                                    returnCujType,
+                                    isEphemeral = false,
+                                )
+                            }
+                            .first()
+
+                    return object : DelegateTransitionAnimatorController(controller) {
+                        override val isLaunching: Boolean
+                            get() = forLaunch
+
+                        override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
+                            delegate.onTransitionAnimationStart(isExpandingFullyAbove)
+                            transitionState.value =
+                                if (isLaunching) {
+                                    TransitionState.Launching
+                                } else {
+                                    TransitionState.Returning
+                                }
+                        }
+
+                        override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
+                            delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
+                            transitionState.value = TransitionState.NoTransition
+                        }
+
+                        override fun onTransitionAnimationCancelled(
+                            newKeyguardOccludedState: Boolean?
+                        ) {
+                            delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
+                            transitionState.value = TransitionState.NoTransition
+                        }
+                    }
+                }
+            }
+
+        transitionControllerFactory = factory
+        return factory
+    }
+
+    /** Define the current state of this chip's transition animation. */
+    private sealed interface TransitionState {
+        /** Idle. */
+        data object NoTransition : TransitionState
+
+        /** Launch animation has been requested but hasn't started yet. */
+        data object LaunchRequested : TransitionState
+
+        /** Launch animation in progress. */
+        data object Launching : TransitionState
+
+        /** Return animation has been requested but hasn't started yet. */
+        data object ReturnRequested : TransitionState
+
+        /** Return animation in progress. */
+        data object Returning : TransitionState
+    }
+
     companion object {
         private val phoneIcon =
             Icon.Resource(
@@ -175,5 +411,42 @@
                 ContentDescription.Resource(R.string.ongoing_call_content_description),
             )
         private val TAG = "CallVM".pad()
+
+        /** Determines whether or not an active call chip should be hidden. */
+        private fun shouldChipBeHidden(
+            oldState: OngoingCallModel,
+            newState: OngoingCallModel.InCall,
+            oldTransitionState: TransitionState,
+            newTransitionState: TransitionState,
+        ): Boolean {
+            // The app is in the background and no transitions are ongoing (during transitions,
+            // [isAppVisible] must always be true). Show the chip.
+            if (!newState.isAppVisible) return false
+
+            // The call has just started and is visible. Hide the chip.
+            if (oldState is OngoingCallModel.NoCall) return true
+
+            // The state went from the app not being visible to visible. This happens when the chip
+            // is tapped and a launch animation is about to start. Keep the chip showing.
+            if (!(oldState as OngoingCallModel.InCall).isAppVisible) return false
+
+            // The app was and remains visible, but the transition state has changed. A launch or
+            // return animation has been requested or is ongoing. Keep the chip showing.
+            if (
+                newTransitionState is TransitionState.LaunchRequested ||
+                    newTransitionState is TransitionState.Launching ||
+                    newTransitionState is TransitionState.ReturnRequested ||
+                    newTransitionState is TransitionState.Returning
+            ) {
+                return false
+            }
+
+            // The app was and remains visible, so we generally want to hide the chip. The only
+            // exception is if a return transition has just ended. In this case, the transition
+            // state changes shortly before the app visibility does. If we hide the chip between
+            // these two updates, this results in a flicker. We bridge the gap by keeping the chip
+            // showing.
+            return oldTransitionState != TransitionState.Returning
+        }
     }
 }
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 2fe6270..1a802d6 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
@@ -141,7 +141,7 @@
             // When we're promoting notifications automatically, the `when` time set on the
             // notification will likely just be set to the current time, which would cause the chip
             // to always show "now". We don't want early testers to get that experience since it's
-            // not what will happen at launch, so just don't show any time.
+            // not what will happen at launch, so just don't show any time.onometerstate
             return OngoingActivityChipModel.Active.IconOnly(
                 this.key,
                 icon,
@@ -194,14 +194,14 @@
                 }
             }
             is PromotedNotificationContentModel.When.Chronometer -> {
-                // TODO(b/364653005): Check isCountDown and support CountDown.
                 return OngoingActivityChipModel.Active.Timer(
                     this.key,
                     icon,
                     colors,
                     startTimeMs = this.promotedContent.time.elapsedRealtimeMillis,
-                    onClickListenerLegacy,
-                    clickBehavior,
+                    isEventInFuture = this.promotedContent.time.isCountDown,
+                    onClickListenerLegacy = onClickListenerLegacy,
+                    clickBehavior = clickBehavior,
                 )
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt
index 2032ec8..1eb46d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.chips.ui.binder
 
+import android.annotation.ElapsedRealtimeLong
 import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
 
 object ChipChronometerBinder {
@@ -25,9 +26,11 @@
      * @param startTimeMs the time this event started, relative to
      *   [com.android.systemui.util.time.SystemClock.elapsedRealtime]. See
      *   [android.widget.Chronometer.setBase].
+     * @param isCountDown see [android.widget.Chronometer.setCountDown].
      */
-    fun bind(startTimeMs: Long, view: ChipChronometer) {
+    fun bind(@ElapsedRealtimeLong startTimeMs: Long, isCountDown: Boolean, view: ChipChronometer) {
         view.base = startTimeMs
+        view.isCountDown = isCountDown
         view.start()
     }
 }
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 02e4ba4..77e0dde 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
@@ -19,6 +19,7 @@
 import android.annotation.IdRes
 import android.content.Context
 import android.content.res.ColorStateList
+import android.graphics.Typeface
 import android.graphics.drawable.GradientDrawable
 import android.view.View
 import android.view.ViewGroup
@@ -27,6 +28,7 @@
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.annotation.UiThread
+import com.android.systemui.FontStyles
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
 import com.android.systemui.common.ui.binder.IconViewBinder
@@ -170,6 +172,16 @@
         forceLayout()
     }
 
+    /** Updates the typefaces for any text shown in the chip. */
+    fun updateTypefaces(binding: OngoingActivityChipViewBinding) {
+        binding.timeView.typeface =
+            Typeface.create(FontStyles.GSF_LABEL_LARGE_EMPHASIZED, Typeface.NORMAL)
+        binding.textView.typeface =
+            Typeface.create(FontStyles.GSF_LABEL_LARGE_EMPHASIZED, Typeface.NORMAL)
+        binding.shortTimeDeltaView.typeface =
+            Typeface.create(FontStyles.GSF_LABEL_LARGE_EMPHASIZED, Typeface.NORMAL)
+    }
+
     private fun setChipIcon(
         chipModel: OngoingActivityChipModel.Active,
         backgroundView: ChipBackgroundContainer,
@@ -303,7 +315,11 @@
                 chipShortTimeDeltaView.visibility = View.GONE
             }
             is OngoingActivityChipModel.Active.Timer -> {
-                ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
+                ChipChronometerBinder.bind(
+                    chipModel.startTimeMs,
+                    chipModel.isEventInFuture,
+                    chipTimeView,
+                )
                 chipTimeView.visibility = View.VISIBLE
 
                 chipTextView.visibility = View.GONE
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
index 5242fea..fa8d256 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.chips.ui.compose
 
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
@@ -27,11 +28,13 @@
 import androidx.compose.ui.node.LayoutModifierNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.text.TextMeasurer
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.rememberTextMeasurer
 import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.constrain
 import androidx.compose.ui.unit.dp
@@ -42,14 +45,16 @@
 import com.android.systemui.statusbar.chips.ui.viewmodel.rememberTimeRemainingState
 import kotlin.math.min
 
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 fun ChipContent(viewModel: OngoingActivityChipModel.Active, modifier: Modifier = Modifier) {
     val context = LocalContext.current
+    val density = LocalDensity.current
     val isTextOnly = viewModel.icon == null
     val hasEmbeddedIcon =
         viewModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView ||
             viewModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon
-    val textStyle = MaterialTheme.typography.labelLarge
+    val textStyle = MaterialTheme.typography.labelLargeEmphasized
     val textColor = Color(viewModel.colors.text(context))
     val maxTextWidth = dimensionResource(id = R.dimen.ongoing_activity_chip_max_text_width)
     val startPadding =
@@ -69,25 +74,31 @@
     val textMeasurer = rememberTextMeasurer()
     when (viewModel) {
         is OngoingActivityChipModel.Active.Timer -> {
-            val timerState = rememberChronometerState(startTimeMillis = viewModel.startTimeMs)
-            val text = timerState.currentTimeText
-            Text(
-                text = text,
-                style = textStyle,
-                color = textColor,
-                softWrap = false,
-                modifier =
-                    modifier
-                        .hideTextIfDoesNotFit(
-                            text = text,
-                            textStyle = textStyle,
-                            textMeasurer = textMeasurer,
-                            maxTextWidth = maxTextWidth,
-                            startPadding = startPadding,
-                            endPadding = endPadding,
-                        )
-                        .neverDecreaseWidth(),
-            )
+            val timerState =
+                rememberChronometerState(
+                    eventTimeMillis = viewModel.startTimeMs,
+                    isCountDown = viewModel.isEventInFuture,
+                    timeSource = viewModel.timeSource,
+                )
+            timerState.currentTimeText?.let { text ->
+                Text(
+                    text = text,
+                    style = textStyle,
+                    color = textColor,
+                    softWrap = false,
+                    modifier =
+                        modifier
+                            .hideTextIfDoesNotFit(
+                                text = text,
+                                textStyle = textStyle,
+                                textMeasurer = textMeasurer,
+                                maxTextWidth = maxTextWidth,
+                                startPadding = startPadding,
+                                endPadding = endPadding,
+                            )
+                            .neverDecreaseWidth(density),
+                )
+            }
         }
 
         is OngoingActivityChipModel.Active.Countdown -> {
@@ -97,7 +108,7 @@
                 style = textStyle,
                 color = textColor,
                 softWrap = false,
-                modifier = modifier.neverDecreaseWidth(),
+                modifier = modifier.neverDecreaseWidth(density),
             )
         }
 
@@ -150,23 +161,31 @@
 }
 
 /** A modifier that ensures the width of the content only increases and never decreases. */
-private fun Modifier.neverDecreaseWidth(): Modifier {
-    return this.then(NeverDecreaseWidthElement)
+private fun Modifier.neverDecreaseWidth(density: Density): Modifier {
+    return this.then(NeverDecreaseWidthElement(density))
 }
 
-private data object NeverDecreaseWidthElement : ModifierNodeElement<NeverDecreaseWidthNode>() {
+private data class NeverDecreaseWidthElement(val density: Density) :
+    ModifierNodeElement<NeverDecreaseWidthNode>() {
     override fun create(): NeverDecreaseWidthNode {
         return NeverDecreaseWidthNode()
     }
 
     override fun update(node: NeverDecreaseWidthNode) {
-        error("This should never be called")
+        node.onDensityUpdated()
     }
 }
 
 private class NeverDecreaseWidthNode : Modifier.Node(), LayoutModifierNode {
     private var minWidth = 0
 
+    fun onDensityUpdated() {
+        // When the font or display size changes, we should re-determine what our minWidth is from
+        // scratch (e.g. if the font size decreased, we may be able to take *less* room).
+        // See b/395607413.
+        minWidth = 0
+    }
+
     override fun MeasureScope.measure(
         measurable: Measurable,
         constraints: Constraints,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index d81ea07..74b3f30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -18,24 +18,19 @@
 
 import android.content.res.ColorStateList
 import android.view.ViewGroup
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.widthIn
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
@@ -43,12 +38,12 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.Expandable
-import com.android.compose.modifiers.thenIf
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.common.ui.compose.load
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
 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
@@ -60,32 +55,57 @@
     iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
     modifier: Modifier = Modifier,
 ) {
-    when (val clickBehavior = model.clickBehavior) {
-        is OngoingActivityChipModel.ClickBehavior.ExpandAction -> {
-            // Wrap the chip in an Expandable so we can animate the expand transition.
-            ExpandableChip(
-                color = { Color.Transparent },
-                shape =
-                    RoundedCornerShape(
-                        dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius)
-                    ),
-                modifier = modifier,
-            ) { expandable ->
-                ChipBody(model, iconViewStore, onClick = { clickBehavior.onClick(expandable) })
-            }
-        }
-        is OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification -> {
-            ChipBody(
-                model,
-                iconViewStore,
-                onClick = { clickBehavior.onClick() },
-                modifier = modifier,
-            )
+    val contentDescription =
+        when (val icon = model.icon) {
+            is OngoingActivityChipModel.ChipIcon.StatusBarView -> icon.contentDescription.load()
+            is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon ->
+                icon.contentDescription.load()
+            is OngoingActivityChipModel.ChipIcon.SingleColorIcon,
+            null -> null
         }
 
-        is OngoingActivityChipModel.ClickBehavior.None -> {
-            ChipBody(model, iconViewStore, modifier = modifier)
+    val borderStroke =
+        model.colors.outline(LocalContext.current)?.let {
+            BorderStroke(dimensionResource(R.dimen.ongoing_activity_chip_outline_width), Color(it))
         }
+
+    val onClick =
+        when (val clickBehavior = model.clickBehavior) {
+            is OngoingActivityChipModel.ClickBehavior.ExpandAction -> { expandable: Expandable ->
+                    clickBehavior.onClick(expandable)
+                }
+            is OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification -> { _ ->
+                    clickBehavior.onClick()
+                }
+            is OngoingActivityChipModel.ClickBehavior.None -> null
+        }
+
+    Expandable(
+        color = Color(model.colors.background(LocalContext.current).defaultColor),
+        shape =
+            RoundedCornerShape(dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius)),
+        modifier =
+            modifier
+                .height(dimensionResource(R.dimen.ongoing_appops_chip_height))
+                .semantics {
+                    if (contentDescription != null) {
+                        this.contentDescription = contentDescription
+                    }
+                }
+                .graphicsLayer(
+                    alpha =
+                        if (model.transitionManager?.hideChipForTransition == true) {
+                            0f
+                        } else {
+                            1f
+                        }
+                ),
+        borderStroke = borderStroke,
+        onClick = onClick,
+        useModifierBasedImplementation = StatusBarChipsReturnAnimations.isEnabled,
+        transitionControllerFactory = model.transitionManager?.controllerFactory,
+    ) {
+        ChipBody(model, iconViewStore, isClickable = onClick != null)
     }
 }
 
@@ -93,22 +113,13 @@
 private fun ChipBody(
     model: OngoingActivityChipModel.Active,
     iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
+    isClickable: Boolean,
     modifier: Modifier = Modifier,
-    onClick: (() -> Unit)? = null,
 ) {
-    val context = LocalContext.current
-    val isClickable = onClick != null
     val hasEmbeddedIcon =
         model.icon is OngoingActivityChipModel.ChipIcon.StatusBarView ||
             model.icon is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon
-    val contentDescription =
-        when (val icon = model.icon) {
-            is OngoingActivityChipModel.ChipIcon.StatusBarView -> icon.contentDescription.load()
-            is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon ->
-                icon.contentDescription.load()
-            is OngoingActivityChipModel.ChipIcon.SingleColorIcon -> null
-            null -> null
-        }
+
     val chipSidePadding = dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
     val minWidth =
         if (isClickable) {
@@ -119,68 +130,38 @@
             dimensionResource(id = R.dimen.ongoing_activity_chip_min_text_width) + chipSidePadding
         }
 
-    val outline = model.colors.outline(context)
-    val outlineWidth = dimensionResource(R.dimen.ongoing_activity_chip_outline_width)
-
-    val shape =
-        RoundedCornerShape(dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius))
-
-    // Use a Box with `fillMaxHeight` to create a larger click surface for the chip. The visible
-    // height of the chip is determined by the height of the background of the Row below.
-    Box(
-        contentAlignment = Alignment.Center,
+    Row(
+        horizontalArrangement = Arrangement.Center,
+        verticalAlignment = Alignment.CenterVertically,
         modifier =
             modifier
                 .fillMaxHeight()
-                .clickable(enabled = isClickable, onClick = onClick ?: {})
-                .semantics {
-                    if (contentDescription != null) {
-                        this.contentDescription = contentDescription
-                    }
-                },
-    ) {
-        Row(
-            horizontalArrangement = Arrangement.Center,
-            verticalAlignment = Alignment.CenterVertically,
-            modifier =
-                Modifier.height(dimensionResource(R.dimen.ongoing_appops_chip_height))
-                    .thenIf(isClickable) { Modifier.widthIn(min = minWidth) }
-                    .layout { measurable, constraints ->
-                        val placeable = measurable.measure(constraints)
-                        layout(placeable.width, placeable.height) {
-                            if (constraints.maxWidth >= minWidth.roundToPx()) {
-                                placeable.place(0, 0)
-                            }
+                .layout { measurable, constraints ->
+                    val placeable = measurable.measure(constraints)
+                    layout(placeable.width, placeable.height) {
+                        if (constraints.maxWidth >= minWidth.roundToPx()) {
+                            placeable.place(0, 0)
                         }
                     }
-                    .background(Color(model.colors.background(context).defaultColor), shape = shape)
-                    .thenIf(outline != null) {
-                        Modifier.border(
-                            width = outlineWidth,
-                            color = Color(outline!!),
-                            shape = shape,
-                        )
-                    }
-                    .padding(
-                        horizontal =
-                            if (hasEmbeddedIcon) {
-                                dimensionResource(
-                                    R.dimen
-                                        .ongoing_activity_chip_side_padding_for_embedded_padding_icon
-                                )
-                            } else {
-                                dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
-                            }
-                    ),
-        ) {
-            model.icon?.let {
-                ChipIcon(viewModel = it, iconViewStore = iconViewStore, colors = model.colors)
-            }
+                }
+                .padding(
+                    horizontal =
+                        if (hasEmbeddedIcon) {
+                            dimensionResource(
+                                R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
+                            )
+                        } else {
+                            dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
+                        }
+                ),
+    ) {
+        model.icon?.let {
+            ChipIcon(viewModel = it, iconViewStore = iconViewStore, colors = model.colors)
+        }
 
-            val isIconOnly = model is OngoingActivityChipModel.Active.IconOnly
-            if (!isIconOnly) {
-                ChipContent(viewModel = model)
-            }
+        val isIconOnly = model is OngoingActivityChipModel.Active.IconOnly
+        if (!isIconOnly) {
+            ChipContent(viewModel = model)
         }
     }
 }
@@ -228,6 +209,7 @@
     iconFactory: () -> StatusBarIconView?,
 ) {
     val context = LocalContext.current
+    val colorTintList = ColorStateList.valueOf(colors.text(context))
 
     val iconSizePx =
         context.resources.getDimensionPixelSize(
@@ -238,18 +220,8 @@
         factory = { _ ->
             iconFactory.invoke()?.apply {
                 layoutParams = ViewGroup.LayoutParams(iconSizePx, iconSizePx)
-                imageTintList = ColorStateList.valueOf(colors.text(context))
             } ?: throw IllegalStateException("Missing StatusBarIconView for $notificationKey")
         },
+        update = { iconView -> iconView.imageTintList = colorTintList },
     )
 }
-
-@Composable
-private fun ExpandableChip(
-    color: () -> Color,
-    shape: Shape,
-    modifier: Modifier = Modifier,
-    content: @Composable (Expandable) -> Unit,
-) {
-    Expandable(color = color(), shape = shape, modifier = modifier.clip(shape)) { content(it) }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
index 7080c34..700e6d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChips.kt
@@ -21,12 +21,14 @@
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.key
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.dimensionResource
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.StatusBarChipsReturnAnimations
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
 
@@ -36,18 +38,30 @@
     iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
     modifier: Modifier = Modifier,
 ) {
-    Row(
-        modifier =
-            modifier
-                .fillMaxHeight()
-                .padding(start = dimensionResource(R.dimen.ongoing_activity_chip_margin_start)),
-        verticalAlignment = Alignment.CenterVertically,
-        horizontalArrangement =
-            Arrangement.spacedBy(dimensionResource(R.dimen.ongoing_activity_chip_margin_start)),
-    ) {
-        chips.active
-            .filter { !it.isHidden }
-            .forEach {
+    if (StatusBarChipsReturnAnimations.isEnabled) {
+        SideEffect {
+            // Active chips must always be capable of animating to/from activities, even when they
+            // are hidden. Therefore we always register their transitions.
+            for (chip in chips.active) chip.transitionManager?.registerTransition?.invoke()
+            // Inactive chips and chips in the overflow are never shown, so they must not have any
+            // registered transition.
+            for (chip in chips.overflow) chip.transitionManager?.unregisterTransition?.invoke()
+            for (chip in chips.inactive) chip.transitionManager?.unregisterTransition?.invoke()
+        }
+    }
+
+    val shownChips = chips.active.filter { !it.isHidden }
+    if (shownChips.isNotEmpty()) {
+        Row(
+            modifier =
+                modifier
+                    .fillMaxHeight()
+                    .padding(start = dimensionResource(R.dimen.ongoing_activity_chip_margin_start)),
+            verticalAlignment = Alignment.CenterVertically,
+            horizontalArrangement =
+                Arrangement.spacedBy(dimensionResource(R.dimen.ongoing_activity_chip_margin_start)),
+        ) {
+            shownChips.forEach {
                 key(it.key) {
                     OngoingActivityChip(
                         model = it,
@@ -56,5 +70,6 @@
                     )
                 }
             }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
index 4954cb0..b268376 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.content.res.ColorStateList
 import androidx.annotation.ColorInt
-import com.android.settingslib.Utils
 import com.android.systemui.res.R
 
 /** Model representing how the chip in the status bar should be colored. */
@@ -34,14 +33,14 @@
     @ColorInt fun outline(context: Context): Int?
 
     /** The chip should match the theme's primary accent color. */
-    // TODO(b/347717946): The chip's color isn't getting updated when the user switches theme, it
-    // only gets updated when a different configuration change happens, like a rotation.
     data object AccentThemed : ColorsModel {
         override fun background(context: Context): ColorStateList =
-            Utils.getColorAttr(context, com.android.internal.R.attr.colorAccent)
+            ColorStateList.valueOf(
+                context.getColor(com.android.internal.R.color.materialColorPrimaryFixedDim)
+            )
 
         override fun text(context: Context) =
-            Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
+            context.getColor(com.android.internal.R.color.materialColorOnPrimaryFixed)
 
         override fun outline(context: Context) = null
     }
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 0cfc321..364e665 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
@@ -16,12 +16,17 @@
 
 package com.android.systemui.statusbar.chips.ui.model
 
+import android.annotation.CurrentTimeMillisLong
+import android.annotation.ElapsedRealtimeLong
+import android.os.SystemClock
 import android.view.View
+import com.android.systemui.animation.ComposableControllerFactory
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ui.viewmodel.TimeSource
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 
 /** Model representing the display of an ongoing activity as a chip in the status bar. */
@@ -29,6 +34,9 @@
     /** Condensed name representing the model, used for logs. */
     abstract val logName: String
 
+    /** Object used to manage the behavior of this chip during activity launch and returns. */
+    abstract val transitionManager: TransitionManager?
+
     /**
      * This chip shouldn't be shown.
      *
@@ -36,7 +44,10 @@
      *   animated, and false if that transition should *not* be animated (i.e. the chip view should
      *   immediately disappear).
      */
-    data class Inactive(val shouldAnimate: Boolean = true) : OngoingActivityChipModel() {
+    data class Inactive(
+        val shouldAnimate: Boolean = true,
+        override val transitionManager: TransitionManager? = null,
+    ) : OngoingActivityChipModel() {
         override val logName = "Inactive(anim=$shouldAnimate)"
     }
 
@@ -57,6 +68,7 @@
         open val onClickListenerLegacy: View.OnClickListener?,
         /** Data class that determines how clicks on the chip should be handled. */
         open val clickBehavior: ClickBehavior,
+        override val transitionManager: TransitionManager?,
         /**
          * Whether this chip should be hidden. This can be the case depending on system states (like
          * which apps are in the foreground and whether there is an ongoing transition.
@@ -73,6 +85,7 @@
             override val colors: ColorsModel,
             override val onClickListenerLegacy: View.OnClickListener?,
             override val clickBehavior: ClickBehavior,
+            override val transitionManager: TransitionManager? = null,
             override val isHidden: Boolean = false,
             override val shouldAnimate: Boolean = true,
         ) :
@@ -82,6 +95,7 @@
                 colors,
                 onClickListenerLegacy,
                 clickBehavior,
+                transitionManager,
                 isHidden,
                 shouldAnimate,
             ) {
@@ -102,9 +116,23 @@
              * [ChipChronometer] is based off of elapsed realtime. See
              * [android.widget.Chronometer.setBase].
              */
-            val startTimeMs: Long,
+            @ElapsedRealtimeLong val startTimeMs: Long,
+
+            /**
+             * The [TimeSource] that should be used to track the current time for this timer. Should
+             * be compatible with [startTimeMs].
+             */
+            val timeSource: TimeSource = TimeSource { SystemClock.elapsedRealtime() },
+
+            /**
+             * True if this chip represents an event starting in the future and false if this chip
+             * represents an event that has already started. If true, [startTimeMs] should be in the
+             * future. Otherwise, [startTimeMs] should be in the past.
+             */
+            val isEventInFuture: Boolean = false,
             override val onClickListenerLegacy: View.OnClickListener?,
             override val clickBehavior: ClickBehavior,
+            override val transitionManager: TransitionManager? = null,
             override val isHidden: Boolean = false,
             override val shouldAnimate: Boolean = true,
         ) :
@@ -114,6 +142,7 @@
                 colors,
                 onClickListenerLegacy,
                 clickBehavior,
+                transitionManager,
                 isHidden,
                 shouldAnimate,
             ) {
@@ -129,12 +158,18 @@
             override val icon: ChipIcon,
             override val colors: ColorsModel,
             /**
-             * The time of the event that this chip represents, relative to
-             * [com.android.systemui.util.time.SystemClock.currentTimeMillis].
+             * The time of the event that this chip represents. Relative to
+             * [com.android.systemui.util.time.SystemClock.currentTimeMillis] because that's what's
+             * required by [android.widget.DateTimeView].
+             *
+             * TODO(b/372657935): When the Compose chips are launched, we should convert this to be
+             *   relative to [com.android.systemui.util.time.SystemClock.elapsedRealtime] so that
+             *   this model and the [Timer] model use the same units.
              */
-            val time: Long,
+            @CurrentTimeMillisLong val time: Long,
             override val onClickListenerLegacy: View.OnClickListener?,
             override val clickBehavior: ClickBehavior,
+            override val transitionManager: TransitionManager? = null,
             override val isHidden: Boolean = false,
             override val shouldAnimate: Boolean = true,
         ) :
@@ -144,6 +179,7 @@
                 colors,
                 onClickListenerLegacy,
                 clickBehavior,
+                transitionManager,
                 isHidden,
                 shouldAnimate,
             ) {
@@ -163,6 +199,7 @@
             override val colors: ColorsModel,
             /** The number of seconds until an event is started. */
             val secondsUntilStarted: Long,
+            override val transitionManager: TransitionManager? = null,
             override val isHidden: Boolean = false,
             override val shouldAnimate: Boolean = true,
         ) :
@@ -172,6 +209,7 @@
                 colors,
                 onClickListenerLegacy = null,
                 clickBehavior = ClickBehavior.None,
+                transitionManager,
                 isHidden,
                 shouldAnimate,
             ) {
@@ -187,6 +225,7 @@
             val text: String,
             override val onClickListenerLegacy: View.OnClickListener? = null,
             override val clickBehavior: ClickBehavior,
+            override val transitionManager: TransitionManager? = null,
             override val isHidden: Boolean = false,
             override val shouldAnimate: Boolean = true,
         ) :
@@ -196,6 +235,7 @@
                 colors,
                 onClickListenerLegacy,
                 clickBehavior,
+                transitionManager,
                 isHidden,
                 shouldAnimate,
             ) {
@@ -249,4 +289,23 @@
         /** Clicking the chip will show the heads up notification associated with the chip. */
         data class ShowHeadsUpNotification(val onClick: () -> Unit) : ClickBehavior
     }
+
+    /** Defines the behavior of the chip with respect to activity launch and return transitions. */
+    data class TransitionManager(
+        /** The factory used to create the controllers that animate the chip. */
+        val controllerFactory: ComposableControllerFactory? = null,
+        /**
+         * Used to create a registration for this chip using [controllerFactory]. Must be
+         * idempotent.
+         */
+        val registerTransition: () -> Unit = {},
+        /** Used to remove the existing registration for this chip, if any. */
+        val unregisterTransition: () -> Unit = {},
+        /**
+         * Whether the chip should be made invisible (0 opacity) while still being composed. This is
+         * necessary to avoid flickers at the beginning of return transitions, when the chip must
+         * not be visible but must be composed in order for the animation to start.
+         */
+        val hideChipForTransition: Boolean = false,
+    )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
index 6278978..0fc7f82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChronometerState.kt
@@ -15,7 +15,7 @@
  */
 package com.android.systemui.statusbar.chips.ui.viewmodel
 
-import android.os.SystemClock
+import android.annotation.ElapsedRealtimeLong
 import android.text.format.DateUtils.formatElapsedTime
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -27,6 +27,7 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.compose.LocalLifecycleOwner
 import androidx.lifecycle.repeatOnLifecycle
+import kotlin.math.absoluteValue
 import kotlinx.coroutines.delay
 
 /** Platform-optimized interface for getting current time */
@@ -34,18 +35,50 @@
     fun getCurrentTime(): Long
 }
 
-/** Holds and manages the state for a Chronometer */
-class ChronometerState(private val timeSource: TimeSource, private val startTimeMillis: Long) {
-    private var currentTimeMillis by mutableLongStateOf(0L)
+/**
+ * Holds and manages the state for a Chronometer, which shows a timer in a format like "MM:SS" or
+ * "H:MM:SS".
+ *
+ * If [isEventInFuture] is false, then this Chronometer is counting up from an event that started in
+ * the past, like a phone call that was answered. [eventTimeMillis] represents the time the event
+ * started and the timer will tick up: 04:00, 04:01, ... No timer is shown if [eventTimeMillis] is
+ * in the future and [isEventInFuture] is false.
+ *
+ * If [isEventInFuture] is true, then this Chronometer is counting down to an event that will occur
+ * in the future, like a future meeting. [eventTimeMillis] represents the time the event will occur
+ * and the timer will tick down: 04:00, 03:59, ... No timer is shown if [eventTimeMillis] is in the
+ * past and [isEventInFuture] is true.
+ */
+class ChronometerState(
+    private val timeSource: TimeSource,
+    @ElapsedRealtimeLong private val eventTimeMillis: Long,
+    private val isEventInFuture: Boolean,
+) {
+    private var currentTimeMillis by mutableLongStateOf(timeSource.getCurrentTime())
     private val elapsedTimeMillis: Long
-        get() = maxOf(0L, currentTimeMillis - startTimeMillis)
+        get() =
+            if (isEventInFuture) {
+                eventTimeMillis - currentTimeMillis
+            } else {
+                currentTimeMillis - eventTimeMillis
+            }
 
-    val currentTimeText: String by derivedStateOf { formatElapsedTime(elapsedTimeMillis / 1000) }
+    /**
+     * The current timer string in a format like "MM:SS" or "H:MM:SS", or null if we shouldn't show
+     * the timer string.
+     */
+    val currentTimeText: String? by derivedStateOf {
+        if (elapsedTimeMillis < 0) {
+            null
+        } else {
+            formatElapsedTime(elapsedTimeMillis / 1000)
+        }
+    }
 
     suspend fun run() {
         while (true) {
             currentTimeMillis = timeSource.getCurrentTime()
-            val delaySkewMillis = (currentTimeMillis - startTimeMillis) % 1000L
+            val delaySkewMillis = (eventTimeMillis - currentTimeMillis).absoluteValue % 1000L
             delay(1000L - delaySkewMillis)
         }
     }
@@ -54,13 +87,16 @@
 /** Remember and manage the ChronometerState */
 @Composable
 fun rememberChronometerState(
-    startTimeMillis: Long,
-    timeSource: TimeSource = remember { TimeSource { SystemClock.elapsedRealtime() } },
+    eventTimeMillis: Long,
+    isCountDown: Boolean,
+    timeSource: TimeSource,
 ): ChronometerState {
     val state =
-        remember(timeSource, startTimeMillis) { ChronometerState(timeSource, startTimeMillis) }
+        remember(timeSource, eventTimeMillis, isCountDown) {
+            ChronometerState(timeSource, eventTimeMillis, isCountDown)
+        }
     val lifecycleOwner = LocalLifecycleOwner.current
-    LaunchedEffect(lifecycleOwner, timeSource, startTimeMillis) {
+    LaunchedEffect(lifecycleOwner, timeSource, eventTimeMillis) {
         lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { state.run() }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index eae2c25..8228b55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -555,7 +555,6 @@
                 secondary = DEFAULT_INTERNAL_INACTIVE_MODEL,
             )
 
-        // TODO(b/392886257): Support 3 chips if there's space available.
-        private const val MAX_VISIBLE_CHIPS = 2
+        private const val MAX_VISIBLE_CHIPS = 3
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/TimeRemainingState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/TimeRemainingState.kt
index eb6ebca..803d422 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/TimeRemainingState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/TimeRemainingState.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.chips.ui.viewmodel
 
-import android.os.SystemClock
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.derivedStateOf
@@ -39,7 +38,14 @@
  * Manages state and updates for the duration remaining between now and a given time in the future.
  */
 class TimeRemainingState(private val timeSource: TimeSource, private val futureTimeMillis: Long) {
-    private var durationRemaining by mutableStateOf(Duration.ZERO)
+    // Start with the right duration from the outset so we don't use "now" as an initial value.
+    private var durationRemaining by
+        mutableStateOf(
+            calculateDurationRemaining(
+                currentTimeMillis = timeSource.getCurrentTime(),
+                futureTimeMillis = futureTimeMillis,
+            )
+        )
     private var startTimeMillis: Long = 0
 
     /**
@@ -56,7 +62,11 @@
         while (true) {
             val currentTime = timeSource.getCurrentTime()
             durationRemaining =
-                (futureTimeMillis - currentTime).toDuration(DurationUnit.MILLISECONDS)
+                calculateDurationRemaining(
+                    currentTimeMillis = currentTime,
+                    futureTimeMillis = futureTimeMillis,
+                )
+
             // No need to update if duration is more than 1 minute in the past. Because, we will
             // stop displaying anything.
             if (durationRemaining.inWholeMilliseconds < -1.minutes.inWholeMilliseconds) {
@@ -67,6 +77,13 @@
         }
     }
 
+    private fun calculateDurationRemaining(
+        currentTimeMillis: Long,
+        futureTimeMillis: Long,
+    ): Duration {
+        return (futureTimeMillis - currentTimeMillis).toDuration(DurationUnit.MILLISECONDS)
+    }
+
     private fun calculateNextUpdateDelay(duration: Duration): Long {
         val durationAbsolute = duration.absoluteValue
         return when {
@@ -85,7 +102,7 @@
 @Composable
 fun rememberTimeRemainingState(
     futureTimeMillis: Long,
-    timeSource: TimeSource = remember { TimeSource { SystemClock.elapsedRealtime() } },
+    timeSource: TimeSource = remember { TimeSource { System.currentTimeMillis() } },
 ): TimeRemainingState {
 
     val state =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index 48f0245..4cf456d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -32,25 +32,25 @@
 import com.android.systemui.qs.tiles.InternetTile
 import com.android.systemui.qs.tiles.InternetTileNewImpl
 import com.android.systemui.qs.tiles.NfcTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel
-import com.android.systemui.qs.tiles.impl.airplane.domain.AirplaneModeMapper
 import com.android.systemui.qs.tiles.impl.airplane.domain.interactor.AirplaneModeTileDataInteractor
 import com.android.systemui.qs.tiles.impl.airplane.domain.interactor.AirplaneModeTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
-import com.android.systemui.qs.tiles.impl.internet.domain.InternetTileMapper
+import com.android.systemui.qs.tiles.impl.airplane.ui.mapper.AirplaneModeTileMapper
 import com.android.systemui.qs.tiles.impl.internet.domain.interactor.InternetTileDataInteractor
 import com.android.systemui.qs.tiles.impl.internet.domain.interactor.InternetTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
-import com.android.systemui.qs.tiles.impl.saver.domain.DataSaverTileMapper
+import com.android.systemui.qs.tiles.impl.internet.ui.mapper.InternetTileMapper
 import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileDataInteractor
 import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.impl.saver.ui.mapper.DataSaverTileMapper
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
@@ -161,10 +161,10 @@
         @StringKey(AIRPLANE_MODE_TILE_SPEC)
         fun provideAirplaneModeTileViewModel(
             factory: QSTileViewModelFactory.Static<AirplaneModeTileModel>,
-            mapper: AirplaneModeMapper,
+            mapper: AirplaneModeTileMapper,
             stateInteractor: AirplaneModeTileDataInteractor,
             userActionInteractor: AirplaneModeTileUserActionInteractor,
-            internetDetailsViewModelFactory: InternetDetailsViewModel.Factory
+            internetDetailsViewModelFactory: InternetDetailsViewModel.Factory,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(AIRPLANE_MODE_TILE_SPEC),
@@ -197,7 +197,7 @@
             factory: QSTileViewModelFactory.Static<DataSaverTileModel>,
             mapper: DataSaverTileMapper,
             stateInteractor: DataSaverTileDataInteractor,
-            userActionInteractor: DataSaverTileUserActionInteractor
+            userActionInteractor: DataSaverTileUserActionInteractor,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(DATA_SAVER_TILE_SPEC),
@@ -230,7 +230,7 @@
             mapper: InternetTileMapper,
             stateInteractor: InternetTileDataInteractor,
             userActionInteractor: InternetTileUserActionInteractor,
-            internetDetailsViewModelFactory: InternetDetailsViewModel.Factory
+            internetDetailsViewModelFactory: InternetDetailsViewModel.Factory,
         ): QSTileViewModel =
             factory.create(
                 TileSpec.create(INTERNET_TILE_SPEC),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 434eb7d..a7929ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -33,6 +33,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpHandler;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
@@ -44,7 +45,6 @@
 import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationClickNotifier;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
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 7fa9f0e..a658e1d 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
@@ -267,7 +267,8 @@
                         if (StatusBarChipsModernization.isEnabled) {
                             ongoingProcessRequiresStatusBarVisible
                         } else {
-                            ongoingCallStateLegacy is OngoingCallModel.InCall
+                            ongoingCallStateLegacy is OngoingCallModel.InCall &&
+                                !ongoingCallStateLegacy.isAppVisible
                         }
                     val statusBarMode =
                         toBarMode(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
index 3168a22..cb1002a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
@@ -17,14 +17,14 @@
 package com.android.systemui.statusbar.data.repository
 
 import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import com.android.app.displaylib.PerDisplayInstanceProvider
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.app.displaylib.SingleInstanceRepositoryImpl
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.common.ui.ConfigurationStateImpl
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
-import com.android.systemui.display.data.repository.PerDisplayInstanceProvider
-import com.android.systemui.display.data.repository.PerDisplayInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayRepository
-import com.android.systemui.display.data.repository.SingleInstanceRepositoryImpl
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import dagger.Lazy
 import dagger.Module
@@ -39,7 +39,6 @@
     private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
     private val factory: ConfigurationStateImpl.Factory,
 ) : PerDisplayInstanceProvider<ConfigurationState> {
-
     override fun createInstance(displayId: Int): ConfigurationState? {
         val displayWindowProperties =
             displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
index aeeb042..e91e977 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
@@ -14,7 +14,7 @@
 
 package com.android.systemui.statusbar.disableflags.data.repository
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.DisplayId
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 8be9e41..3bb1ff1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -23,6 +23,7 @@
 
 import com.android.systemui.DejankUtils;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -44,13 +45,20 @@
     private final Optional<Bubbles> mBubblesOptional;
     private final NotificationActivityStarter mNotificationActivityStarter;
 
-    private ExpandableNotificationRow.OnDragSuccessListener mOnDragSuccessListener =
-            new ExpandableNotificationRow.OnDragSuccessListener() {
-                @Override
-                public void onDragSuccess(NotificationEntry entry) {
-                    mNotificationActivityStarter.onDragSuccess(entry);
-                }
-            };
+    private ExpandableNotificationRow.OnDragSuccessListener mOnDragSuccessListener
+            = new ExpandableNotificationRow.OnDragSuccessListener() {
+        @Override
+        public void onDragSuccess(NotificationEntry entry) {
+            NotificationBundleUi.assertInLegacyMode();
+            mNotificationActivityStarter.onDragSuccess(entry);
+        }
+
+        @Override
+        public void onDragSuccess(EntryAdapter entryAdapter) {
+            NotificationBundleUi.isUnexpectedlyInLegacyMode();
+            entryAdapter.onDragSuccess();
+        }
+    };
 
     private NotificationClicker(
             NotificationClickerLogger logger,
@@ -105,7 +113,7 @@
                 mBubblesOptional.get().collapseStack();
             }
         } else {
-            if (!row.getEntry().isBubble() && mBubblesOptional.isPresent()) {
+            if (!row.getEntryLegacy().isBubble() && mBubblesOptional.isPresent()) {
                 mBubblesOptional.get().collapseStack();
             }
         }
@@ -130,7 +138,7 @@
             } else {
                 row.setBubbleClickListener(v ->
                         mNotificationActivityStarter.onNotificationBubbleIconClicked(
-                                row.getEntry()));
+                                row.getEntryLegacy()));
             }
             row.setOnClickListener(this);
             row.setOnDragSuccessListener(mOnDragSuccessListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
index 874a059..8163128 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
@@ -74,7 +74,6 @@
         const val ANIMATION_DURATION_TOP_ROUNDING = 100L
     }
 
-    private val notificationEntry = notification.entry
     private val notificationKey = notification.key
 
     override val isLaunching: Boolean = true
@@ -160,7 +159,7 @@
     private val headsUpNotificationRow: ExpandableNotificationRow?
         get() {
             val pipelineParent = if (NotificationBundleUi.isEnabled)
-                notification.entryAdapter?.parent else notificationEntry.parent
+                notification.entryAdapter?.parent else notification.entryLegacy.parent
             val summaryEntry = (pipelineParent as? GroupEntry)?.summary
             return when {
                 headsUpManager.isHeadsUpEntry(notificationKey) -> notification
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
index 41353b9..f653573 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
@@ -33,7 +33,8 @@
 import com.android.systemui.statusbar.notification.icon.IconPack;
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import kotlinx.coroutines.flow.MutableStateFlow;
@@ -51,10 +52,27 @@
     // TODO (b/389839319): implement the row
     private ExpandableNotificationRow mRow;
 
+    private final List<ListEntry> mChildren = new ArrayList<>();
+
+    private final List<ListEntry> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
+
     public BundleEntry(String key) {
         super(key);
     }
 
+    void addChild(ListEntry child) {
+        mChildren.add(child);
+    }
+
+    @NonNull
+    public List<ListEntry> getChildren() {
+        return mUnmodifiableChildren;
+    }
+
+    /**
+     * @return Null because bundles do not have an associated NotificationEntry.
+     */
+
     @Nullable
     @Override
     public NotificationEntry getRepresentativeEntry() {
@@ -63,17 +81,6 @@
 
     @Nullable
     @Override
-    public NotifSection getSection() {
-        return null;
-    }
-
-    @Override
-    public int getSectionIndex() {
-        return 0;
-    }
-
-    @Nullable
-    @Override
     public PipelineEntry getParent() {
         return null;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
index 64db9df..e743d87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -20,7 +20,9 @@
 import android.content.Context
 import android.os.Build
 import android.service.notification.StatusBarNotification
+import android.util.Log
 import com.android.systemui.statusbar.notification.icon.IconPack
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import kotlinx.coroutines.flow.StateFlow
 
@@ -112,11 +114,42 @@
         return false
     }
 
+    override fun getPeopleNotificationType(): Int {
+        return TYPE_NON_PERSON
+    }
+
+    override fun isPromotedOngoing(): Boolean {
+        return false
+    }
+
     override fun isFullScreenCapable(): Boolean {
         return false
     }
 
+    override fun onDragSuccess() {
+        // do nothing. these should not be draggable
+        Log.wtf(TAG, "onDragSuccess() called")
+    }
+
     override fun onNotificationBubbleIconClicked() {
         // do nothing. these cannot be a bubble
+        Log.wtf(TAG, "onNotificationBubbleIconClicked() called")
+    }
+
+    override fun onNotificationActionClicked() {
+        // do nothing. these have no actions
+        Log.wtf(TAG, "onNotificationActionClicked() called")
+    }
+
+    override fun getDismissState(): NotificationEntry.DismissState {
+        // TODO(b/394483200): setDismissState is only called in NotifCollection so it does not
+        // work on bundles yet
+        return NotificationEntry.DismissState.NOT_DISMISSED
+    }
+
+    override fun onEntryClicked(row: ExpandableNotificationRow) {
+        // TODO(b/396446620): should anything happen when you click on a bundle?
     }
 }
+
+private const val TAG = "BundleEntryAdapter"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 0e75b60..f39bd03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -23,6 +23,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.systemui.statusbar.notification.icon.IconPack;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import kotlinx.coroutines.flow.StateFlow;
@@ -132,13 +133,31 @@
 
     boolean isAmbient();
 
+    @PeopleNotificationIdentifier.Companion.PeopleNotificationType int getPeopleNotificationType();
+
+    /**
+     * Returns whether this row represents promoted ongoing notification.
+     */
+    boolean isPromotedOngoing();
+
     default boolean isFullScreenCapable() {
         return false;
     }
 
+    void onDragSuccess();
+
     /**
      * Process a click on a notification bubble icon
      */
     void onNotificationBubbleIconClicked();
+
+    /**
+     * Processes a click on a notification action
+     */
+    void onNotificationActionClicked();
+
+    NotificationEntry.DismissState getDismissState();
+
+    void onEntryClicked(ExpandableNotificationRow row);
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt
index 779c25a..a516986 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar.notification.collection
 
-import androidx.annotation.VisibleForTesting
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.statusbar.notification.NotificationActivityStarter
 import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.row.NotificationActionClickManager
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
 import javax.inject.Inject
 
@@ -33,6 +33,7 @@
     private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
     private val iconStyleProvider: NotificationIconStyleProvider,
     private val visualStabilityCoordinator: VisualStabilityCoordinator,
+    private val notificationActionClickManager: NotificationActionClickManager,
 ) : EntryAdapterFactory {
     override fun create(entry: PipelineEntry): EntryAdapter {
         return if (entry is NotificationEntry) {
@@ -42,15 +43,11 @@
                 peopleNotificationIdentifier,
                 iconStyleProvider,
                 visualStabilityCoordinator,
+                notificationActionClickManager,
                 entry,
             )
         } else {
             BundleEntryAdapter((entry as BundleEntry))
         }
     }
-
-    @VisibleForTesting
-    fun getNotificationActivityStarter() : NotificationActivityStarter {
-        return notificationActivityStarter
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
index 4a1b956..04dc7d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
@@ -31,8 +31,8 @@
     var parent: PipelineEntry?,
 
     /**
-     * The section that this ListEntry was sorted into. If the child of the group, this will be the
-     * parent's section. Null if not attached to the list.
+     * The section that this PipelineEntry was sorted into. If the child of the group, this will be
+     * the parent's section. Null if not attached to the list.
      */
     var section: NotifSection?,
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index 697d0a0..8f7f61f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -34,7 +34,7 @@
     }
 
     /**
-     * The SystemClock.uptimeMillis() when this object was created. In general, this means the
+     * The SystemClock.elapsedRealtime() when this object was created. In general, this means the
      * moment when NotificationManager notifies our listener about the existence of this entry.
      *
      * This value will not change if the notification is updated, although it will change if the
@@ -65,17 +65,4 @@
     @Nullable public PipelineEntry getPreviousParent() {
         return mPreviousAttachState.getParent();
     }
-
-    public int getSectionIndex() {
-        return mAttachState.getSection() != null ? mAttachState.getSection().getIndex() : -1;
-    }
-
-    /**
-     * Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
-     * fresh attach state (all entries will be null/default-initialized).
-     */
-    void beginNewAttachState() {
-        mPreviousAttachState.clone(mAttachState);
-        mAttachState.reset();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 9795edf..b7fe39e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -522,7 +522,7 @@
     }
 
     private void onNotificationsInitialized() {
-        mInitializedTimestamp = mClock.uptimeMillis();
+        mInitializedTimestamp = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
     }
 
     private void postNotification(
@@ -532,7 +532,8 @@
 
         if (entry == null) {
             // A new notification!
-            entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+            entry = new NotificationEntry(sbn, ranking,
+                    UseElapsedRealtimeForCreationTime.getCurrentTime(mClock));
             mEventQueue.add(new InitEntryEvent(entry));
             mEventQueue.add(new BindEntryEvent(entry, sbn));
             mNotificationSet.put(sbn.getKey(), entry);
@@ -861,7 +862,7 @@
     // messages from system server.
     private void crashIfNotInitializing(RuntimeException exception) {
         final boolean isRecentlyInitialized = mInitializedTimestamp == 0
-                || mClock.uptimeMillis() - mInitializedTimestamp
+                || UseElapsedRealtimeForCreationTime.getCurrentTime(mClock) - mInitializedTimestamp
                         < INITIALIZATION_FORGIVENESS_WINDOW;
 
         if (isRecentlyInitialized) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
index 1f8d365..698fed3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt
@@ -89,9 +89,9 @@
                 return true
             }
 
-            // Using uptimeMillis since it's guaranteed to be monotonic, as we don't want a
+            // Using elapsedRealtime since it's guaranteed to be monotonic, as we don't want a
             // timezone/clock change to break us
-            val now = systemClock.uptimeMillis()
+            val now = UseElapsedRealtimeForCreationTime.getCurrentTime(systemClock)
 
             // Cannot purge the same entry from two threads simultaneously
             synchronized(key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
index f662a04..0fc0e9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifBundler
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
@@ -169,6 +170,14 @@
     }
 
     /**
+     * NotifBundler that is used to determine whether a notification should be bundled according to
+     * classification.
+     */
+    fun setNotifBundler(bundler: NotifBundler) {
+        mShadeListBuilder.setBundler(bundler)
+    }
+
+    /**
      * StabilityManager that is used to determine whether to suppress group and section changes.
      * This should only be set once.
      */
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 3d8ba7f..4558017 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
@@ -29,8 +29,6 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
 
-import static com.android.systemui.statusbar.notification.collection.BundleEntry.ROOT_BUNDLES;
-import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
 import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
 
 import static java.util.Objects.requireNonNull;
@@ -41,7 +39,6 @@
 import android.app.Notification.MessagingStyle.Message;
 import android.app.NotificationChannel;
 import android.app.NotificationManager.Policy;
-import android.app.PendingIntent;
 import android.app.Person;
 import android.app.RemoteInput;
 import android.app.RemoteInputHistoryItem;
@@ -68,7 +65,6 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
 import com.android.systemui.statusbar.notification.icon.IconPack;
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
@@ -81,14 +77,14 @@
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.util.ListenerSet;
 
-import kotlinx.coroutines.flow.MutableStateFlow;
-import kotlinx.coroutines.flow.StateFlow;
-import kotlinx.coroutines.flow.StateFlowKt;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlow;
+import kotlinx.coroutines.flow.StateFlowKt;
+
 /**
  * Represents a notification that the system UI knows about
  *
@@ -255,7 +251,7 @@
     /**
      * @param sbn the StatusBarNotification from system server
      * @param ranking also from system server
-     * @param creationTime SystemClock.uptimeMillis of when we were created
+     * @param creationTime SystemClock.elapsedRealtime of when we were created
      */
     public NotificationEntry(
             @NonNull StatusBarNotification sbn,
@@ -497,7 +493,8 @@
     public @Nullable List<NotificationEntry> getAttachedNotifChildren() {
         if (NotificationBundleUi.isEnabled()) {
             if (isGroupSummary()) {
-                return ((GroupEntry) getParent()).getChildren();
+                GroupEntry parent = (GroupEntry) getParent();
+                return parent != null ? new ArrayList<>(parent.getChildren()) : null;
             }
         } else {
             if (row == null) {
@@ -511,7 +508,7 @@
 
             ArrayList<NotificationEntry> children = new ArrayList<>();
             for (ExpandableNotificationRow child : rowChildren) {
-                children.add(child.getEntry());
+                children.add(child.getEntryLegacy());
             }
 
             return children;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
index 0ff2dd7..12cfa91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.statusbar.notification.icon.IconPack
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationActionClickManager
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
 import kotlinx.coroutines.flow.StateFlow
 
@@ -33,6 +34,7 @@
     private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
     private val iconStyleProvider: NotificationIconStyleProvider,
     private val visualStabilityCoordinator: VisualStabilityCoordinator,
+    private val notificationActionClickManager: NotificationActionClickManager,
     private val entry: NotificationEntry,
 ) : EntryAdapter {
 
@@ -135,11 +137,35 @@
         return entry.ranking.isAmbient
     }
 
+    override fun getPeopleNotificationType(): Int {
+        return peopleNotificationIdentifier.getPeopleNotificationType(entry)
+    }
+
+    override fun isPromotedOngoing(): Boolean {
+        return entry.isPromotedOngoing
+    }
+
     override fun isFullScreenCapable(): Boolean {
         return entry.sbn.notification.fullScreenIntent != null
     }
 
+    override fun onDragSuccess() {
+        notificationActivityStarter.onDragSuccess(entry)
+    }
+
     override fun onNotificationBubbleIconClicked() {
         notificationActivityStarter.onNotificationBubbleIconClicked(entry)
     }
+
+    override fun onNotificationActionClicked() {
+        notificationActionClickManager.onNotificationActionClicked(entry)
+    }
+
+    override fun getDismissState(): NotificationEntry.DismissState {
+        return entry.dismissState
+    }
+
+    override fun onEntryClicked(row: ExpandableNotificationRow) {
+        notificationActivityStarter.onNotificationClicked(entry, row)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
index 84de77b..e9c4efc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
@@ -70,7 +70,9 @@
     /**
      * @return Index of section assigned to this entry.
      */
-    public abstract int getSectionIndex();
+    public int getSectionIndex() {
+        return mAttachState.getSection() != null ? mAttachState.getSection().getIndex() : -1;
+    }
 
     /**
      * @return Parent PipelineEntry
@@ -99,4 +101,13 @@
     public void setBucket(@PriorityBucket int bucket) {
         mBucket = bucket;
     }
+
+    /**
+     * Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
+     * fresh attach state (all entries will be null/default-initialized).
+     */
+    void beginNewAttachState() {
+        mPreviousAttachState.clone(mAttachState);
+        mAttachState.reset();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index bb84ab8..5cea821 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -48,6 +48,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.statusbar.NotificationInteractionTracker;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
+import com.android.systemui.statusbar.notification.collection.coordinator.BundleCoordinator;
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
@@ -58,8 +59,10 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.SemiStableSort.StableOrder;
 import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderHelper;
 import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderLogger;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.DefaultNotifBundler;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.DefaultNotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifBundler;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
@@ -78,6 +81,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -122,7 +126,8 @@
     private final List<NotifComparator> mNotifComparators = new ArrayList<>();
     private final List<NotifSection> mNotifSections = new ArrayList<>();
     private NotifStabilityManager mNotifStabilityManager;
-
+    private NotifBundler mNotifBundler;
+    private Map<String, BundleEntry> mIdToBundleEntry = new HashMap<>();
     private final NamedListenerSet<OnBeforeTransformGroupsListener>
             mOnBeforeTransformGroupsListeners = new NamedListenerSet<>();
     private final NamedListenerSet<OnBeforeSortListener>
@@ -273,6 +278,24 @@
         }
     }
 
+    void setBundler(NotifBundler bundler) {
+        Assert.isMainThread();
+        mPipelineState.requireState(STATE_IDLE);
+
+        mNotifBundler = bundler;
+        if (mNotifBundler == null) {
+            throw new IllegalStateException(TAG + ".setBundler: null");
+        }
+
+        mIdToBundleEntry.clear();
+        for (String id: mNotifBundler.getBundleIds()) {
+            if (BundleCoordinator.debugBundleUi) {
+                Log.i(TAG, "create BundleEntry with id: " + id);
+            }
+            mIdToBundleEntry.put(id, new BundleEntry(id));
+        }
+    }
+
     void setNotifStabilityManager(@NonNull NotifStabilityManager notifStabilityManager) {
         Assert.isMainThread();
         mPipelineState.requireState(STATE_IDLE);
@@ -297,6 +320,14 @@
         return mNotifStabilityManager;
     }
 
+    @NonNull
+    private NotifBundler getNotifBundler() {
+        if (mNotifBundler == null) {
+            return DefaultNotifBundler.INSTANCE;
+        }
+        return mNotifBundler;
+    }
+
     void setComparators(List<NotifComparator> comparators) {
         Assert.isMainThread();
         mPipelineState.requireState(STATE_IDLE);
@@ -534,6 +565,11 @@
             entry.beginNewAttachState();
         }
 
+        for (BundleEntry be : mIdToBundleEntry.values()) {
+            be.beginNewAttachState();
+            // TODO(b/399736937) Clear bundle children
+            // BundleEntry has not representative summary so we do not need to clear it here.
+        }
         mNotifList.clear();
     }
 
@@ -542,7 +578,7 @@
             List<PipelineEntry> out,
             List<NotifFilter> filters) {
         Trace.beginSection("ShadeListBuilder.filterNotifs");
-        final long now = mSystemClock.uptimeMillis();
+        final long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock);
         for (PipelineEntry entry : entries) {
             if (entry instanceof GroupEntry) {
                 final GroupEntry groupEntry = (GroupEntry) entry;
@@ -586,7 +622,8 @@
 
                 GroupEntry group = mGroups.get(topLevelKey);
                 if (group == null) {
-                    group = new GroupEntry(topLevelKey, mSystemClock.uptimeMillis());
+                    group = new GroupEntry(topLevelKey,
+                            UseElapsedRealtimeForCreationTime.getCurrentTime(mSystemClock));
                     mGroups.put(topLevelKey, group);
                 }
                 if (group.getParent() == null) {
@@ -651,7 +688,7 @@
                         j--;
                     }
                 }
-            } else {
+            } else if (tle instanceof NotificationEntry) {
                 // maybe put top-level-entries back into their previous groups
                 if (maybeSuppressGroupChange(tle.getRepresentativeEntry(), topLevelList)) {
                     // entry was put back into its previous group, so we remove it from the list of
@@ -659,7 +696,7 @@
                     topLevelList.remove(i);
                     i--;
                 }
-            }
+            } // Promoters ignore bundles so we don't have to demote any here.
         }
         Trace.endSection();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt
new file mode 100644
index 0000000..23f90f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/UseElapsedRealtimeForCreationTime.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.app.Flags
+import com.android.systemui.util.time.SystemClock
+
+/** A helper class for replacing uptimeMillis with elapsedRealtime for entry creation times */
+public object UseElapsedRealtimeForCreationTime {
+    @JvmStatic
+    fun getCurrentTime(clock: SystemClock): Long {
+        if (Flags.notifEntryCreationTimeUseElapsedRealtime()) {
+            return clock.elapsedRealtime()
+        }
+        return clock.uptimeMillis()
+    }
+
+    @JvmStatic
+    fun getCurrentTime(): Long {
+        if (Flags.notifEntryCreationTimeUseElapsedRealtime()) {
+            return android.os.SystemClock.elapsedRealtime()
+        }
+        return android.os.SystemClock.uptimeMillis()
+
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
index 2eec68b..fb7772e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
@@ -25,7 +25,7 @@
  * Represents a set of notification post events for a particular notification group.
  */
 public class EventBatch {
-    /** SystemClock.uptimeMillis() */
+    /** SystemClock.elapsedRealtime() */
     final long mCreatedTimestamp;
 
     /** SBN.getGroupKey -- same for all members */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 96b3542..944e313 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
 import com.android.systemui.statusbar.notification.collection.PipelineDumper;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
 
@@ -182,11 +183,12 @@
     private void maybeEmitBatch(StatusBarNotification sbn) {
         final CoalescedEvent event = mCoalescedEvents.get(sbn.getKey());
         final EventBatch batch = mBatches.get(sbn.getGroupKey());
+        long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
         if (event != null) {
             mLogger.logEarlyEmit(sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey);
             emitBatch(requireNonNull(event.getBatch()));
         } else if (batch != null
-                && mClock.uptimeMillis() - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
+                && now - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
             mLogger.logMaxBatchTimeout(sbn.getKey(), batch.mGroupKey);
             emitBatch(batch);
         }
@@ -228,7 +230,8 @@
     private EventBatch getOrBuildBatch(final String groupKey) {
         EventBatch batch = mBatches.get(groupKey);
         if (batch == null) {
-            batch = new EventBatch(mClock.uptimeMillis(), groupKey);
+            batch = new EventBatch(UseElapsedRealtimeForCreationTime.getCurrentTime(mClock),
+                    groupKey);
             mBatches.put(groupKey, batch);
         }
         return batch;
@@ -268,7 +271,8 @@
         }
         events.sort(mEventComparator);
 
-        long batchAge = mClock.uptimeMillis() - batch.mCreatedTimestamp;
+        long batchAge = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock)
+                - batch.mCreatedTimestamp;
         mLogger.logEmitBatch(batch.mGroupKey, batch.mMembers.size(), batchAge);
 
         mHandler.onNotificationBatchPosted(events);
@@ -298,7 +302,7 @@
 
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        long now = mClock.uptimeMillis();
+        long now = UseElapsedRealtimeForCreationTime.getCurrentTime(mClock);
 
         int eventCount = 0;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
index e6d5f41..4478d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
@@ -20,15 +20,19 @@
 import android.app.NotificationChannel.PROMOTIONS_ID
 import android.app.NotificationChannel.RECS_ID
 import android.app.NotificationChannel.SOCIAL_MEDIA_ID
-import com.android.systemui.statusbar.notification.collection.PipelineEntry
+import android.app.NotificationChannel.SYSTEM_RESERVED_IDS
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifBundler
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.dagger.NewsHeader
 import com.android.systemui.statusbar.notification.dagger.PromoHeader
 import com.android.systemui.statusbar.notification.dagger.RecsHeader
 import com.android.systemui.statusbar.notification.dagger.SocialHeader
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
 import com.android.systemui.statusbar.notification.stack.BUCKET_NEWS
 import com.android.systemui.statusbar.notification.stack.BUCKET_PROMO
 import com.android.systemui.statusbar.notification.stack.BUCKET_RECS
@@ -90,6 +94,30 @@
             }
         }
 
+    val bundler =
+        object : NotifBundler("NotifBundler") {
+
+            // Use list instead of set to keep fixed order
+            override val bundleIds: List<String> =
+                if (debugBundleUi) SYSTEM_RESERVED_IDS + "notify"
+                else SYSTEM_RESERVED_IDS
+
+            override fun getBundleIdOrNull(entry: NotificationEntry?): String? {
+                if (debugBundleUi && entry?.key?.contains("notify") == true) {
+                    return "notify"
+                }
+                return entry?.representativeEntry?.channel?.id?.takeIf { it in this.bundleIds }
+            }
+        }
+
     override fun attach(pipeline: NotifPipeline) {
+        if (NotificationBundleUi.isEnabled) {
+            pipeline.setNotifBundler(bundler)
+        }
+    }
+
+    companion object {
+        @kotlin.jvm.JvmField
+        var debugBundleUi: Boolean = true
     }
 }
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 fdb8cd8..a0eab43 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
@@ -26,6 +26,7 @@
 import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
+import com.android.systemui.statusbar.notification.collection.BundleEntry
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.NotifCollection
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -47,7 +48,9 @@
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
 import com.android.systemui.statusbar.notification.logKey
+import com.android.systemui.statusbar.notification.row.NotificationActionClickManager
 import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
 import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.time.SystemClock
@@ -82,6 +85,7 @@
     private val mHeadsUpViewBinder: HeadsUpViewBinder,
     private val mVisualInterruptionDecisionProvider: VisualInterruptionDecisionProvider,
     private val mRemoteInputManager: NotificationRemoteInputManager,
+    private val notificationActionClickManager: NotificationActionClickManager,
     private val mLaunchFullScreenIntentProvider: LaunchFullScreenIntentProvider,
     private val mFlags: NotifPipelineFlags,
     private val statusBarNotificationChipsInteractor: StatusBarNotificationChipsInteractor,
@@ -107,7 +111,11 @@
         pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilter)
         pipeline.addPromoter(mNotifPromoter)
         pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
-        mRemoteInputManager.addActionPressListener(mActionPressListener)
+        if (NotificationBundleUi.isEnabled) {
+            notificationActionClickManager.addActionClickListener(mActionPressListener)
+        } else {
+            mRemoteInputManager.addActionPressListener(mActionPressListener)
+        }
 
         if (StatusBarNotifChips.isEnabled) {
             applicationScope.launch {
@@ -423,6 +431,7 @@
                             map[child.key] = GroupLocation.Child
                         }
                     }
+                    is BundleEntry -> map[topLevelEntry.key] = GroupLocation.Bundle
                     else -> error("unhandled type $topLevelEntry")
                 }
             }
@@ -781,7 +790,7 @@
      */
     private val mActionPressListener =
         Consumer<NotificationEntry> { entry ->
-            mHeadsUpManager.setUserActionMayIndirectlyRemove(entry)
+            mHeadsUpManager.setUserActionMayIndirectlyRemove(entry.key)
             mExecutor.execute { endNotifLifetimeExtensionIfExtended(entry) }
         }
 
@@ -950,6 +959,7 @@
     Isolated,
     Summary,
     Child,
+    Bundle,
 }
 
 private fun Map<String, GroupLocation>.getLocation(key: String): GroupLocation =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
index 56deb18..d542e67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.collection.BundleEntry
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.PipelineEntry
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -193,6 +194,7 @@
                     when (it) {
                         is NotificationEntry -> listOfNotNull(it)
                         is GroupEntry -> it.children
+                        is BundleEntry -> emptyList()
                         else -> error("unhandled type of $it")
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index df0cde5..818ef6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
 import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
@@ -113,11 +114,12 @@
         mCoordinators.add(remoteInputCoordinator)
         mCoordinators.add(dismissibilityCoordinator)
         mCoordinators.add(automaticPromotionCoordinator)
-
+        if (NotificationBundleUi.isEnabled) {
+            mCoordinators.add(bundleCoordinator)
+        }
         if (NotificationsLiveDataStoreRefactor.isEnabled) {
             mCoordinators.add(statsLoggerCoordinator)
         }
-
         // Manually add Ordered Sections
         if (NotificationMinimalism.isEnabled) {
             mOrderedSections.add(lockScreenMinimalismCoordinator.topOngoingSectioner) // Top Ongoing
@@ -135,7 +137,7 @@
             mOrderedSections.add(conversationCoordinator.peopleSilentSectioner) // People Silent
         }
         mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting
-        if (NotificationClassificationFlag.isEnabled) {
+        if (NotificationClassificationFlag.isEnabled && !NotificationBundleUi.isEnabled) {
             mOrderedSections.add(bundleCoordinator.newsSectioner)
             mOrderedSections.add(bundleCoordinator.socialSectioner)
             mOrderedSections.add(bundleCoordinator.recsSectioner)
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 20c6736..1be415d 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
@@ -34,6 +34,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.statusbar.notification.collection.BundleEntry;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.PipelineEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -54,6 +55,7 @@
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
 import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -239,8 +241,7 @@
                 isMemberOfDelayedGroup = shouldWaitForGroupToInflate(parent, now);
                 mIsDelayedGroupCache.put(parent, isMemberOfDelayedGroup);
             }
-
-            return !isInflated(entry) || isMemberOfDelayedGroup;
+            return !isInflated(entry) || (isMemberOfDelayedGroup != null && isMemberOfDelayedGroup);
         }
 
         @Override
@@ -306,7 +307,9 @@
     private void inflateAllRequiredViews(List<PipelineEntry> entries) {
         for (int i = 0, size = entries.size(); i < size; i++) {
             PipelineEntry entry = entries.get(i);
-            if (entry instanceof GroupEntry) {
+            if (NotificationBundleUi.isEnabled() && entry instanceof BundleEntry) {
+                // TODO(b/399738511) Inflate bundle views.
+            } else if (entry instanceof GroupEntry) {
                 GroupEntry groupEntry = (GroupEntry) entry;
                 inflateRequiredGroupViews(groupEntry);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index d1063d9..cda535d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -16,10 +16,13 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator;
 
+import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.BundleEntry;
 import com.android.systemui.statusbar.notification.collection.PipelineEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -98,7 +101,11 @@
             NotificationPriorityBucketKt.BUCKET_ALERTING) {
         @Override
         public boolean isInSection(PipelineEntry entry) {
-            return mHighPriorityProvider.isHighPriority(entry);
+            return mHighPriorityProvider.isHighPriority(entry)
+                    && entry.getRepresentativeEntry() != null
+                    && entry.getRepresentativeEntry().getChannel() != null
+                    && !SYSTEM_RESERVED_IDS.contains(
+                    entry.getRepresentativeEntry().getChannel().getId());
         }
 
         @Nullable
@@ -116,6 +123,9 @@
             NotificationPriorityBucketKt.BUCKET_SILENT) {
         @Override
         public boolean isInSection(PipelineEntry entry) {
+            if (entry instanceof BundleEntry) {
+                return true;
+            }
             return !mHighPriorityProvider.isHighPriority(entry)
                     && entry.getRepresentativeEntry() != null
                     && !entry.getRepresentativeEntry().isAmbient();
@@ -132,7 +142,12 @@
         public void onEntriesUpdated(@NonNull List<PipelineEntry> entries) {
             mHasSilentEntries = false;
             for (int i = 0; i < entries.size(); i++) {
-                if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
+                NotificationEntry notifEntry = entries.get(i).getRepresentativeEntry();
+                if (notifEntry == null) {
+                    // TODO(b/395698521) Handle BundleEntry
+                    continue;
+                }
+                if (notifEntry.getSbn().isClearable()) {
                     mHasSilentEntries = true;
                     break;
                 }
@@ -147,6 +162,7 @@
         @Override
         public boolean isInSection(PipelineEntry entry) {
             return !mHighPriorityProvider.isHighPriority(entry)
+                    && entry.getRepresentativeEntry() != null
                     && entry.getRepresentativeEntry().isAmbient();
         }
 
@@ -161,7 +177,12 @@
         public void onEntriesUpdated(@NonNull List<PipelineEntry> entries) {
             mHasMinimizedEntries = false;
             for (int i = 0; i < entries.size(); i++) {
-                if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
+                NotificationEntry notifEntry = entries.get(i).getRepresentativeEntry();
+                if (notifEntry == null) {
+                    // TODO(b/395698521) Handle BundleEntry
+                    continue;
+                }
+                if (notifEntry.getSbn().isClearable()) {
                     mHasMinimizedEntries = true;
                     break;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
index e7c767f..27c0dcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
@@ -221,20 +221,20 @@
                 mSmartReplyHistoryExtender.isExtending(key)
         } else false
 
-    override fun releaseNotificationIfKeptForRemoteInputHistory(entry: NotificationEntry) {
-        if (DEBUG) Log.d(TAG, "releaseNotificationIfKeptForRemoteInputHistory(entry=${entry.key})")
+    override fun releaseNotificationIfKeptForRemoteInputHistory(entryKey: String) {
+        if (DEBUG) Log.d(TAG, "releaseNotificationIfKeptForRemoteInputHistory(entry=${entryKey})")
         if (!lifetimeExtensionRefactor()) {
             mRemoteInputHistoryExtender.endLifetimeExtensionAfterDelay(
-                entry.key,
+                entryKey,
                 REMOTE_INPUT_EXTENDER_RELEASE_DELAY,
             )
             mSmartReplyHistoryExtender.endLifetimeExtensionAfterDelay(
-                entry.key,
+                entryKey,
                 REMOTE_INPUT_EXTENDER_RELEASE_DELAY,
             )
         }
         mRemoteInputActiveExtender.endLifetimeExtensionAfterDelay(
-            entry.key,
+            entryKey,
             REMOTE_INPUT_EXTENDER_RELEASE_DELAY,
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index a0a8671..f43767d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -22,7 +22,6 @@
 import com.android.internal.widget.MessagingMessage
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags
 import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -147,9 +146,7 @@
         traceSection("updateNotifOnUiModeChanged") {
             mPipeline?.allNotifs?.forEach { entry ->
                 entry.row?.onUiModeChanged()
-                if (Flags.notificationUndoGutsOnConfigChanged()) {
-                    mGutsManager.closeAndUndoGuts()
-                }
+                mGutsManager.closeAndUndoGuts()
             }
         }
     }
@@ -158,16 +155,7 @@
         colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
         mPipeline?.allNotifs?.forEach { entry ->
             entry.onDensityOrFontScaleChanged()
-            if (Flags.notificationUndoGutsOnConfigChanged()) {
-                mGutsManager.closeAndUndoGuts()
-            } else {
-                // This property actually gets reset when the guts are re-inflated, so we're never
-                // actually calling onDensityOrFontScaleChanged below.
-                val exposedGuts = entry.areGutsExposed()
-                if (exposedGuts) {
-                    mGutsManager.onDensityOrFontScaleChanged(entry)
-                }
-            }
+            mGutsManager.closeAndUndoGuts()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index bdbdc53..0466c03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -39,9 +39,9 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.PipelineEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
@@ -113,6 +113,8 @@
     @VisibleForTesting
     protected static final long ALLOW_SECTION_CHANGE_TIMEOUT = 500;
 
+    private final boolean mCheckLockScreenTransitionEnabled = Flags.checkLockscreenGoneTransition();
+
     @Inject
     public VisualStabilityCoordinator(
             @Background DelayableExecutor delayableExecutor,
@@ -182,7 +184,7 @@
                     this::onTrackingHeadsUpModeChanged);
         }
 
-        if (Flags.checkLockscreenGoneTransition()) {
+        if (mCheckLockScreenTransitionEnabled) {
             if (SceneContainerFlag.isEnabled()) {
                 mJavaAdapter.alwaysCollectFlow(mKeyguardTransitionInteractor.isInTransition(
                                 Edge.create(KeyguardState.LOCKSCREEN, Scenes.Gone), null),
@@ -437,7 +439,7 @@
         boolean wasReorderingAllowed = mReorderingAllowed;
         // No need to run notification pipeline when the lockscreen is in fading animation.
         mPipelineRunAllowed = !(isPanelCollapsingOrLaunchingActivity()
-                || (Flags.checkLockscreenGoneTransition() && mLockscreenInGoneTransition));
+                || (mCheckLockScreenTransitionEnabled && mLockscreenInGoneTransition));
         mReorderingAllowed = isReorderingAllowed();
         if (wasPipelineRunAllowed != mPipelineRunAllowed
                 || wasReorderingAllowed != mReorderingAllowed) {
@@ -499,7 +501,7 @@
      * notification and we are reordering based on the user's change.
      *
      * @param entry notification entry that can change sections even if isReorderingAllowed is false
-     * @param now current time SystemClock.uptimeMillis
+     * @param now current time SystemClock.elapsedRealtime
      */
     public void temporarilyAllowSectionChanges(@NonNull NotificationEntry entry, long now) {
         final String entryKey = entry.getKey();
@@ -566,7 +568,7 @@
         pw.println("pipelineRunAllowed: " + mPipelineRunAllowed);
         pw.println("  notifPanelCollapsing: " + mNotifPanelCollapsing);
         pw.println("  launchingNotifActivity: " + mNotifPanelLaunchingActivity);
-        if (Flags.checkLockscreenGoneTransition()) {
+        if (mCheckLockScreenTransitionEnabled) {
             pw.println("  lockscreenInGoneTransition: " + mLockscreenInGoneTransition);
         }
         pw.println("reorderingAllowed: " + mReorderingAllowed);
@@ -627,7 +629,7 @@
     }
 
     private void onLockscreenInGoneTransitionChanged(boolean inGoneTransition) {
-        if (!Flags.checkLockscreenGoneTransition()) {
+        if (!mCheckLockScreenTransitionEnabled) {
             return;
         }
         if (inGoneTransition == mLockscreenInGoneTransition) {
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 c2f0806..6b32c6a 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
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.notification.collection.inflation;
 
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE;
-import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
 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;
@@ -276,7 +275,7 @@
 
         if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
             if (inflaterParams.isChildInGroup()
-                    && redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+                    && redactionType != REDACTION_TYPE_NONE) {
                 params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
             } else {
                 params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index 07fa6ae..03b4076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -20,7 +20,6 @@
 
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 
-import android.os.SystemClock;
 import android.service.notification.NotificationStats;
 
 import androidx.annotation.NonNull;
@@ -30,6 +29,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
 import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -85,7 +85,7 @@
     public void onImportanceChanged(NotificationEntry entry) {
         mVisualStabilityCoordinator.temporarilyAllowSectionChanges(
                 entry,
-                SystemClock.uptimeMillis());
+                UseElapsedRealtimeForCreationTime.getCurrentTime());
     }
 
     @NonNull
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifBundler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifBundler.kt
new file mode 100644
index 0000000..14a9113
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifBundler.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+/** Pluggable for bundling notifications according to classification. */
+abstract class NotifBundler protected constructor(name: String?) : Pluggable<NotifBundler?>(name) {
+    abstract val bundleIds: List<String>
+
+    abstract fun getBundleIdOrNull(entry: NotificationEntry?): String?
+}
+
+/** The default, no-op instance of NotifBundler which does not bundle anything. */
+object DefaultNotifBundler : NotifBundler("DefaultNotifBundler") {
+    override val bundleIds: List<String>
+        get() = listOf()
+
+    override fun getBundleIdOrNull(entry: NotificationEntry?): String? {
+        return null
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
index 776c7d5..389bb31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
@@ -41,8 +41,8 @@
      *              this entry will not have any grouping nor sorting information.
      *              If this filter is registered via {@link NotifPipeline#addFinalizeFilter},
      *              this entry will have grouping and sorting information.
-     * @param now A timestamp in SystemClock.uptimeMillis that represents "now" for the purposes of
-     *            pipeline execution. This value will be the same for all pluggable calls made
+     * @param now A timestamp in SystemClock.elapsedRealtime that represents "now" for the purposes
+     *            of pipeline execution. This value will be the same for all pluggable calls made
      *            during this pipeline run, giving pluggables a stable concept of "now" to compare
      *            various entries against.
      * @return True if the notif should be removed from the list
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 8021d8f..a552ca5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -234,21 +234,21 @@
     fun getChildCount(): Int = controller.getChildCount()
 
     fun addChildAt(child: ShadeNode, index: Int) {
-        traceSection("ShadeNode#addChildAt") {
+        traceSection({ "ShadeNode#${controller::class.simpleName}#addChildAt" }) {
             controller.addChildAt(child.controller, index)
             child.controller.onViewAdded()
         }
     }
 
     fun moveChildTo(child: ShadeNode, index: Int) {
-        traceSection("ShadeNode#moveChildTo") {
+        traceSection({ "ShadeNode#${controller::class.simpleName}#moveChildTo" }) {
             controller.moveChildTo(child.controller, index)
             child.controller.onViewMoved()
         }
     }
 
     fun removeChild(child: ShadeNode, isTransfer: Boolean) {
-        traceSection("ShadeNode#removeChild") {
+        traceSection({ "ShadeNode#${controller::class.simpleName}#removeChild" }) {
             controller.removeChild(child.controller, isTransfer)
             child.controller.onViewRemoved()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index ef3da94..1e5aa01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -83,6 +83,7 @@
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractorImpl;
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
+import com.android.systemui.statusbar.notification.row.NotificationActionClickManager;
 import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory;
 import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -346,4 +347,5 @@
     /** Provides an instance of {@link EntryAdapterFactory} */
     @Binds
     EntryAdapterFactory provideEntryAdapterFactory(EntryAdapterFactoryImpl impl);
+
 }
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 cdbe0fd..8d1e611 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
@@ -81,7 +81,7 @@
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(emptySet())
         } else {
-            activeHeadsUpRows.map { it.map { (repo, _) -> repo }.toSet() }
+            activeHeadsUpRows.map { it.map { (repo, _) -> repo }.toSet() }.distinctUntilChanged()
         }
     }
 
@@ -90,9 +90,9 @@
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(emptySet())
         } else {
-            activeHeadsUpRows.map {
-                it.filter { (_, isPinned) -> isPinned }.map { (repo, _) -> repo }.toSet()
-            }
+            activeHeadsUpRows
+                .map { it.filter { (_, isPinned) -> isPinned }.map { (repo, _) -> repo }.toSet() }
+                .distinctUntilChanged() // TODO(b/402428276) stop sending duplicate updates instead
         }
     }
 
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 9728fcf..25ae50c 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
@@ -19,7 +19,6 @@
 import android.graphics.Region
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.collection.EntryAdapter
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import dagger.Binds
@@ -155,9 +154,9 @@
     fun setAnimationStateHandler(handler: AnimationStateHandler)
 
     /**
-    * Set an entry to be expanded and therefore stick in the heads up area if it's pinned until
-    * it's collapsed again.
-    */
+     * Set an entry to be expanded and therefore stick in the heads up area if it's pinned until
+     * it's collapsed again.
+     */
     fun setExpanded(key: String, row: ExpandableNotificationRow, expanded: Boolean)
 
     /**
@@ -199,12 +198,12 @@
      * Notes that the user took an action on an entry that might indirectly cause the system or the
      * app to remove the notification.
      *
-     * @param entry the entry that might be indirectly removed by the user's action
+     * @param entry the key of the entry that might be indirectly removed by the user's action
      * @see
      *   com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator.mActionPressListener
      * @see .canRemoveImmediately
      */
-    fun setUserActionMayIndirectlyRemove(entry: NotificationEntry)
+    fun setUserActionMayIndirectlyRemove(entryKey: String)
 
     /**
      * Decides whether a click is invalid for a notification, i.e. it has not been shown long enough
@@ -332,7 +331,7 @@
 
     override fun setUser(user: Int) {}
 
-    override fun setUserActionMayIndirectlyRemove(entry: NotificationEntry) {}
+    override fun setUserActionMayIndirectlyRemove(entryKey: String) {}
 
     override fun shouldSwallowClick(key: String): Boolean = false
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerExt.kt
index 6525b6f..3767350 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerExt.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.headsup
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
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 ca94655..ca83666 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
@@ -1126,8 +1126,8 @@
      * @see HeadsUpCoordinator.mActionPressListener
      * @see #canRemoveImmediately(String)
      */
-    public void setUserActionMayIndirectlyRemove(@NonNull NotificationEntry entry) {
-        HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
+    public void setUserActionMayIndirectlyRemove(@NonNull String entryKey) {
+        HeadsUpEntry headsUpEntry = getHeadsUpEntry(entryKey);
         if (headsUpEntry != null) {
             headsUpEntry.mUserActionMayIndirectlyRemove = true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
index 147a5af..619d48f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
@@ -18,9 +18,9 @@
 
 import android.view.Display
 import androidx.lifecycle.lifecycleScope
+import com.android.app.displaylib.PerDisplayRepository
 import com.android.app.tracing.traceSection
 import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.display.data.repository.PerDisplayRepository
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.notification.collection.NotifCollection
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index c3266fc..92c87e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -18,10 +18,10 @@
 
 import android.service.notification.StatusBarNotification
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.NotificationMediaManager
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
 import com.android.systemui.statusbar.NotificationListener
-import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.statusbar.NotificationPresenter
 import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager
 import com.android.systemui.statusbar.notification.NotificationActivityStarter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index ec8fbc0..5bdd769 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -19,7 +19,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.service.notification.NotificationListenerService;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -44,6 +43,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.UseElapsedRealtimeForCreationTime;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.UpdateSource;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -112,7 +112,7 @@
 
         @Override
         public void run() {
-            mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
+            mLastVisibilityReportUptimeMs = UseElapsedRealtimeForCreationTime.getCurrentTime();
 
             // 1. Loop over active entries:
             //   A. Keep list of visible notifications.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 2a01a14..777392d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -19,19 +19,26 @@
 import android.app.Flags
 import android.app.Flags.notificationsRedesignTemplates
 import android.app.Notification
+import android.content.Context
 import android.graphics.PorterDuff
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.View.GONE
+import android.view.View.MeasureSpec.AT_MOST
+import android.view.View.MeasureSpec.EXACTLY
+import android.view.View.MeasureSpec.UNSPECIFIED
+import android.view.View.MeasureSpec.makeMeasureSpec
 import android.view.View.VISIBLE
 import android.view.ViewGroup.MarginLayoutParams
 import android.view.ViewStub
 import android.widget.Chronometer
 import android.widget.DateTimeView
+import android.widget.FrameLayout
 import android.widget.ImageView
 import android.widget.ProgressBar
 import android.widget.TextView
+import androidx.annotation.DimenRes
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.border
 import androidx.compose.foundation.layout.Box
@@ -42,7 +49,9 @@
 import androidx.compose.runtime.key
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.LocalDensity
 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.core.view.isVisible
@@ -88,22 +97,12 @@
     }
 
     key(content.identity) {
-        val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
-        val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
-
-        val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
-
-        val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
-        val borderShape = RoundedCornerShape(borderRadius)
-
-        Box(modifier = modifier.padding(sidePaddingValues)) {
-            AODPromotedNotificationView(
-                layoutResource = layoutResource,
-                content = content,
-                audiblyAlertedIconVisible = audiblyAlertedIconVisible,
-                modifier = Modifier.border(borderStroke, borderShape),
-            )
-        }
+        AODPromotedNotificationView(
+            layoutResource = layoutResource,
+            content = content,
+            audiblyAlertedIconVisible = audiblyAlertedIconVisible,
+            modifier = modifier,
+        )
     }
 }
 
@@ -114,27 +113,91 @@
     audiblyAlertedIconVisible: Boolean,
     modifier: Modifier = Modifier,
 ) {
-    AndroidView(
-        factory = { context ->
-            val view =
-                traceSection("$TAG.inflate") {
-                    LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
-                }
+    val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
+    val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
 
-            val updater =
-                traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(view) }
+    val boxModifier = modifier.padding(sidePaddingValues)
 
-            view.setTag(viewUpdaterTagId, updater)
+    val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
 
-            view
-        },
-        update = { view ->
-            val updater = view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
+    val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
+    val borderShape = RoundedCornerShape(borderRadius)
 
-            traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) }
-        },
-        modifier = modifier,
-    )
+    val maxHeight =
+        with(LocalDensity.current) {
+                scaledFontHeight(systemuiR.dimen.notification_max_height_for_promoted_ongoing)
+                    .toPx()
+            }
+            .toInt()
+
+    val viewModifier = Modifier.border(borderStroke, borderShape)
+
+    Box(modifier = boxModifier) {
+        AndroidView(
+            factory = { context ->
+                val notif =
+                    traceSection("$TAG.inflate") {
+                        LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
+                    }
+                val updater =
+                    traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(notif) }
+
+                val frame = FrameLayoutWithMaxHeight(maxHeight, context)
+                frame.addView(notif)
+                frame.setTag(viewUpdaterTagId, updater)
+
+                frame
+            },
+            update = { frame ->
+                val updater = frame.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
+
+                traceSection("$TAG.update") { updater.update(content, audiblyAlertedIconVisible) }
+                frame.maxHeight = maxHeight
+            },
+            modifier = viewModifier,
+        )
+    }
+}
+
+private class FrameLayoutWithMaxHeight(maxHeight: Int, context: Context) : FrameLayout(context) {
+    var maxHeight = maxHeight
+        set(value) {
+            if (field != value) {
+                field = value
+                requestLayout()
+            }
+        }
+
+    // This mirrors the logic in NotificationContentView.onMeasure.
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        if (childCount < 1) {
+            return
+        }
+
+        val child = getChildAt(0)
+        val childLayoutHeight = child.layoutParams.height
+        val childHeightSpec =
+            if (childLayoutHeight >= 0) {
+                makeMeasureSpec(maxHeight.coerceAtMost(childLayoutHeight), EXACTLY)
+            } else {
+                makeMeasureSpec(maxHeight, AT_MOST)
+            }
+        measureChildWithMargins(child, widthMeasureSpec, 0, childHeightSpec, 0)
+        val childMeasuredHeight = child.measuredHeight
+
+        val ownHeightMode = MeasureSpec.getMode(heightMeasureSpec)
+        val ownHeightSize = MeasureSpec.getSize(heightMeasureSpec)
+
+        val ownMeasuredWidth = MeasureSpec.getSize(widthMeasureSpec)
+        val ownMeasuredHeight =
+            if (ownHeightMode != UNSPECIFIED) {
+                childMeasuredHeight.coerceAtMost(ownHeightSize)
+            } else {
+                childMeasuredHeight
+            }
+
+        setMeasuredDimension(ownMeasuredWidth, ownMeasuredHeight)
+    }
 }
 
 private val PromotedNotificationContentModel.layoutResource: Int?
@@ -521,6 +584,11 @@
     val brush = SolidColor(androidx.compose.ui.graphics.Color(colorInt))
 }
 
+@Composable
+private fun scaledFontHeight(@DimenRes dimenId: Int): Dp {
+    return dimensionResource(dimenId) * LocalDensity.current.fontScale.coerceAtLeast(1f)
+}
+
 private val viewUpdaterTagId = systemuiR.id.aod_promoted_notification_view_updater_tag
 
 private const val TAG = "AODPromotedNotification"
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 2aafe8c..d35c3b61 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
@@ -20,6 +20,7 @@
 import android.app.Notification.BigPictureStyle
 import android.app.Notification.BigTextStyle
 import android.app.Notification.CallStyle
+import android.app.Notification.EXTRA_BIG_TEXT
 import android.app.Notification.EXTRA_CHRONOMETER_COUNT_DOWN
 import android.app.Notification.EXTRA_PROGRESS
 import android.app.Notification.EXTRA_PROGRESS_INDETERMINATE
@@ -27,8 +28,10 @@
 import android.app.Notification.EXTRA_SUB_TEXT
 import android.app.Notification.EXTRA_TEXT
 import android.app.Notification.EXTRA_TITLE
+import android.app.Notification.EXTRA_TITLE_BIG
 import android.app.Notification.EXTRA_VERIFICATION_ICON
 import android.app.Notification.EXTRA_VERIFICATION_TEXT
+import android.app.Notification.InboxStyle
 import android.app.Notification.ProgressStyle
 import android.content.Context
 import android.graphics.drawable.Icon
@@ -105,8 +108,8 @@
         contentBuilder.shortCriticalText = notification.shortCriticalText()
         contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs
         contentBuilder.profileBadgeResId = null // TODO
-        contentBuilder.title = notification.title()
-        contentBuilder.text = notification.text()
+        contentBuilder.title = notification.resolveTitle(recoveredBuilder.style)
+        contentBuilder.text = notification.resolveText(recoveredBuilder.style)
         contentBuilder.skeletonLargeIcon = notification.skeletonLargeIcon(imageModelProvider)
         contentBuilder.oldProgress = notification.oldProgress()
 
@@ -127,8 +130,39 @@
 
     private fun Notification.title(): CharSequence? = extras?.getCharSequence(EXTRA_TITLE)
 
+    private fun Notification.bigTitle(): CharSequence? = extras?.getCharSequence(EXTRA_TITLE_BIG)
+
+    private fun Notification.Style.bigTitleOverridesTitle(): Boolean {
+        return when (this) {
+            is BigTextStyle,
+            is BigPictureStyle,
+            is InboxStyle -> true
+            else -> false
+        }
+    }
+
+    private fun Notification.resolveTitle(style: Notification.Style?): CharSequence? {
+        return if (style?.bigTitleOverridesTitle() == true) {
+            bigTitle()
+        } else {
+            null
+        } ?: title()
+    }
+
     private fun Notification.text(): CharSequence? = extras?.getCharSequence(EXTRA_TEXT)
 
+    private fun Notification.bigText(): CharSequence? = extras?.getCharSequence(EXTRA_BIG_TEXT)
+
+    private fun Notification.Style.bigTextOverridesText(): Boolean = this is BigTextStyle
+
+    private fun Notification.resolveText(style: Notification.Style?): CharSequence? {
+        return if (style?.bigTextOverridesText() == true) {
+            bigText()
+        } else {
+            null
+        } ?: text()
+    }
+
     private fun Notification.subText(): String? = extras?.getString(EXTRA_SUB_TEXT)
 
     private fun Notification.shortCriticalText(): String? {
@@ -233,13 +267,13 @@
     private fun BigPictureStyle.extractContent(
         contentBuilder: PromotedNotificationContentModel.Builder
     ) {
-        // TODO?
+        // Big title is handled in resolveTitle, and big picture is unsupported.
     }
 
     private fun BigTextStyle.extractContent(
         contentBuilder: PromotedNotificationContentModel.Builder
     ) {
-        // TODO?
+        // Big title and big text are handled in resolveTitle and resolveText.
     }
 
     private fun CallStyle.extractContent(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt
new file mode 100644
index 0000000..1429535
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractor.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** A class which can receive both a demotion signal and a single handler of that signal */
+@SysUISingleton
+class PackageDemotionInteractor @Inject constructor() {
+    private var demotionSignalHandler: ((packageName: String, uid: Int) -> Unit)? = null
+
+    /**
+     * called after sending a the demotion signal to
+     * [android.app.INotificationManager.setCanBePromoted]
+     */
+    fun onPackageDemoted(packageName: String, uid: Int) {
+        demotionSignalHandler?.invoke(packageName, uid)
+    }
+
+    /** sets the [handler] that will be called when [onPackageDemoted] is called. */
+    fun setOnPackageDemotionHandler(handler: (packageName: String, uid: Int) -> Unit) {
+        demotionSignalHandler = handler
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt
index 1abd48c..a99ca072 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt
@@ -70,9 +70,6 @@
     private fun OngoingCallModel.getNotifData(): NotifAndPromotedContent? =
         when (this) {
             is OngoingCallModel.InCall -> NotifAndPromotedContent(notificationKey, promotedContent)
-            is OngoingCallModel.InCallWithVisibleApp ->
-                // TODO(b/395989259): support InCallWithVisibleApp when it has notif data
-                null
             is OngoingCallModel.NoCall -> null
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 6837cb2..b0b08a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -40,6 +40,7 @@
 import com.android.app.animation.Interpolators;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.jank.InteractionJankMonitor.Configuration;
+import com.android.systemui.Flags;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.common.shared.colors.SurfaceEffectColors;
 import com.android.systemui.res.R;
@@ -101,7 +102,8 @@
     private ValueAnimator mBackgroundColorAnimator;
     private float mAppearAnimationFraction = -1.0f;
     private float mAppearAnimationTranslation;
-    private int mNormalColor;
+    protected int mNormalColor;
+    protected int mOpaqueColor;
     private boolean mIsBelowSpeedBump;
     private long mLastActionUpTime;
 
@@ -128,14 +130,15 @@
         updateColors();
     }
 
-    private void updateColors() {
-        if (usesTransparentBackground()) {
+    protected void updateColors() {
+        if (notificationRowTransparency()) {
             mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+            mOpaqueColor = mContext.getColor(
+                    com.android.internal.R.color.materialColorSurfaceContainer);
         } else {
             mNormalColor = mContext.getColor(
                     com.android.internal.R.color.materialColorSurfaceContainerHigh);
         }
-        setBackgroundToNormalColor();
         mTintedRippleColor = mContext.getColor(
                 R.color.notification_ripple_tinted_color);
         mNormalRippleColor = mContext.getColor(
@@ -146,12 +149,6 @@
         mOverrideAmount = 0.0f;
     }
 
-    private void setBackgroundToNormalColor() {
-        if (mBackgroundNormal != null) {
-            mBackgroundNormal.setNormalColor(mNormalColor);
-        }
-    }
-
     /**
      * Reload background colors from resources and invalidate views.
      */
@@ -181,7 +178,6 @@
         mBackgroundNormal = findViewById(R.id.backgroundNormal);
         mFakeShadow = findViewById(R.id.fake_shadow);
         mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
-        setBackgroundToNormalColor();
         initBackground();
         updateBackgroundTint();
         updateOutlineAlpha();
@@ -704,7 +700,11 @@
         if (withTint && mBgTint != NO_COLOR) {
             return mBgTint;
         } else {
-            return mNormalColor;
+            if (Flags.notificationRowTransparency()) {
+                return usesTransparentBackground() ? mNormalColor : mOpaqueColor;
+            } else {
+                return mNormalColor;
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
index 640d364..dccc28f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
@@ -34,11 +34,11 @@
 import android.view.View
 import android.widget.ImageView
 import android.widget.LinearLayout
-import android.widget.Switch
 import android.widget.TextView
 import com.android.settingslib.Utils
 import com.android.systemui.res.R
 import com.android.systemui.util.Assert
+import com.google.android.material.materialswitch.MaterialSwitch
 
 /** Half-shelf for notification channel controls */
 class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) {
@@ -139,12 +139,12 @@
 class AppControlView(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) {
     lateinit var iconView: ImageView
     lateinit var channelName: TextView
-    lateinit var switch: Switch
+    lateinit var switch: MaterialSwitch
 
     override fun onFinishInflate() {
         iconView = requireViewById(R.id.icon)
         channelName = requireViewById(R.id.app_name)
-        switch = requireViewById(R.id.toggle)
+        switch = requireViewById(R.id.material_toggle)
 
         setOnClickListener { switch.toggle() }
     }
@@ -155,7 +155,7 @@
     lateinit var controller: ChannelEditorDialogController
     private lateinit var channelName: TextView
     private lateinit var channelDescription: TextView
-    private lateinit var switch: Switch
+    private lateinit var switch: MaterialSwitch
     private val highlightColor: Int
     var gentle = false
 
@@ -175,7 +175,7 @@
         super.onFinishInflate()
         channelName = requireViewById(R.id.channel_name)
         channelDescription = requireViewById(R.id.channel_description)
-        switch = requireViewById(R.id.toggle)
+        switch = requireViewById(R.id.material_toggle)
         switch.setOnCheckedChangeListener { _, b ->
             channel?.let {
                 controller.proposeEditForChannel(
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 68ad4fa..256d549 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
@@ -19,7 +19,10 @@
 import static android.app.Flags.notificationsRedesignTemplates;
 import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_EXPANDED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
 
+import static com.android.systemui.Flags.notificationRowAccessibilityExpanded;
 import static com.android.systemui.Flags.notificationRowTransparency;
 import static com.android.systemui.Flags.notificationsPinnedHunInShade;
 import static com.android.systemui.flags.Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE;
@@ -38,6 +41,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -65,6 +69,7 @@
 import android.view.ViewParent;
 import android.view.ViewStub;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.widget.Chronometer;
@@ -78,6 +83,7 @@
 
 import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -111,7 +117,6 @@
 import com.android.systemui.statusbar.notification.SourceType;
 import com.android.systemui.statusbar.notification.collection.EntryAdapter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryAdapter;
 import com.android.systemui.statusbar.notification.collection.PipelineEntry;
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
@@ -120,6 +125,7 @@
 import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
 import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
@@ -182,7 +188,6 @@
     private boolean mShowSnooze = false;
     private boolean mIsFaded;
 
-    private boolean mIsPromotedOngoing = false;
     private boolean mHasStatusBarChipDuringHeadsUpAnimation = false;
 
     @Nullable
@@ -406,10 +411,7 @@
     }
 
     private void toggleExpansionState(View v, boolean shouldLogExpandClickMetric) {
-        boolean isGroupRoot = NotificationBundleUi.isEnabled()
-                ? mGroupMembershipManager.isGroupRoot(mEntryAdapter)
-                : mGroupMembershipManager.isGroupSummary(mEntry);
-        if (!shouldShowPublic() && (!mIsMinimized || isExpanded()) && isGroupRoot) {
+        if (!shouldShowPublic() && (!mIsMinimized || isExpanded()) && isGroupRoot()) {
             mGroupExpansionChanging = true;
             if (NotificationBundleUi.isEnabled()) {
                 final boolean wasExpanded =  mGroupExpansionManager.isGroupExpanded(mEntryAdapter);
@@ -421,9 +423,10 @@
                 }
                 onExpansionChanged(true /* userAction */, wasExpanded);
             } else {
-                final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
-                boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
-                mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+                final boolean wasExpanded =
+                        mGroupExpansionManager.isGroupExpanded(getEntryLegacy());
+                boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(getEntryLegacy());
+                mOnExpandClickListener.onExpandClicked(getEntryLegacy(), v, nowExpanded);
                 if (shouldLogExpandClickMetric) {
                     mMetricsLogger.action(
                             MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
@@ -453,7 +456,7 @@
             if (NotificationBundleUi.isEnabled()) {
                 mOnExpandClickListener.onExpandClicked(this, mEntryAdapter, nowExpanded);
             } else {
-                mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+                mOnExpandClickListener.onExpandClicked(getEntryLegacy(), v, nowExpanded);
             }
             if (shouldLogExpandClickMetric) {
                 mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded);
@@ -558,7 +561,7 @@
         if (NotificationBundleUi.isEnabled()) {
             return mKey;
         } else {
-            return mEntry.getKey();
+            return getEntryLegacy().getKey();
         }
     }
 
@@ -661,15 +664,20 @@
      */
     public boolean getIsNonblockable() {
         NotificationBundleUi.assertInLegacyMode();
-        if (mEntry == null) {
+        if (getEntryLegacy() == null) {
             return true;
         }
-        return !mEntry.isBlockable();
+        return !getEntryLegacy().isBlockable();
     }
 
     private boolean isConversation() {
-        return mPeopleNotificationIdentifier.getPeopleNotificationType(mEntry)
-                != PeopleNotificationIdentifier.TYPE_NON_PERSON;
+        if (NotificationBundleUi.isEnabled()) {
+            return getEntryAdapter().getPeopleNotificationType()
+                    != PeopleNotificationIdentifier.TYPE_NON_PERSON;
+        } else {
+            return mPeopleNotificationIdentifier.getPeopleNotificationType(getEntryLegacy())
+                    != PeopleNotificationIdentifier.TYPE_NON_PERSON;
+        }
     }
 
     public void onNotificationUpdated() {
@@ -679,7 +687,7 @@
             Trace.beginSection("ExpNotRow#onNotifUpdated (leaf)");
         }
         for (NotificationContentView l : mLayouts) {
-            l.onNotificationUpdated(mEntry);
+            l.onNotificationUpdated(getEntry());
         }
         mShowingPublicInitialized = false;
         if (mMenuRow != null) {
@@ -746,7 +754,7 @@
      */
     public void updateBubbleButton() {
         for (NotificationContentView l : mLayouts) {
-            l.updateBubbleButton(mEntry);
+            l.updateBubbleButton(getEntry());
         }
     }
 
@@ -777,7 +785,7 @@
                 return mEntryAdapter.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
                         getBackgroundColorWithoutTint());
             } else {
-                return mEntry.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
+                return getEntryLegacy().getContrastedColor(mContext, mIsMinimized && !isExpanded(),
                         getBackgroundColorWithoutTint());
             }
         }
@@ -871,7 +879,7 @@
 
     private void updateLimitsForView(NotificationContentView layout) {
         final int maxExpandedHeight;
-        if (isPromotedOngoing()) {
+        if (PromotedNotificationUiForceExpanded.isEnabled() && isPromotedOngoing()) {
             maxExpandedHeight = mMaxExpandedHeightForPromotedOngoing;
         } else {
             maxExpandedHeight = mMaxExpandedHeight;
@@ -885,7 +893,7 @@
         if (NotificationBundleUi.isEnabled()) {
             targetSdk = mEntryAdapter.getTargetSdk();
         } else {
-            targetSdk = mEntry.targetSdk;
+            targetSdk = getEntryLegacy().targetSdk;
         }
 
         boolean beforeN = targetSdk < Build.VERSION_CODES.N;
@@ -901,7 +909,7 @@
         if (NotificationBundleUi.isEnabled()) {
             summarization = mEntryAdapter.getSummarization();
         } else {
-            summarization = mEntry.getRanking().getSummarization();
+            summarization = getEntryLegacy().getRanking().getSummarization();
         }
 
         if (customView && beforeS && !mIsSummaryWithChildren) {
@@ -945,7 +953,25 @@
         layout.setHeights(smallHeight, headsUpHeight, maxExpandedHeight);
     }
 
+    /**
+     * Check {@link NotificationBundleUi#isEnabled()}
+     * and use {@link #getEntryAdapter()} when true
+     * and {@link #getEntryLegacy()} when false.
+     */
     @NonNull
+    @Deprecated
+    public NotificationEntry getEntryLegacy() {
+        NotificationBundleUi.assertInLegacyMode();
+        return mEntry;
+    }
+
+    /**
+     * Check {@link NotificationBundleUi#isEnabled()}
+     * and use {@link #getEntryAdapter()} when true
+     * and {@link #getEntryLegacy()} when false.
+     */
+    @NonNull
+    @Deprecated
     public NotificationEntry getEntry() {
         return mEntry;
     }
@@ -979,7 +1005,7 @@
         } else if (isAboveShelf() != wasAboveShelf) {
             mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
         }
-        updateBackgroundOpacity();
+        updateBackgroundTint();
     }
 
     /**
@@ -1350,7 +1376,7 @@
         if (mIsSummaryWithChildren) {
             return mChildrenContainer.getIntrinsicHeight();
         }
-        if (isPromotedOngoing()) {
+        if (PromotedNotificationUiForceExpanded.isEnabled() && isPromotedOngoing()) {
             return getMaxExpandHeight();
         }
         if (mExpandedWhenPinned) {
@@ -1481,8 +1507,8 @@
     public void setBubbleClickListener(@Nullable OnClickListener l) {
         mBubbleClickListener = l;
         // ensure listener is passed to the content views
-        mPrivateLayout.updateBubbleButton(mEntry);
-        mPublicLayout.updateBubbleButton(mEntry);
+        mPrivateLayout.updateBubbleButton(getEntry());
+        mPublicLayout.updateBubbleButton(getEntry());
     }
 
     /**
@@ -1554,7 +1580,7 @@
             return initializationTime != -1
                     && SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
         } else {
-            return getEntry().hasFinishedInitialization();
+            return getEntryLegacy().hasFinishedInitialization();
         }
     }
 
@@ -1633,14 +1659,14 @@
         if (NotificationBundleUi.isEnabled()) {
             mEntryAdapter.prepareForInflation();
         } else {
-            mEntry.getSbn().clearPackageContext();
+            getEntryLegacy().getSbn().clearPackageContext();
         }
         // TODO: Move content inflation logic out of this call
         RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
         params.setNeedsReinflation(true);
 
         var rebindEndCallback = mRebindingTracker.trackRebinding(NotificationBundleUi.isEnabled()
-                ? mEntryAdapter.getKey() : mEntry.getKey());
+                ? mEntryAdapter.getKey() : getEntryLegacy().getKey());
         mRowContentBindStage.requestRebind(mEntry, (e) -> rebindEndCallback.onFinished());
         Trace.endSection();
     }
@@ -1678,21 +1704,36 @@
 
     @Override
     protected void setBackgroundTintColor(int color) {
+        if (notificationRowTransparency()) {
+            boolean isColorized = false;
+            if (NotificationBundleUi.isEnabled()) {
+                if (mEntryAdapter != null) {
+                    isColorized = mEntryAdapter.isColorized();
+                }
+            } else {
+                if (mEntry != null) {
+                    isColorized = mEntry.getSbn().getNotification().isColorized();
+                }
+            }
+            boolean isTransparent = usesTransparentBackground();
+            if (isColorized) {
+                // For colorized notifications, use a color that matches the tint color at 90% alpha
+                // when the row is transparent.
+                color = ColorUtils.setAlphaComponent(
+                        color, (int) (0xFF * (isTransparent ? 0.9f : 1)));
+            } else {
+                // For non-colorized notifications, use the semi-transparent normal color token
+                // when the row is transparent, and the opaque color token otherwise.
+                if (!isTransparent && mBgTint == NO_COLOR) {
+                    color = mOpaqueColor;
+                }
+            }
+        }
         super.setBackgroundTintColor(color);
         NotificationContentView view = getShowingLayout();
         if (view != null) {
             view.setBackgroundTintColor(color);
         }
-        if (notificationRowTransparency() && mBackgroundNormal != null) {
-            if (NotificationBundleUi.isEnabled() && mEntryAdapter != null) {
-                mBackgroundNormal.setBgIsColorized(mEntryAdapter.isColorized());
-            } else {
-                if (mEntry != null) {
-                    mBackgroundNormal.setBgIsColorized(
-                            mEntry.getSbn().getNotification().isColorized());
-                }
-            }
-        }
     }
 
     public void closeRemoteInput() {
@@ -1750,7 +1791,11 @@
 
     /** @return true if the User has dismissed this notif's parent */
     public boolean isParentDismissed() {
-        return getEntry().getDismissState() == PARENT_DISMISSED;
+        if (NotificationBundleUi.isEnabled()) {
+            return getEntryAdapter().getDismissState() == PARENT_DISMISSED;
+        } else {
+            return getEntryLegacy().getDismissState() == PARENT_DISMISSED;
+        }
     }
 
     @Override
@@ -2310,7 +2355,9 @@
     }
 
     @VisibleForTesting
-    protected void setEntry(NotificationEntry entry) {
+    @Deprecated
+    protected void setEntryLegacy(NotificationEntry entry) {
+        NotificationBundleUi.assertInLegacyMode();
         mEntry = entry;
     }
 
@@ -2403,7 +2450,7 @@
         if (NotificationBundleUi.isEnabled()) {
             return traceTag + "(" + getEntryAdapter().getStyle() + ")";
         } else {
-            return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+            return traceTag + "(" + getEntryLegacy().getNotificationStyle() + ")";
         }
     }
 
@@ -2473,7 +2520,11 @@
      */
     public void dragAndDropSuccess() {
         if (mOnDragSuccessListener != null) {
-            mOnDragSuccessListener.onDragSuccess(getEntry());
+            if (NotificationBundleUi.isEnabled()) {
+                mOnDragSuccessListener.onDragSuccess(getEntryAdapter());
+            } else {
+                mOnDragSuccessListener.onDragSuccess(getEntryLegacy());
+            }
         }
     }
 
@@ -2775,7 +2826,6 @@
         return false;
     }
 
-
     public void applyLaunchAnimationParams(LaunchAnimationParameters params) {
         if (params == null) {
             // `null` params indicates the animation is over, which means we can't access
@@ -2909,7 +2959,7 @@
         if (NotificationBundleUi.isEnabled()) {
             return getEntryAdapter().getIcons().getShelfIcon();
         } else {
-            return mEntry.getIcons().getShelfIcon();
+            return getEntryLegacy().getIcons().getShelfIcon();
         }
     }
 
@@ -2940,7 +2990,7 @@
         if (mIsSummaryWithChildren && !shouldShowPublic()) {
             return !mChildrenExpanded;
         }
-        if (isPromotedOngoing()) {
+        if (PromotedNotificationUiForceExpanded.isEnabled() && isPromotedOngoing()) {
             return false;
         }
         return mEnableNonGroupedNotificationExpand && mExpandable;
@@ -2951,17 +3001,6 @@
         mPrivateLayout.updateExpandButtons(isExpandable());
     }
 
-    /**
-     * Set this notification to be promoted ongoing
-     */
-    public void setPromotedOngoing(boolean promotedOngoing) {
-        if (PromotedNotificationUiForceExpanded.isUnexpectedlyInLegacyMode()) {
-            return;
-        }
-
-        mIsPromotedOngoing = promotedOngoing;
-        setExpandable(!mIsPromotedOngoing);
-    }
 
     /**
      * Sets whether the status bar is showing a chip corresponding to this notification.
@@ -3028,7 +3067,7 @@
                     mGroupExpansionManager.setGroupExpanded(mEntryAdapter, userExpanded);
                 }
             } else {
-                mGroupExpansionManager.setGroupExpanded(mEntry, userExpanded);
+                mGroupExpansionManager.setGroupExpanded(getEntryLegacy(), userExpanded);
             }
             onExpansionChanged(true /* userAction */, wasExpanded);
             return;
@@ -3062,10 +3101,13 @@
     }
 
     public void setUserLocked(boolean userLocked) {
-        if (isPromotedOngoing()) return;
+        if (PromotedNotificationUiForceExpanded.isEnabled() && isPromotedOngoing()) return;
 
         mUserLocked = userLocked;
         mPrivateLayout.setUserExpanding(userLocked);
+        if (android.app.Flags.expandingPublicView()) {
+            mPublicLayout.setUserExpanding(userLocked);
+        }
         // This is intentionally not guarded with mIsSummaryWithChildren since we might have had
         // children but not anymore.
         if (mChildrenContainer != null) {
@@ -3122,7 +3164,7 @@
                     mChildrenContainer.setOnKeyguard(onKeyguard);
                 }
             }
-            updateBackgroundOpacity();
+            updateBackgroundTint();
         }
     }
 
@@ -3168,7 +3210,7 @@
     public boolean canShowHeadsUp() {
         boolean canEntryHun = NotificationBundleUi.isEnabled()
                 ? mEntryAdapter.canPeek()
-                : mEntry.isStickyAndNotDemoted();
+                : getEntryLegacy().isStickyAndNotDemoted();
         if (mOnKeyguard && !isDozing() && !isBypassEnabled() &&
                 (!canEntryHun
                         || (!mIgnoreLockscreenConstraints && mSaveSpaceOnLockscreen))) {
@@ -3190,7 +3232,13 @@
         if (NotificationBundleUi.isEnabled()) {
             return mGroupExpansionManager.isGroupExpanded(mEntryAdapter);
         }
-        return mGroupExpansionManager.isGroupExpanded(mEntry);
+        return mGroupExpansionManager.isGroupExpanded(getEntryLegacy());
+    }
+
+    private boolean isGroupRoot() {
+        return NotificationBundleUi.isEnabled()
+                ? mGroupMembershipManager.isGroupRoot(mEntryAdapter)
+                : mGroupMembershipManager.isGroupSummary(getEntryLegacy());
     }
 
     private void onAttachedChildrenCountChanged() {
@@ -3212,7 +3260,8 @@
             if (NotificationBundleUi.isEnabled()) {
                 mPublicLayout.setNotificationWhen(mEntryAdapter.getWhen());
             } else {
-                mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().getWhen());
+                mPublicLayout.setNotificationWhen(
+                        getEntryLegacy().getSbn().getNotification().getWhen());
             }
         }
         getShowingLayout().updateBackgroundColor(false /* animate */);
@@ -3242,7 +3291,23 @@
     }
 
     public boolean isPromotedOngoing() {
-        return PromotedNotificationUiForceExpanded.isEnabled() && mIsPromotedOngoing;
+        if (!PromotedNotificationUi.isEnabled()) {
+            return false;
+        }
+
+        if (NotificationBundleUi.isEnabled()) {
+            final EntryAdapter entryAdapter = mEntryAdapter;
+            if (entryAdapter == null) {
+                return false;
+            }
+            return entryAdapter.isPromotedOngoing();
+        } else {
+            final NotificationEntry entry = mEntry;
+            if (entry == null) {
+                return false;
+            }
+            return entry.isPromotedOngoing();
+        }
     }
 
     private boolean isPromotedNotificationExpanded(boolean allowOnKeyguard) {
@@ -3271,6 +3336,27 @@
     }
 
     /**
+     * Is this row currently showing an expanded state? This method is different from
+     * {@link #isExpanded()}, because it also handles groups, and pinned notifications.
+     */
+    private boolean isShowingExpanded() {
+        if (!shouldShowPublic() && (!mIsMinimized || isExpanded()) && isGroupRoot()) {
+            // is group and expanded?
+            return isGroupExpanded();
+        } else if (mEnableNonGroupedNotificationExpand) {
+            if (isPinned()) {
+                // is pinned and expanded?
+                return mExpandedWhenPinned;
+            } else {
+                // is regular notification and expanded?
+                return isExpanded();
+            }
+        } else {
+            return false;
+        }
+    }
+
+    /**
      * Check whether the view state is currently expanded. This is given by the system in {@link
      * #setSystemExpanded(boolean)} and can be overridden by user expansion or
      * collapsing in {@link #setUserExpanded(boolean)}. Note that the visual appearance of this
@@ -3283,7 +3369,7 @@
     }
 
     public boolean isExpanded(boolean allowOnKeyguard) {
-        if (isPromotedOngoing()) {
+        if (PromotedNotificationUiForceExpanded.isEnabled() && isPromotedOngoing()) {
             return isPromotedNotificationExpanded(allowOnKeyguard);
         }
 
@@ -3510,7 +3596,8 @@
             return mEntryAdapter.isClearable()
                     && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
         } else {
-            return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+            return getEntryLegacy().isClearable()
+                    && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
         }
     }
 
@@ -3524,7 +3611,7 @@
             if (!NotificationBundleUi.isEnabled()) {
                 // this is only called if row.getParent() instanceof NotificationStackScrollLayout,
                 // so there is never a group to expand
-                mGroupExpansionManager.setGroupExpanded(mEntry, true);
+                mGroupExpansionManager.setGroupExpanded(getEntryLegacy(), true);
             }
         }
         notifyHeightChanged(/* needsAnimation= */ false);
@@ -3757,7 +3844,7 @@
                 return true;
             }
         } else {
-            if (getEntry().getSbn().getNotification().isColorized()) {
+            if (getEntryLegacy().getSbn().getNotification().isColorized()) {
                 return true;
             }
         }
@@ -3924,12 +4011,18 @@
             }
         } else if (isChildInGroup()) {
             final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
-            // Only show a background if the group is expanded OR if it is expanding / collapsing
-            // and has a custom background color.
-            final boolean showBackground = isGroupExpanded()
-                    || ((mNotificationParent.isGroupExpansionChanging()
-                    || mNotificationParent.isUserLocked()) && childColor != 0);
-            mShowNoBackground = !showBackground;
+            if (Flags.notificationRowTransparency() && childColor == Color.TRANSPARENT) {
+                // If child is not customizing its background color, switch from the parent to
+                // the child background when the expansion finishes.
+                mShowNoBackground = !mNotificationParent.mShowNoBackground;
+            } else {
+                // Only show a background if the group is expanded OR if it is
+                // expanding / collapsing and has a custom background color.
+                final boolean showBackground = isGroupExpanded()
+                        || ((mNotificationParent.isGroupExpansionChanging()
+                        || mNotificationParent.isUserLocked()) && childColor != 0);
+                mShowNoBackground = !showBackground;
+            }
         } else {
             // Only children or parents ever need no background.
             mShowNoBackground = false;
@@ -3952,9 +4045,7 @@
 
     public void onExpandedByGesture(boolean userExpanded) {
         int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
-        if (NotificationBundleUi.isEnabled()
-                ? mGroupMembershipManager.isGroupRoot(mEntryAdapter)
-                : mGroupMembershipManager.isGroupSummary(mEntry)) {
+        if (isGroupRoot()) {
             event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
         }
         mMetricsLogger.action(event, userExpanded);
@@ -4007,6 +4098,18 @@
             if (mExpansionChangedListener != null) {
                 mExpansionChangedListener.onExpansionChanged(nowExpanded);
             }
+            if (notificationRowAccessibilityExpanded()) {
+                notifyAccessibilityContentExpansionChanged();
+            }
+        }
+    }
+
+    private void notifyAccessibilityContentExpansionChanged() {
+        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            AccessibilityEvent event = AccessibilityEvent.obtain();
+            event.setEventType(TYPE_WINDOW_CONTENT_CHANGED);
+            event.setContentChangeTypes(CONTENT_CHANGE_TYPE_EXPANDED);
+            sendAccessibilityEventUnchecked(event);
         }
     }
 
@@ -4037,33 +4140,50 @@
         super.onInitializeAccessibilityNodeInfoInternal(info);
         final boolean isLongClickable = isNotificationRowLongClickable();
         if (isLongClickable) {
-            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+            info.addAction(AccessibilityAction.ACTION_LONG_CLICK);
         }
         info.setLongClickable(isLongClickable);
 
         if (canViewBeDismissed() && !mIsSnoozed) {
-            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
+            info.addAction(AccessibilityAction.ACTION_DISMISS);
         }
-        boolean expandable = shouldShowPublic();
-        boolean isExpanded = false;
-        if (!expandable) {
-            if (mIsSummaryWithChildren) {
-                expandable = true;
-                if (!mIsMinimized || isExpanded()) {
-                    isExpanded = isGroupExpanded();
+
+        if (notificationRowAccessibilityExpanded()) {
+            if (isAccessibilityExpandable()) {
+                if (isShowingExpanded()) {
+                    info.addAction(AccessibilityAction.ACTION_COLLAPSE);
+                    info.setExpandedState(AccessibilityNodeInfo.EXPANDED_STATE_FULL);
+                } else {
+                    info.addAction(AccessibilityAction.ACTION_EXPAND);
+                    info.setExpandedState(AccessibilityNodeInfo.EXPANDED_STATE_COLLAPSED);
                 }
             } else {
-                expandable = mPrivateLayout.isContentExpandable();
-                isExpanded = isExpanded();
+                info.setExpandedState(AccessibilityNodeInfo.EXPANDED_STATE_UNDEFINED);
+            }
+        } else {
+            boolean expandable = shouldShowPublic();
+            boolean isExpanded = false;
+            if (!expandable) {
+                if (mIsSummaryWithChildren) {
+                    expandable = true;
+                    if (!mIsMinimized || isExpanded()) {
+                        isExpanded = isGroupExpanded();
+                    }
+                } else {
+                    expandable = mPrivateLayout.isContentExpandable();
+                    isExpanded = isExpanded();
+                }
+            }
+
+            if (expandable) {
+                if (isExpanded) {
+                    info.addAction(AccessibilityAction.ACTION_COLLAPSE);
+                } else {
+                    info.addAction(AccessibilityAction.ACTION_EXPAND);
+                }
             }
         }
-        if (expandable && !mIsSnoozed) {
-            if (isExpanded) {
-                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
-            } else {
-                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
-            }
-        }
+
         NotificationMenuRowPlugin provider = getProvider();
         if (provider != null) {
             MenuItem snoozeMenu = provider.getSnoozeMenuItem(getContext());
@@ -4076,6 +4196,12 @@
         }
     }
 
+    /** @return whether this row's expansion state can be toggled by an accessibility action. */
+    private boolean isAccessibilityExpandable() {
+        // don't add expand accessibility actions to snoozed notifications
+        return !mIsSnoozed && isContentExpandable();
+    }
+
     @Override
     public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
         if (super.performAccessibilityActionInternal(action, arguments)) {
@@ -4195,7 +4321,7 @@
 
     public boolean isMediaRow() {
         NotificationBundleUi.assertInLegacyMode();
-        return mEntry.getSbn().getNotification().isMediaNotification();
+        return getEntryLegacy().getSbn().getNotification().isMediaNotification();
     }
 
     public void setAboveShelf(boolean aboveShelf) {
@@ -4299,6 +4425,12 @@
          * @param entry NotificationEntry that succeed to drop on proper target window.
          */
         void onDragSuccess(NotificationEntry entry);
+
+        /**
+         * @param entryAdapter The EntryAdapter that successfully dropped on the proper
+         *            target window
+         */
+        void onDragSuccess(EntryAdapter entryAdapter);
     }
 
     /**
@@ -4317,11 +4449,7 @@
     public void dump(PrintWriter pwOriginal, String[] args) {
         IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
         // Skip super call; dump viewState ourselves
-        if (NotificationBundleUi.isEnabled()) {
-            pw.println("Notification: " + mEntryAdapter.getKey());
-        } else {
-            pw.println("Notification: " + mEntry.getKey());
-        }
+        pw.println("Notification: " + getKey());
         DumpUtilsKt.withIncreasedIndent(pw, () -> {
             pw.println(this);
             pw.print("visibility: " + getVisibility());
@@ -4339,8 +4467,10 @@
                     + (!shouldShowPublic() && mIsSummaryWithChildren));
             pw.print(", mShowNoBackground: " + mShowNoBackground);
             pw.print(", clipBounds: " + getClipBounds());
-            if (PromotedNotificationUiForceExpanded.isEnabled()) {
-                pw.print(", isPromotedOngoing: " + isPromotedOngoing());
+            pw.print(", isPromotedOngoing: " + isPromotedOngoing());
+            if (notificationRowAccessibilityExpanded()) {
+                pw.print(", isShowingExpanded: " + isShowingExpanded());
+                pw.print(", isAccessibilityExpandable: " + isAccessibilityExpandable());
             }
             pw.print(", isExpandable: " + isExpandable());
             pw.print(", mExpandable: " + mExpandable);
@@ -4556,7 +4686,7 @@
         if (NotificationBundleUi.isEnabled()) {
             mLaunchAnimationRunning = launchAnimationRunning;
         } else {
-            getEntry().setExpandAnimationRunning(launchAnimationRunning);
+            getEntryLegacy().setExpandAnimationRunning(launchAnimationRunning);
         }
     }
 
@@ -4565,15 +4695,14 @@
         if (NotificationBundleUi.isEnabled()) {
             return mLaunchAnimationRunning;
         } else {
-            return getEntry().isExpandAnimationRunning();
+            return getEntryLegacy().isExpandAnimationRunning();
         }
     }
 
-    private void updateBackgroundOpacity() {
-        if (mBackgroundNormal != null) {
-            // Row background should be opaque when it's displayed as a heads-up notification or
-            // displayed on keyguard.
-            mBackgroundNormal.setForceOpaque(mIsHeadsUp || mOnKeyguard);
-        }
+    @Override
+    protected boolean usesTransparentBackground() {
+        // Row background should be opaque when it's displayed as a heads-up notification or
+        // displayed on keyguard.
+        return super.usesTransparentBackground() && !mIsHeadsUp && !mOnKeyguard;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index ac55930..49d715c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -20,6 +20,7 @@
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.net.Uri;
 import android.os.UserHandle;
@@ -35,8 +36,8 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.Flags;
 import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -45,11 +46,8 @@
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.FeedbackIcon;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.collection.EntryAdapterFactory;
-import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl;
 import com.android.systemui.statusbar.notification.collection.PipelineEntry;
-import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -60,7 +58,6 @@
 import com.android.systemui.statusbar.notification.row.dagger.AppName;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
-import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -69,6 +66,7 @@
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
 import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor;
 
 import com.google.android.msdl.data.model.MSDLToken;
 import com.google.android.msdl.domain.MSDLPlayer;
@@ -125,6 +123,7 @@
     private final MSDLPlayer mMSDLPlayer;
     private final NotificationSettingsController mSettingsController;
     private final EntryAdapterFactory mEntryAdapterFactory;
+    private final WindowRootViewBlurInteractor mWindowRootViewBlurInteractor;
 
     @VisibleForTesting
     final NotificationSettingsController.Listener mSettingsListener =
@@ -139,7 +138,7 @@
                         }
                         final int viewUserId = NotificationBundleUi.isEnabled()
                             ? mView.getEntryAdapter().getSbn().getUserId()
-                            : mView.getEntry().getSbn().getUserId();
+                            : mView.getEntryLegacy().getSbn().getUserId();
                         if (viewUserId == UserHandle.USER_ALL || viewUserId == userId) {
                             mView.getPrivateLayout().setBubblesEnabledForUser(
                                     BUBBLES_SETTING_ENABLED_VALUE.equals(value));
@@ -291,7 +290,8 @@
             UiEventLogger uiEventLogger,
             MSDLPlayer msdlPlayer,
             NotificationRebindingTracker notificationRebindingTracker,
-            EntryAdapterFactory entryAdapterFactory) {
+            EntryAdapterFactory entryAdapterFactory,
+            WindowRootViewBlurInteractor windowRootViewBlurInteractor) {
         mView = view;
         mListContainer = listContainer;
         mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
@@ -329,6 +329,7 @@
         mUiEventLogger = uiEventLogger;
         mMSDLPlayer = msdlPlayer;
         mEntryAdapterFactory = entryAdapterFactory;
+        mWindowRootViewBlurInteractor = windowRootViewBlurInteractor;
     }
 
     /**
@@ -367,7 +368,8 @@
         );
         mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
         if (mAllowLongPress) {
-            if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_DRAG_TO_CONTENTS)) {
+            if (mFeatureFlags.isEnabled(
+                    com.android.systemui.flags.Flags.NOTIFICATION_DRAG_TO_CONTENTS)) {
                 mView.setDragController(mDragController);
             }
 
@@ -395,7 +397,7 @@
                         mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
                     }
                 } else {
-                    mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+                    mView.getEntryLegacy().setInitializationTime(mClock.elapsedRealtime());
                     mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
                 }
                 mPluginManager.addPluginListener(mView,
@@ -416,6 +418,11 @@
                 mSettingsController.removeCallback(BUBBLES_SETTING_URI, mSettingsListener);
             }
         });
+
+        if (Flags.notificationRowTransparency()) {
+            collectFlow(mView, mWindowRootViewBlurInteractor.isBlurCurrentlySupported(),
+                    mView::setIsBlurSupported);
+        }
     }
 
     private final StatusBarStateController.StateListener mStatusBarStateListener =
@@ -429,7 +436,9 @@
     @Override
     @NonNull
     public String getNodeLabel() {
-        return NotificationBundleUi.isEnabled() ? mView.getLoggingKey() : logKey(mView.getEntry());
+        return NotificationBundleUi.isEnabled()
+                ? mView.getLoggingKey()
+                : logKey(mView.getEntryLegacy());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index 9ae2eb1..20b826a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -109,7 +109,7 @@
 
         StatusBarNotification sn = NotificationBundleUi.isEnabled()
                 ? enr.getEntryAdapter().getSbn()
-                : enr.getEntry().getSbn();
+                : enr.getEntryLegacy().getSbn();
         Notification notification = sn.getNotification();
         final PendingIntent contentIntent = notification.contentIntent != null
                 ? notification.contentIntent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
index d02f636..fe3a856 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
@@ -18,7 +18,9 @@
 
 import android.animation.ValueAnimator
 import android.content.Context
+import android.content.res.ColorStateList
 import android.graphics.Canvas
+import android.graphics.Color
 import android.graphics.ColorFilter
 import android.graphics.Paint
 import android.graphics.Path
@@ -31,9 +33,27 @@
 import android.graphics.Shader
 import com.android.systemui.res.R
 import com.android.wm.shell.shared.animation.Interpolators
+import android.graphics.drawable.RippleDrawable
+import androidx.core.content.ContextCompat
 
 class MagicActionBackgroundDrawable(
     context: Context,
+) : RippleDrawable(
+    ContextCompat.getColorStateList(
+        context,
+        R.color.notification_ripple_untinted_color
+    ) ?: ColorStateList.valueOf(Color.TRANSPARENT),
+    createBaseDrawable(context), null
+) {
+    companion object {
+        private fun createBaseDrawable(context: Context): Drawable {
+            return BaseBackgroundDrawable(context)
+        }
+    }
+}
+
+class BaseBackgroundDrawable(
+    context: Context,
 ) : Drawable() {
 
     private val cornerRadius = context.resources.getDimension(R.dimen.magic_action_button_corner_radius)
@@ -42,6 +62,14 @@
 
     private val buttonShape = Path()
     // Color and style
+    private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+        val bgColor =
+            context.getColor(
+                com.android.internal.R.color.materialColorSurfaceContainerHigh
+            )
+        color = bgColor
+        style = Paint.Style.FILL
+    }
     private val outlinePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
         val outlineColor =
             context.getColor(
@@ -91,6 +119,7 @@
         canvas.save()
         // Draw background
         canvas.clipPath(buttonShape)
+        canvas.drawPath(buttonShape, bgPaint)
         // Apply gradient to outline
         canvas.drawPath(buttonShape, outlinePaint)
         updateGradient(boundsF)
@@ -119,11 +148,13 @@
     }
 
     override fun setAlpha(alpha: Int) {
+        bgPaint.alpha = alpha
         outlinePaint.alpha = alpha
         invalidateSelf()
     }
 
     override fun setColorFilter(colorFilter: ColorFilter?) {
+        bgPaint.colorFilter = colorFilter
         outlinePaint.colorFilter = colorFilter
         invalidateSelf()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationActionClickManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationActionClickManager.kt
new file mode 100644
index 0000000..2b45140
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationActionClickManager.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.util.ListenerSet
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * Pipeline components can register consumers here to be informed when a notification action is
+ * clicked
+ */
+@SysUISingleton
+class NotificationActionClickManager @Inject constructor() {
+    private val actionClickListeners = ListenerSet<Consumer<NotificationEntry>>()
+
+    fun addActionClickListener(listener: Consumer<NotificationEntry>) {
+        actionClickListeners.addIfAbsent(listener)
+    }
+
+    fun removeActionClickListener(listener: Consumer<NotificationEntry>) {
+        actionClickListeners.remove(listener)
+    }
+
+    fun onNotificationActionClicked(entry: NotificationEntry) {
+        for (listener in actionClickListeners) {
+            listener.accept(entry)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index e1219e8..e4997e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -36,7 +36,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.internal.graphics.ColorUtils;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dumpable;
 import com.android.systemui.common.shared.colors.SurfaceEffectColors;
@@ -53,7 +52,6 @@
 public class NotificationBackgroundView extends View implements Dumpable,
         ExpandableNotificationRow.DismissButtonTargetVisibilityListener {
 
-    private static final int MAX_ALPHA = 0xFF;
     private final boolean mDontModifyCorners;
     private Drawable mBackground;
     private int mClipTopAmount;
@@ -74,8 +72,6 @@
     private final ColorStateList mLightColoredStatefulColors;
     private final ColorStateList mDarkColoredStatefulColors;
     private int mNormalColor;
-    private boolean mBgIsColorized = false;
-    private boolean mForceOpaque = false;
     private final int convexR = 9;
     private final int concaveR = 22;
 
@@ -89,13 +85,15 @@
                 R.color.notification_state_color_light);
         mDarkColoredStatefulColors = getResources().getColorStateList(
                 R.color.notification_state_color_dark);
+        if (notificationRowTransparency()) {
+            mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
+        } else  {
+            mNormalColor = mContext.getColor(
+                    com.android.internal.R.color.materialColorSurfaceContainerHigh);
+        }
         mFocusOverlayStroke = getResources().getDimension(R.dimen.notification_focus_stroke_width);
     }
 
-    public void setNormalColor(int color) {
-        mNormalColor = color;
-    }
-
     @Override
     public void onTargetVisibilityChanged(boolean targetVisible) {
         if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) {
@@ -141,29 +139,6 @@
         }
     }
 
-    /**
-     * A way to tell whether the background has been colorized.
-     */
-    public boolean isColorized() {
-        return mBgIsColorized;
-    }
-
-    /**
-     * A way to inform this class whether the background has been colorized.
-     * We need to know this, in order to *not* override that color.
-     */
-    public void setBgIsColorized(boolean b) {
-        mBgIsColorized = b;
-    }
-
-    /** Sets if the background should be opaque. */
-    public void setForceOpaque(boolean forceOpaque) {
-        mForceOpaque = forceOpaque;
-        if (notificationRowTransparency()) {
-            updateBaseLayerColor();
-        }
-    }
-
     private Path calculateDismissButtonCutoutPath(Rect backgroundBounds) {
         // TODO(b/365585705): Adapt to RTL after the UX design is finalized.
 
@@ -320,31 +295,21 @@
         return ((LayerDrawable) mBackground).getDrawable(1);
     }
 
-    private void updateBaseLayerColor() {
-        // BG base layer being a drawable, there isn't a method like setColor() to color it.
-        // Instead, we set a color filter that essentially replaces every pixel of the drawable.
-        // For non-colorized notifications, this function specifies a new color token.
-        // For colorized notifications, this uses a color that matches the tint color at 90% alpha.
-        int color = isColorized()
-                ? ColorUtils.setAlphaComponent(mTintColor, (int) (MAX_ALPHA * 0.9f))
-                : SurfaceEffectColors.surfaceEffect1(getContext());
-        if (mForceOpaque) {
-            color = ColorUtils.setAlphaComponent(color, MAX_ALPHA);
-        }
-        getBaseBackgroundLayer().setColorFilter(
-                new PorterDuffColorFilter(
-                        color,
-                        PorterDuff.Mode.SRC)); // SRC operator discards the drawable's color+alpha
-    }
-
     public void setTint(int tintColor) {
         Drawable baseLayer = getBaseBackgroundLayer();
-        baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
-        baseLayer.setTint(tintColor);
-        mTintColor = tintColor;
         if (notificationRowTransparency()) {
-            updateBaseLayerColor();
+            // BG base layer being a drawable, there isn't a method like setColor() to color it.
+            // Instead, we set a color filter that essentially replaces every pixel of the drawable.
+            baseLayer.setColorFilter(
+                    new PorterDuffColorFilter(
+                            tintColor,
+                            // SRC operator discards the drawable's color+alpha
+                            PorterDuff.Mode.SRC));
+        } else {
+            baseLayer.mutate().setTintMode(PorterDuff.Mode.SRC_ATOP);
+            baseLayer.setTint(tintColor);
         }
+        mTintColor = tintColor;
         setStatefulColors();
         invalidate();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 51569c1..d97e25f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_OTP;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
@@ -59,7 +59,6 @@
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
 import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
@@ -237,7 +236,7 @@
                     );
         }
         if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
-            if (bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+            if (bindParams.redactionType == REDACTION_TYPE_OTP) {
                 result.mPublicInflatedSingleLineViewModel =
                         SingleLineViewInflater.inflateSingleLineViewModel(
                                 entry.getSbn().getNotification(),
@@ -470,11 +469,11 @@
             if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
                 logger.logAsyncTaskProgress(row.getLoggingKey(), "creating public remote view");
                 if (LockscreenOtpRedaction.isEnabled()
-                        && bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+                        && bindParams.redactionType == REDACTION_TYPE_OTP) {
                     result.newPublicView = createSensitiveContentMessageNotification(
                             NotificationBundleUi.isEnabled()
                                     ? row.getEntryAdapter().getSbn().getNotification()
-                                    : row.getEntry().getSbn().getNotification(),
+                                    : row.getEntryLegacy().getSbn().getNotification(),
                             builder.getStyle(),
                             systemUiContext, packageContext).createContentView();
                 } else {
@@ -815,7 +814,7 @@
                     existingWrapper.onReinflated();
                 }
             } catch (Exception e) {
-                handleInflationError(runningInflations, e, row, callback, logger,
+                handleInflationError(runningInflations, e, row, entry, callback, logger,
                         "applying view synchronously");
                 // Add a running inflation to make sure we don't trigger callbacks.
                 // Safe to do because only happens in tests.
@@ -837,7 +836,7 @@
                 String invalidReason = isValidView(v, entry, row.getResources());
                 if (invalidReason != null) {
                     handleInflationError(runningInflations, new InflationException(invalidReason),
-                            row, callback, logger, "applied invalid view");
+                            row, entry, callback, logger, "applied invalid view");
                     runningInflations.remove(inflationId);
                     return;
                 }
@@ -874,7 +873,7 @@
                     onViewApplied(newView);
                 } catch (Exception anotherException) {
                     runningInflations.remove(inflationId);
-                    handleInflationError(runningInflations, e, row,
+                    handleInflationError(runningInflations, e, row, entry,
                             callback, logger, "applying view");
                 }
             }
@@ -970,13 +969,14 @@
 
     private static void handleInflationError(
             HashMap<Integer, CancellationSignal> runningInflations, Exception e,
-            ExpandableNotificationRow row, @Nullable InflationCallback callback,
+            ExpandableNotificationRow row, NotificationEntry entry,
+            @Nullable InflationCallback callback,
             NotificationRowContentBinderLogger logger, String logContext) {
         Assert.isMainThread();
         logger.logAsyncTaskException(row.getLoggingKey(), logContext, e);
         runningInflations.values().forEach(CancellationSignal::cancel);
         if (callback != null) {
-            callback.handleInflationException(row.getEntry(), e);
+            callback.handleInflationException(entry, e);
         }
     }
 
@@ -1006,10 +1006,6 @@
             entry.setPromotedNotificationContentModel(result.mPromotedContent);
         }
 
-        if (PromotedNotificationUiForceExpanded.isEnabled()) {
-            row.setPromotedOngoing(entry.isOngoingPromoted());
-        }
-
         boolean setRepliesAndActions = true;
         if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
             if (result.inflatedContentView != null) {
@@ -1360,7 +1356,7 @@
             }
 
             if (LockscreenOtpRedaction.isSingleLineViewEnabled()) {
-                if (mBindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+                if (mBindParams.redactionType == REDACTION_TYPE_OTP) {
                     result.mPublicInflatedSingleLineViewModel =
                             SingleLineViewInflater.inflateSingleLineViewModel(
                                     mEntry.getSbn().getNotification(),
@@ -1448,7 +1444,7 @@
                     + Integer.toHexString(sbn.getId());
             Log.e(CentralSurfaces.TAG, "couldn't inflate view for notification " + ident, e);
             if (mCallback != null) {
-                mCallback.handleInflationException(mRow.getEntry(),
+                mCallback.handleInflationException(mEntry,
                         new InflationException("Couldn't inflate contentViews" + e));
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 1932037..cb1e898 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static android.app.Flags.notificationsRedesignTemplates;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Flags;
@@ -267,6 +269,7 @@
         mNotificationMaxHeight = maxHeight;
     }
 
+    // This logic is mirrored in FrameLayoutWithMaxHeight.onMeasure in AODPromotedNotification.kt.
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
@@ -293,8 +296,8 @@
                 useExactly = true;
             }
             int spec = MeasureSpec.makeMeasureSpec(size, useExactly
-                            ? MeasureSpec.EXACTLY
-                            : MeasureSpec.AT_MOST);
+                    ? MeasureSpec.EXACTLY
+                    : MeasureSpec.AT_MOST);
             measureChildWithMargins(mExpandedChild, widthMeasureSpec, 0, spec, 0);
             maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
         }
@@ -598,7 +601,7 @@
         if (NotificationBundleUi.isEnabled()) {
             return mContainingNotification.getEntryAdapter().getSbn();
         } else {
-            return mContainingNotification.getEntry().getSbn();
+            return mContainingNotification.getEntryLegacy().getSbn();
         }
     }
 
@@ -719,16 +722,19 @@
      *         height, the notification is clipped instead of being further shrunk.
      */
     private int getMinContentHeightHint() {
+        int actionListHeight = mContext.getResources().getDimensionPixelSize(
+                notificationsRedesignTemplates()
+                        ? com.android.internal.R.dimen.notification_2025_action_list_height
+                        : com.android.internal.R.dimen.notification_action_list_height);
         if (mIsChildInGroup && isVisibleOrTransitioning(VISIBLE_TYPE_SINGLELINE)) {
-            return mContext.getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.notification_action_list_height);
+            return actionListHeight;
         }
 
         // Transition between heads-up & expanded, or pinned.
         if (mHeadsUpChild != null && mExpandedChild != null) {
             boolean transitioningBetweenHunAndExpanded =
                     isTransitioningFromTo(VISIBLE_TYPE_HEADSUP, VISIBLE_TYPE_EXPANDED) ||
-                    isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
+                            isTransitioningFromTo(VISIBLE_TYPE_EXPANDED, VISIBLE_TYPE_HEADSUP);
             boolean pinned = !isVisibleOrTransitioning(VISIBLE_TYPE_CONTRACTED)
                     && (mIsHeadsUp || mHeadsUpAnimatingAway)
                     && mContainingNotification.canShowHeadsUp();
@@ -756,9 +762,7 @@
         } else if (mExpandedChild != null) {
             hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
         } else if (mContractedChild != null) {
-            hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
-                    + mContext.getResources().getDimensionPixelSize(
-                            com.android.internal.R.dimen.notification_action_list_height);
+            hint = getViewHeight(VISIBLE_TYPE_CONTRACTED) + actionListHeight;
         } else {
             hint = getMinHeight();
         }
@@ -1053,8 +1057,8 @@
         // the original type
         final int visibleType = (
                 isGroupExpanded() || mContainingNotification.isUserLocked())
-                    ? calculateVisibleType()
-                    : getVisibleType();
+                ? calculateVisibleType()
+                : getVisibleType();
         return getBackgroundColor(visibleType);
     }
 
@@ -1240,7 +1244,14 @@
                 height = mContentHeight;
             }
             int expandedVisualType = getVisualTypeForHeight(height);
-            int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
+            final boolean shouldShowSingleLineView = mIsChildInGroup && !isGroupExpanded();
+            final boolean isSingleLineViewPresent = mSingleLineView != null;
+
+            if (shouldShowSingleLineView && !isSingleLineViewPresent) {
+                Log.e(TAG, "calculateVisibleType: SingleLineView is not available!");
+            }
+
+            final int collapsedVisualType = shouldShowSingleLineView && isSingleLineViewPresent
                     ? VISIBLE_TYPE_SINGLELINE
                     : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
             return mTransformationStartVisibleType == collapsedVisualType
@@ -1261,7 +1272,13 @@
         if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
             return VISIBLE_TYPE_EXPANDED;
         }
-        if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
+        final boolean shouldShowSingleLineView = mIsChildInGroup && !isGroupExpanded();
+        final boolean isSingleLinePresent =  mSingleLineView != null;
+        if (shouldShowSingleLineView && !isSingleLinePresent) {
+            Log.e(TAG, "getVisualTypeForHeight: singleLineView is not available.");
+        }
+
+        if (!mUserExpanding && shouldShowSingleLineView && isSingleLinePresent) {
             return VISIBLE_TYPE_SINGLELINE;
         }
 
@@ -1276,7 +1293,7 @@
             if (noExpandedChild || (mContractedChild != null
                     && viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
                     && (!mIsChildInGroup || isGroupExpanded()
-                            || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
+                    || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
                 return VISIBLE_TYPE_CONTRACTED;
             } else if (!noExpandedChild) {
                 return VISIBLE_TYPE_EXPANDED;
@@ -1576,20 +1593,26 @@
             return;
         }
         ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
-        View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
-        ViewGroup actionListMarginTarget = layout.findViewById(
-                com.android.internal.R.id.notification_action_list_margin_target);
-        if (bubbleButton == null || actionContainer == null) {
+        // With the new design, the actions_container should always be visible to act as padding
+        // when there are no actions. We're making its child visible/invisible instead.
+        View actionsContainerForVisibilityChange = layout.findViewById(
+                notificationsRedesignTemplates()
+                        ? com.android.internal.R.id.actions_container_layout
+                        : com.android.internal.R.id.actions_container);
+        if (bubbleButton == null || actionsContainerForVisibilityChange == null) {
             return;
         }
 
         if (shouldShowBubbleButton(entry)) {
+            boolean isBubble = NotificationBundleUi.isEnabled()
+                    ? mContainingNotification.getEntryAdapter().isBubbleCapable()
+                    : entry.isBubble();
             // explicitly resolve drawable resource using SystemUI's theme
-            Drawable d = mContext.getDrawable(entry.isBubble()
+            Drawable d = mContext.getDrawable(isBubble
                     ? com.android.wm.shell.R.drawable.bubble_ic_stop_bubble
                     : com.android.wm.shell.R.drawable.bubble_ic_create_bubble);
 
-            String contentDescription = mContext.getResources().getString(entry.isBubble()
+            String contentDescription = mContext.getResources().getString(isBubble
                     ? R.string.notification_conversation_unbubble
                     : R.string.notification_conversation_bubble);
 
@@ -1597,15 +1620,14 @@
             bubbleButton.setImageDrawable(d);
             bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener());
             bubbleButton.setVisibility(VISIBLE);
-            actionContainer.setVisibility(VISIBLE);
-            // Set notification_action_list_margin_target's bottom margin to 0 when showing bubble
-            if (actionListMarginTarget != null) {
-                ViewGroup.LayoutParams lp = actionListMarginTarget.getLayoutParams();
-                if (lp instanceof ViewGroup.MarginLayoutParams) {
-                    final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
-                    if (mlp.bottomMargin > 0) {
-                        mlp.setMargins(mlp.leftMargin, mlp.topMargin, mlp.rightMargin, 0);
-                    }
+            actionsContainerForVisibilityChange.setVisibility(VISIBLE);
+            if (!notificationsRedesignTemplates()) {
+                // Set notification_action_list_margin_target's bottom margin to 0 when showing
+                // bubble
+                ViewGroup actionListMarginTarget = layout.findViewById(
+                        com.android.internal.R.id.notification_action_list_margin_target);
+                if (actionListMarginTarget != null) {
+                    removeBottomMargin(actionListMarginTarget);
                 }
             }
         } else  {
@@ -1613,6 +1635,16 @@
         }
     }
 
+    private static void removeBottomMargin(ViewGroup actionListMarginTarget) {
+        ViewGroup.LayoutParams lp = actionListMarginTarget.getLayoutParams();
+        if (lp instanceof MarginLayoutParams) {
+            final MarginLayoutParams mlp = (MarginLayoutParams) lp;
+            if (mlp.bottomMargin > 0) {
+                mlp.setMargins(mlp.leftMargin, mlp.topMargin, mlp.rightMargin, 0);
+            }
+        }
+    }
+
     @MainThread
     public void setBubblesEnabledForUser(boolean enabled) {
         mBubblesEnabledForUser = enabled;
@@ -1623,12 +1655,18 @@
 
     @VisibleForTesting
     boolean shouldShowBubbleButton(NotificationEntry entry) {
-        boolean isPersonWithShortcut =
-                mPeopleIdentifier.getPeopleNotificationType(entry)
-                        >= PeopleNotificationIdentifier.TYPE_FULL_PERSON;
+        int peopleType = NotificationBundleUi.isEnabled()
+                ? mContainingNotification.getEntryAdapter().getPeopleNotificationType()
+                : mPeopleIdentifier.getPeopleNotificationType(entry);
+        Notification.BubbleMetadata bubbleMetadata = NotificationBundleUi.isEnabled()
+                ? mContainingNotification.getEntryAdapter().getSbn().getNotification()
+                        .getBubbleMetadata()
+                : entry.getBubbleMetadata();
+        boolean isPersonWithShortcut = peopleType
+                >= PeopleNotificationIdentifier.TYPE_FULL_PERSON;
         return mBubblesEnabledForUser
                 && isPersonWithShortcut
-                && entry.getBubbleMetadata() != null;
+                && bubbleMetadata != null;
     }
 
     private void applySnoozeAction(View layout) {
@@ -1636,8 +1674,13 @@
             return;
         }
         ImageView snoozeButton = layout.findViewById(com.android.internal.R.id.snooze_button);
-        View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
-        if (snoozeButton == null || actionContainer == null) {
+        // With the new design, the actions_container should always be visible to act as padding
+        // when there are no actions. We're making its child visible/invisible instead.
+        View actionsContainerForVisibilityChange = layout.findViewById(
+                notificationsRedesignTemplates()
+                        ? com.android.internal.R.id.actions_container_layout
+                        : com.android.internal.R.id.actions_container);
+        if (snoozeButton == null || actionsContainerForVisibilityChange == null) {
             return;
         }
         // Notification.Builder can 'disable' the snooze button to prevent it from being shown here
@@ -1663,7 +1706,7 @@
         snoozeButton.setOnClickListener(
                 mContainingNotification.getSnoozeClickListener(snoozeMenuItem));
         snoozeButton.setVisibility(VISIBLE);
-        actionContainer.setVisibility(VISIBLE);
+        actionsContainerForVisibilityChange.setVisibility(VISIBLE);
     }
 
     private void applySmartReplyView() {
@@ -1687,7 +1730,7 @@
                             : smartReplies.fromAssistant;
                     boolean editBeforeSending = smartReplies != null
                             && mSmartReplyConstants.getEffectiveEditChoicesBeforeSending(
-                                    smartReplies.remoteInput.getEditChoicesBeforeSending());
+                            smartReplies.remoteInput.getEditChoicesBeforeSending());
 
                     mSmartReplyController.smartSuggestionsAdded(mNotificationEntry, numSmartReplies,
                             numSmartActions, fromAssistant, editBeforeSending);
@@ -2114,8 +2157,8 @@
     public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
         boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded);
         if (mUserExpanding) {
-             needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
-                     bottomRounded);
+            needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded,
+                    bottomRounded);
         }
         return needsPaddings;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 9a75253..cdb78d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -47,7 +47,6 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.settingslib.notification.ConversationIconFactory;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.Flags;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -71,6 +70,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
 import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -100,6 +100,7 @@
     private final AccessibilityManager mAccessibilityManager;
     private final HighPriorityProvider mHighPriorityProvider;
     private final ChannelEditorDialogController mChannelEditorDialogController;
+    private final PackageDemotionInteractor mPackageDemotionInteractor;
     private final OnUserInteractionCallback mOnUserInteractionCallback;
 
     // Dependencies:
@@ -155,6 +156,7 @@
             LauncherApps launcherApps,
             ShortcutManager shortcutManager,
             ChannelEditorDialogController channelEditorDialogController,
+            PackageDemotionInteractor packageDemotionInteractor,
             UserContextProvider contextTracker,
             AssistantFeedbackController assistantFeedbackController,
             Optional<BubblesManager> bubblesManagerOptional,
@@ -184,6 +186,7 @@
         mShortcutManager = shortcutManager;
         mContextTracker = contextTracker;
         mChannelEditorDialogController = channelEditorDialogController;
+        mPackageDemotionInteractor = packageDemotionInteractor;
         mAssistantFeedbackController = assistantFeedbackController;
         mBubblesManagerOptional = bubblesManagerOptional;
         mUiEventLogger = uiEventLogger;
@@ -231,15 +234,6 @@
         }
     }
 
-    public void onDensityOrFontScaleChanged(NotificationEntry entry) {
-        if (!Flags.notificationUndoGutsOnConfigChanged()) {
-            Log.wtf(TAG, "onDensityOrFontScaleChanged should not be called if"
-                    + " notificationUndoGutsOnConfigChanged is off");
-        }
-        setExposedGuts(entry.getGuts());
-        bindGuts(entry.getRow());
-    }
-
     /**
      * Sends an intent to open the notification settings for a particular package and optional
      * channel.
@@ -291,11 +285,6 @@
         mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
     }
 
-    private boolean bindGuts(final ExpandableNotificationRow row) {
-        row.ensureGutsInflated();
-        return bindGuts(row, mGutsMenuItem);
-    }
-
     @VisibleForTesting
     protected boolean bindGuts(final ExpandableNotificationRow row,
             NotificationMenuRowPlugin.MenuItem item) {
@@ -429,6 +418,7 @@
                 mIconStyleProvider,
                 mOnUserInteractionCallback,
                 mChannelEditorDialogController,
+                mPackageDemotionInteractor,
                 packageName,
                 row.getEntry().getChannel(),
                 row.getEntry(),
@@ -440,6 +430,7 @@
                 NotificationBundleUi.isEnabled()
                         ? !row.getEntry().isBlockable()
                         : row.getIsNonblockable(),
+                row.canViewBeDismissed(),
                 mHighPriorityProvider.isHighPriority(row.getEntry()),
                 mAssistantFeedbackController,
                 mMetricsLogger,
@@ -605,6 +596,7 @@
         return mNotificationGutsExposed;
     }
 
+    @VisibleForTesting
     public void setExposedGuts(NotificationGuts guts) {
         mNotificationGutsExposed = guts;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 6611225..b6f4ffc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -74,6 +74,7 @@
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 
@@ -120,6 +121,7 @@
     private boolean mIsAutomaticChosen;
     private boolean mIsSingleDefaultChannel;
     private boolean mIsNonblockable;
+    private boolean mIsDismissable;
     private NotificationEntry mEntry;
     private StatusBarNotification mSbn;
     private boolean mIsDeviceProvisioned;
@@ -160,6 +162,7 @@
         mPressedApply = true;
         mGutsContainer.closeControls(v, /* save= */ true);
     };
+    private OnClickListener mOnCloseClickListener;
 
     public NotificationInfo(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -193,6 +196,7 @@
             NotificationIconStyleProvider iconStyleProvider,
             OnUserInteractionCallback onUserInteractionCallback,
             ChannelEditorDialogController channelEditorDialogController,
+            PackageDemotionInteractor packageDemotionInteractor,
             String pkg,
             NotificationChannel notificationChannel,
             NotificationEntry entry,
@@ -202,6 +206,7 @@
             UiEventLogger uiEventLogger,
             boolean isDeviceProvisioned,
             boolean isNonblockable,
+            boolean isDismissable,
             boolean wasShownHighPriority,
             AssistantFeedbackController assistantFeedbackController,
             MetricsLogger metricsLogger,
@@ -226,11 +231,13 @@
         mStartingChannelImportance = mSingleNotificationChannel.getImportance();
         mWasShownHighPriority = wasShownHighPriority;
         mIsNonblockable = isNonblockable;
+        mIsDismissable = isDismissable;
         mAppUid = mSbn.getUid();
         mDelegatePkg = mSbn.getOpPkg();
         mIsDeviceProvisioned = isDeviceProvisioned;
         mShowAutomaticSetting = mAssistantFeedbackController.isFeedbackEnabled();
         mUiEventLogger = uiEventLogger;
+        mOnCloseClickListener = onCloseClick;
 
         mIsSystemRegisteredCall = mSbn.getNotification().isStyle(Notification.CallStyle.class)
                 && mINotificationManager.isInCall(mSbn.getPackageName(), mSbn.getUid());
@@ -277,6 +284,11 @@
         turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
                 ? VISIBLE : GONE);
 
+        View dismissButton = findViewById(R.id.inline_dismiss);
+        dismissButton.setOnClickListener(mOnCloseClickListener);
+        dismissButton.setVisibility(dismissButton.hasOnClickListeners() && mIsDismissable
+                ? VISIBLE : GONE);
+
         View done = findViewById(R.id.done);
         done.setOnClickListener(mOnDismissSettings);
         done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index e89a76f..977936f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -49,6 +49,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
@@ -261,15 +262,19 @@
             mSnoozeItem = createSnoozeItem(mContext);
         }
         mFeedbackItem = createFeedbackItem(mContext);
-        NotificationEntry entry = mParent.getEntry();
-        int personNotifType = mPeopleNotificationIdentifier.getPeopleNotificationType(entry);
+        int personNotifType = NotificationBundleUi.isEnabled()
+                ? mParent.getEntryAdapter().getPeopleNotificationType()
+                : mPeopleNotificationIdentifier.getPeopleNotificationType(mParent.getEntryLegacy());
+        StatusBarNotification sbn = NotificationBundleUi.isEnabled()
+                ? mParent.getEntryAdapter().getSbn()
+                : mParent.getEntryLegacy().getSbn();
         if (personNotifType == PeopleNotificationIdentifier.TYPE_PERSON) {
             mInfoItem = createPartialConversationItem(mContext);
         } else if (personNotifType >= PeopleNotificationIdentifier.TYPE_FULL_PERSON) {
             mInfoItem = createConversationItem(mContext);
         } else if (android.app.Flags.uiRichOngoing()
                 && Flags.permissionHelperUiRichOngoing()
-                && entry.getSbn().getNotification().isPromotedOngoing()) {
+                && sbn.getNotification().isPromotedOngoing()) {
             mInfoItem = createPromotedItem(mContext);
         } else {
             mInfoItem = createInfoItem(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 482b315..ae52db8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -16,7 +16,6 @@
 package com.android.systemui.statusbar.notification.row
 
 import android.annotation.SuppressLint
-import android.app.Flags
 import android.app.Notification
 import android.app.Notification.EXTRA_SUMMARIZED_CONTENT
 import android.app.Notification.MessagingStyle
@@ -45,7 +44,7 @@
 import com.android.systemui.dagger.qualifiers.NotifInflation
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.InflationTask
-import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_OTP
 import com.android.systemui.statusbar.NotificationRemoteInputManager
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
 import com.android.systemui.statusbar.notification.InflationException
@@ -53,7 +52,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.logKey
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED
 import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED
@@ -543,7 +541,7 @@
             val ident: String = (sbn.packageName + "/0x" + Integer.toHexString(sbn.id))
             Log.e(TAG, "couldn't inflate view for notification $ident", e)
             callback?.handleInflationException(
-                if (NotificationBundleUi.isEnabled) entry else row.entry,
+                if (NotificationBundleUi.isEnabled) entry else row.entryLegacy,
                 InflationException("Couldn't inflate contentViews$e"),
             )
 
@@ -759,7 +757,7 @@
                         entry.logKey,
                         "inflating public single line view model",
                     )
-                    if (bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
+                    if (bindParams.redactionType == REDACTION_TYPE_OTP) {
                         SingleLineViewInflater.inflateSingleLineViewModel(
                             notification = entry.sbn.notification,
                             messagingStyle = messagingStyle,
@@ -877,7 +875,7 @@
                         logger.logAsyncTaskProgress(row.loggingKey, "creating public remote view")
                         if (
                             LockscreenOtpRedaction.isEnabled &&
-                                bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT
+                                bindParams.redactionType == REDACTION_TYPE_OTP
                         ) {
                             createSensitiveContentMessageNotification(
                                     entry.sbn.notification,
@@ -1293,6 +1291,7 @@
                         runningInflations,
                         e,
                         row,
+                        entry,
                         callback,
                         logger,
                         "applying view synchronously",
@@ -1318,6 +1317,7 @@
                                 runningInflations,
                                 InflationException(invalidReason),
                                 row,
+                                entry,
                                 callback,
                                 logger,
                                 "applied invalid view",
@@ -1377,6 +1377,7 @@
                                 runningInflations,
                                 e,
                                 row,
+                                entry,
                                 callback,
                                 logger,
                                 "applying view",
@@ -1480,6 +1481,7 @@
             runningInflations: HashMap<Int, CancellationSignal>,
             e: Exception,
             notification: ExpandableNotificationRow?,
+            entry: NotificationEntry,
             callback: InflationCallback?,
             logger: NotificationRowContentBinderLogger,
             logContext: String,
@@ -1487,7 +1489,7 @@
             Assert.isMainThread()
             logger.logAsyncTaskException(notification?.loggingKey, logContext, e)
             runningInflations.values.forEach(Consumer { obj: CancellationSignal -> obj.cancel() })
-            callback?.handleInflationException(notification?.entry, e)
+            callback?.handleInflationException(entry, e)
         }
 
         /**
@@ -1520,10 +1522,6 @@
                 entry.promotedNotificationContentModel = result.promotedContent
             }
 
-            if (PromotedNotificationUiForceExpanded.isEnabled) {
-                row.setPromotedOngoing(entry.isOngoingPromoted())
-            }
-
             result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) }
 
             setContentViewsFromRemoteViews(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 83897f5..cec0ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -51,7 +51,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Flags;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.res.R;
@@ -477,7 +476,7 @@
 
     @Override
     public boolean handleCloseControls(boolean save, boolean force) {
-        if (Flags.notificationUndoGutsOnConfigChanged() && !save) {
+        if (!save) {
             // Undo changes and let the guts handle closing the view
             mSelectedOption = null;
             showSnoozeOptions(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
index 6ff711d..01ee788 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
@@ -31,6 +31,7 @@
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
 import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
 import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 
@@ -42,6 +43,7 @@
 public class PromotedNotificationInfo extends NotificationInfo {
     private static final String TAG = "PromotedNotifInfoGuts";
     private INotificationManager mNotificationManager;
+    private PackageDemotionInteractor mPackageDemotionInteractor;
     private NotificationGuts mGutsContainer;
 
     public PromotedNotificationInfo(Context context, AttributeSet attrs) {
@@ -56,6 +58,7 @@
             NotificationIconStyleProvider iconStyleProvider,
             OnUserInteractionCallback onUserInteractionCallback,
             ChannelEditorDialogController channelEditorDialogController,
+            PackageDemotionInteractor packageDemotionInteractor,
             String pkg,
             NotificationChannel notificationChannel,
             NotificationEntry entry,
@@ -65,16 +68,19 @@
             UiEventLogger uiEventLogger,
             boolean isDeviceProvisioned,
             boolean isNonblockable,
+            boolean isDismissable,
             boolean wasShownHighPriority,
             AssistantFeedbackController assistantFeedbackController,
             MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException {
         super.bindNotification(pm, iNotificationManager, appIconProvider, iconStyleProvider,
-                onUserInteractionCallback, channelEditorDialogController, pkg, notificationChannel,
+                onUserInteractionCallback, channelEditorDialogController, packageDemotionInteractor,
+                pkg, notificationChannel,
                 entry, onSettingsClick, onAppSettingsClick, feedbackClickListener, uiEventLogger,
-                isDeviceProvisioned, isNonblockable, wasShownHighPriority,
+                isDeviceProvisioned, isNonblockable, isDismissable, wasShownHighPriority,
                 assistantFeedbackController, metricsLogger, onCloseClick);
 
         mNotificationManager = iNotificationManager;
+        mPackageDemotionInteractor = packageDemotionInteractor;
 
         bindDemote(entry.getSbn(), pkg);
     }
@@ -94,8 +100,8 @@
     private OnClickListener getDemoteClickListener(StatusBarNotification sbn, String packageName) {
         return ((View v) -> {
             try {
-                // TODO(b/391661009): Signal AutomaticPromotionCoordinator here
                 mNotificationManager.setCanBePromoted(packageName, sbn.getUid(), false, true);
+                mPackageDemotionInteractor.onPackageDemoted(packageName, sbn.getUid());
                 mGutsContainer.closeControls(v, true);
             } catch (RemoteException e) {
                 Log.e(TAG, "Couldn't revoke live update permission", e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
index 08c1d71..03990bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
@@ -87,9 +87,15 @@
                 // It's not a system app at all.
                 return false
             } else {
-                // If there's no launch intent, it's probably a headless app.
-                val pm = context.packageManager
-                return (pm.getLaunchIntentForPackage(info.packageName) == null)
+                // If there's no launch intent, it's probably a headless app. Check for both
+                // direct-aware and -unaware intents; otherwise this will almost certainly fail
+                // for notifications posted before unlocking.
+                val packageLaunchIntent =
+                    context.packageManager.getLaunchIntentForPackage(
+                        info.packageName,
+                        /* includeDirectBootUnaware= */ true,
+                    )
+                return packageLaunchIntent == null
             }
         } else {
             // If for some reason we don't have the app info, we don't know; best assume it's
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
index 4082a5b..2c5b9f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
@@ -61,7 +61,7 @@
         row: ExpandableNotificationRow,
         context: Context,
     ): NotificationIconProvider {
-        val sbn = if (NotificationBundleUi.isEnabled) row.entryAdapter?.sbn else  row.entry.sbn
+        val sbn = if (NotificationBundleUi.isEnabled) row.entryAdapter?.sbn else row.entryLegacy.sbn
         if (sbn == null) {
             return object : NotificationIconProvider {
                 override fun shouldShowAppIcon(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
index 8984f2c..f0b5c36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
@@ -50,15 +50,6 @@
                 view.registerListenersWhileAttached(touchHandler)
             }
         }
-        view.repeatWhenAttached {
-            repeatOnLifecycle(Lifecycle.State.STARTED) {
-                launch {
-                    viewModel.isBlurSupported.collect { supported ->
-                        view.setIsBlurSupported(supported)
-                    }
-                }
-            }
-        }
     }
 
     private suspend fun ActivatableNotificationView.registerListenersWhileAttached(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
index 9a86ffb..2d2fd60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.notification.row.ui.viewmodel
 
 import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
-import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
 import dagger.Module
 import dagger.Provides
 import kotlinx.coroutines.flow.Flow
@@ -27,26 +26,21 @@
 interface ActivatableNotificationViewModel : ExpandableOutlineViewModel {
     /** Does the view react to touches? */
     val isTouchable: Flow<Boolean>
-    val isBlurSupported: Flow<Boolean>
 
     companion object {
         operator fun invoke(
             a11yInteractor: AccessibilityInteractor,
-            windowRootViewBlurInteractor: WindowRootViewBlurInteractor
         ): ActivatableNotificationViewModel =
-            ActivatableNotificationViewModelImpl(a11yInteractor, windowRootViewBlurInteractor)
+            ActivatableNotificationViewModelImpl(a11yInteractor)
     }
 }
 
 private class ActivatableNotificationViewModelImpl(
     a11yInteractor: AccessibilityInteractor,
-    windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
 ) : ActivatableNotificationViewModel {
     override val isTouchable: Flow<Boolean> =
         // If a11y touch exploration is enabled, then the activatable view should ignore touches
         a11yInteractor.isTouchExplorationEnabled.map { !it }
-    override val isBlurSupported: Flow<Boolean> =
-        windowRootViewBlurInteractor.isBlurCurrentlySupported
 }
 
 @Module
@@ -54,7 +48,6 @@
     @Provides
     fun provideViewModel(
         a11yInteractor: AccessibilityInteractor,
-        windowRootViewBlurInteractor: WindowRootViewBlurInteractor
     ) =
-        ActivatableNotificationViewModel(a11yInteractor, windowRootViewBlurInteractor)
+        ActivatableNotificationViewModel(a11yInteractor)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index f492b25..e266dad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -50,7 +50,7 @@
         resolveViews();
         updateImageTag(NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getSbn()
-                : row.getEntry().getSbn());
+                : row.getEntryLegacy().getSbn());
     }
 
     private void resolveViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
index dec674c..71bb9a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
@@ -47,7 +47,7 @@
         // the transformation types and we need to have our values set by then.
         resolveViews(NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getSbn()
-                : row.getEntry().getSbn());
+                : row.getEntryLegacy().getSbn());
         super.onContentUpdated(row);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 585051a..e6dadcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -225,7 +225,7 @@
         super.onContentUpdated(row);
         mIsLowPriority = NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().isAmbient()
-                : row.getEntry().isAmbient();
+                : row.getEntryLegacy().isAmbient();
         mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
         ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
 
@@ -236,7 +236,7 @@
         updateCropToPaddingForImageViews();
         Notification n = NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getSbn().getNotification()
-                : row.getEntry().getSbn().getNotification();
+                : row.getEntryLegacy().getSbn().getNotification();
         mIcon.setTag(ImageTransformState.ICON_TAG, n.getSmallIcon());
 
         // We need to reset all views that are no longer transforming in case a view was previously
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 8176d2a..19321dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -53,8 +53,8 @@
 import com.android.systemui.statusbar.notification.TransformState;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.HybridNotificationView;
-import com.android.systemui.util.DimensionKt;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
+import com.android.systemui.util.DimensionKt;
 
 import java.util.function.Consumer;
 
@@ -327,7 +327,7 @@
         // the transformation types and we need to have our values set by then.
         resolveTemplateViews(NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getSbn()
-                : row.getEntry().getSbn());
+                : row.getEntryLegacy().getSbn());
         super.onContentUpdated(row);
         // With the modern templates, a large icon visually overlaps the header, so we can't
         // hide the header, we must show it.
@@ -411,7 +411,8 @@
     @Override
     public int getExtraMeasureHeight() {
         int extra = 0;
-        if (mActions != null) {
+        if (!notificationsRedesignTemplates() && mActions != null) {
+            // With the redesign, this should always be 0.
             extra = mActions.getExtraMeasureHeight();
         }
         if (mRemoteInputHistory != null && mRemoteInputHistory.getVisibility() != View.GONE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 64babb2..35e286c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -83,7 +83,7 @@
             if (NotificationBundleUi.isEnabled()
                     ? row.getEntryAdapter().getSbn().getNotification().isStyle(
                     Notification.DecoratedCustomViewStyle.class)
-                    : row.getEntry().getSbn().getNotification().isStyle(
+                    : row.getEntryLegacy().getSbn().getNotification().isStyle(
                     Notification.DecoratedCustomViewStyle.class)) {
                 return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
             }
@@ -141,7 +141,7 @@
         // Apps targeting Q should fix their dark mode bugs.
         int targetSdk = NotificationBundleUi.isEnabled()
                 ? mRow.getEntryAdapter().getTargetSdk()
-                : mRow.getEntry().targetSdk;
+                : mRow.getEntryLegacy().targetSdk;
         if (targetSdk >= Build.VERSION_CODES.Q) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
index f00c3ae..53728c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
@@ -89,7 +89,8 @@
     init {
         if (!PromotedNotificationContentModel.featureFlagEnabled()) {
             if (promotedContent != null) {
-                Log.wtf(TAG, "passing non-null promoted content without feature flag enabled")
+                // TODO(b/401018545): convert to Log.wtf and fix tests (see: ag/32114199)
+                Log.e(TAG, "passing non-null promoted content without feature flag enabled")
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index f663ea0..a75330b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -18,6 +18,7 @@
 
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.app.tracing.traceSection
+import com.android.systemui.Flags
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.statusbar.NotificationShelf
@@ -47,6 +48,10 @@
                 launch { viewModel.isAlignedToEnd.collect(::setAlignedToEnd) }
             }
 
+            if (Flags.notificationRowTransparency()) {
+                launch { viewModel.isBlurSupported.collect(shelf::setIsBlurSupported) }
+            }
+
             registerViewListenersWhileAttached(shelf, viewModel)
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
index 96cdda6..9eb1ece 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.statusbar.NotificationShelf
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel
 import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor
+import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
@@ -32,6 +33,7 @@
 @Inject
 constructor(
     private val interactor: NotificationShelfInteractor,
+    windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
     activatableViewModel: ActivatableNotificationViewModel,
 ) : ActivatableNotificationViewModel by activatableViewModel {
     /** Is the shelf allowed to be clickable when it has content? */
@@ -51,6 +53,8 @@
         }
     }
 
+    val isBlurSupported: Flow<Boolean> = windowRootViewBlurInteractor.isBlurCurrentlySupported
+
     /** Notifies that the user has clicked the shelf. */
     fun onShelfClicked() {
         interactor.goToLockedShadeFromShelf()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 1e24952..fc7b24e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -36,13 +36,14 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository;
+import com.android.systemui.statusbar.notification.headsup.AvalancheController;
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController;
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.notification.headsup.AvalancheController;
 
 import java.io.PrintWriter;
 
@@ -424,6 +425,7 @@
     /** the bottom-most y position where we can draw pinned HUNs  */
     public float getHeadsUpBottom() {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+        NotificationsHunSharedAnimationValues.assertInLegacyMode();
         return mHeadsUpBottom;
     }
 
@@ -694,10 +696,13 @@
         return mPulsing;
     }
 
+    public boolean isPulsing(String entryKey) {
+        boolean isHeadsUp = mHeadsUpRepository.isHeadsUpEntry(entryKey);
+        return mPulsing && isHeadsUp;
+    }
+
     public boolean isPulsing(NotificationEntry entry) {
-        boolean isHeadsUp = NotificationBundleUi.isEnabled()
-                ? mHeadsUpRepository.isHeadsUpEntry(entry.getKey())
-                : entry.isHeadsUpEntry();
+        boolean isHeadsUp = entry.isHeadsUpEntry();
         return mPulsing && isHeadsUp;
     }
 
@@ -748,7 +753,10 @@
      * @return whether a row is dozing and not pulsing right now
      */
     public boolean isDozingAndNotPulsing(ExpandableNotificationRow row) {
-        return isDozing() && !isPulsing(row.getEntry());
+        boolean isPulsing = NotificationBundleUi.isEnabled()
+                ? isPulsing(row.getKey())
+                : isPulsing(row.getEntryLegacy());
+        return isDozing() && !isPulsing;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
index 9bd5a5b..5a23f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
@@ -181,7 +181,7 @@
                 it.setMagneticTranslation(targetTranslation)
             }
         }
-        playPullHaptics(mappedTranslation = swipedRowMultiplier * translation, canSwipedBeDismissed)
+        // TODO(b/399633875): Enable pull haptics after we have a clear and polished haptics design
     }
 
     private fun playPullHaptics(mappedTranslation: Float, canSwipedBeDismissed: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 315d37e..f9d8c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -444,7 +444,7 @@
         mIsConversation = isConversation;
         StatusBarNotification notification = NotificationBundleUi.isEnabled()
                 ? mContainingNotification.getEntryAdapter().getSbn()
-                : mContainingNotification.getEntry().getSbn();
+                : mContainingNotification.getEntryLegacy().getSbn();
         if (notification == null) {
             return;
         }
@@ -615,7 +615,7 @@
         RemoteViews header;
         StatusBarNotification notification = NotificationBundleUi.isEnabled()
                 ? mContainingNotification.getEntryAdapter().getSbn()
-                : mContainingNotification.getEntry().getSbn();
+                : mContainingNotification.getEntryLegacy().getSbn();
         if (notification == null) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 96f0e6f..b5562ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -167,7 +167,7 @@
             view === promoHeaderView -> BUCKET_PROMO
             view is ExpandableNotificationRow ->
                 if (NotificationBundleUi.isEnabled) view.entryAdapter?.sectionBucket
-                else view.entry.bucket
+                else view.entryLegacy.bucket
             else -> null
         }
 
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 c2c271b..9fea750 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
@@ -1039,10 +1039,10 @@
                 }
                 int bucket = NotificationBundleUi.isEnabled()
                         ? row.getEntryAdapter().getSectionBucket()
-                        : row.getEntry().getBucket();
+                        : row.getEntryLegacy().getBucket();
                 boolean isAmbient = NotificationBundleUi.isEnabled()
                         ? row.getEntryAdapter().isAmbient()
-                        : row.getEntry().isAmbient();
+                        : row.getEntryLegacy().isAmbient();
                 currentIndex++;
                 boolean beforeSpeedBump;
                 if (mHighPriorityBeforeSpeedBump) {
@@ -1847,7 +1847,7 @@
         } else {
             if (row.isChildInGroup()) {
                 final NotificationEntry groupSummary =
-                        mGroupMembershipManager.getGroupSummary(row.getEntry());
+                        mGroupMembershipManager.getGroupSummary(row.getEntryLegacy());
                 if (groupSummary != null) {
                     row = groupSummary.getRow();
                 }
@@ -2000,16 +2000,16 @@
             if ((bottom - top >= mMinInteractionHeight || !requireMinHeight)
                     && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 if (slidingChild instanceof ExpandableNotificationRow row) {
-                    NotificationEntry entry = row.getEntry();
                     boolean isEntrySummaryForTopHun;
                     if (NotificationBundleUi.isEnabled()) {
                         isEntrySummaryForTopHun = Objects.equals(
                                 ((ExpandableNotificationRow) slidingChild).getNotificationParent(),
                                 mTopHeadsUpRow);
                     } else {
+                        NotificationEntry entry = row.getEntryLegacy();
                         isEntrySummaryForTopHun = mTopHeadsUpRow != null &&
-                                mGroupMembershipManager.getGroupSummary(mTopHeadsUpRow.getEntry())
-                                == entry;
+                                mGroupMembershipManager.getGroupSummary(
+                                        mTopHeadsUpRow.getEntryLegacy()) == entry;
                     }
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
                             && mTopHeadsUpRow != row
@@ -3009,7 +3009,7 @@
             ExpandableNotificationRow childRow = (ExpandableNotificationRow) child;
             return NotificationBundleUi.isEnabled()
                     ? mGroupMembershipManager.isChildInGroup(childRow.getEntryAdapter())
-                    : mGroupMembershipManager.isChildInGroup(childRow.getEntry());
+                    : mGroupMembershipManager.isChildInGroup(childRow.getEntryLegacy());
         }
         return false;
     }
@@ -3854,8 +3854,6 @@
                         // existing overScroll, we have to scroll the view
                         customOverScrollBy((int) scrollAmount, getOwnScrollY(),
                                 range, getHeight() / 2);
-                        // If we're scrolling, leavebehinds should be dismissed
-                        mController.checkSnoozeLeavebehind();
                     }
                 }
                 break;
@@ -6475,7 +6473,7 @@
             @SelectedRows int selection) {
         int bucket = NotificationBundleUi.isEnabled()
                 ? row.getEntryAdapter().getSectionBucket()
-                : row.getEntry().getBucket();
+                : row.getEntryLegacy().getBucket();
         switch (selection) {
             case ROWS_ALL:
                 return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index bb3abc1..f3d8ee2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -648,11 +648,11 @@
                 public void onChildSnappedBack(View animView, float targetLeft) {
                     mView.onSwipeEnd();
                     if (animView instanceof ExpandableNotificationRow row) {
-                        if (row.isPinned() && !canChildBeDismissed(row)
-                                && NotificationBundleUi.isEnabled()
+                        boolean cannotFullScreen = NotificationBundleUi.isEnabled()
                                 ? !row.getEntryAdapter().isFullScreenCapable()
-                                : (row.getEntry().getSbn().getNotification().fullScreenIntent
-                                        == null)) {
+                                : (row.getEntryLegacy().getSbn().getNotification().fullScreenIntent
+                                        == null);
+                        if (row.isPinned() && !canChildBeDismissed(row) && cannotFullScreen) {
                             mHeadsUpManager.removeNotification(
                                     row.getKey(),
                                     /* removeImmediately= */ true,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index fcb63df..e5071d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -413,7 +413,7 @@
                         (currentNotification as? ExpandableNotificationRow)?.entryAdapter
                     counter.incrementForBucket(entryAdapter?.sectionBucket)
                 } else {
-                    val entry = (currentNotification as? ExpandableNotificationRow)?.entry
+                    val entry = (currentNotification as? ExpandableNotificationRow)?.entryLegacy
                     counter.incrementForBucket(entry?.bucket)
                 }
             }
@@ -470,7 +470,7 @@
             calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)
         val canPeek = view is ExpandableNotificationRow &&
                 if (NotificationBundleUi.isEnabled) view.entryAdapter?.canPeek() == true
-                else view.entry.isStickyAndNotDemoted
+                else view.entryLegacy.isStickyAndNotDemoted
 
         var size =
             if (onLockscreen) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index d23a4c6..da14423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -927,7 +927,7 @@
                             childState.headsUpIsVisible, row.showingPulsing(),
                             ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
                                     ? row.getEntryAdapter().canPeek()
-                                    : row.getEntry().isStickyAndNotDemoted())) {
+                                    : row.getEntryLegacy().isStickyAndNotDemoted())) {
                         // the height of this child before clamping it to the top
                         float unmodifiedChildHeight = childState.height;
                         clampHunToTop(
@@ -967,9 +967,12 @@
                             childState.setZTranslation(baseZ);
                         }
                         if (isTopEntry && row.isAboveShelf()) {
+                            float headsUpBottom = NotificationsHunSharedAnimationValues.isEnabled()
+                                    ? mHeadsUpAnimator.getHeadsUpAppearHeightBottom()
+                                    : ambientState.getHeadsUpBottom();
                             clampHunToMaxTranslation(
                                     /* headsUpTop =  */ headsUpTranslation,
-                                    /* headsUpBottom =  */ ambientState.getHeadsUpBottom(),
+                                    /* headsUpBottom =  */ headsUpBottom,
                                     /* viewState = */ childState
                             );
                             updateCornerRoundnessForPinnedHun(row, ambientState.getStackTop());
@@ -981,7 +984,7 @@
                             childState.headsUpIsVisible, row.showingPulsing(),
                             ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
                                     ? row.getEntryAdapter().canPeek()
-                                    : row.getEntry().isStickyAndNotDemoted())) {
+                                    : row.getEntryLegacy().isStickyAndNotDemoted())) {
                         // Ensure that the heads up is always visible even when scrolled off.
                         // NSSL y starts at top of screen in non-split-shade, but below the qs
                         // offset
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
index bc53314..afe7971 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
@@ -60,7 +60,7 @@
                         if (animationsEnabled) {
                             added.forEach { key ->
                                 val row = obtainView(key)
-                                val hasStatusBarChip = statusBarChips.contains(row.entry.key)
+                                val hasStatusBarChip = statusBarChips.contains(row.key)
                                 parentView.generateHeadsUpAnimation(
                                     row,
                                     /* isHeadsUp = */ true,
@@ -69,7 +69,7 @@
                             }
                             removed.forEach { key ->
                                 val row = obtainView(key)
-                                val hasStatusBarChip = statusBarChips.contains(row.entry.key)
+                                val hasStatusBarChip = statusBarChips.contains(row.key)
                                 if (!parentView.isBeingDragged()) {
                                     parentView.generateHeadsUpAnimation(
                                         row,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 9aa4c54..e4e56c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -58,10 +58,10 @@
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.log.SessionTracker;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.model.Scenes;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index fc721bf..a4ee4ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -140,6 +140,7 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.views.NavigationBarView;
 import com.android.systemui.notetask.NoteTaskController;
@@ -191,7 +192,6 @@
 import com.android.systemui.statusbar.LightRevealScrim;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 36193bd..3c14462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -49,6 +49,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.logging.KeyguardLogger;
+import com.android.systemui.Flags;
 import com.android.systemui.battery.BatteryMeterViewController;
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -475,12 +476,14 @@
                 UserHandle.USER_ALL);
         updateUserSwitcher();
         onThemeChanged();
-        collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer,
-                mCoroutineDispatcher);
-        collectFlow(mView, mLockscreenToHubTransitionViewModel.getStatusBarAlpha(),
-                mToGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
-        collectFlow(mView, mHubToLockscreenTransitionViewModel.getStatusBarAlpha(),
-                mFromGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
+        if (!Flags.glanceableHubV2()) {
+            collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer,
+                    mCoroutineDispatcher);
+            collectFlow(mView, mLockscreenToHubTransitionViewModel.getStatusBarAlpha(),
+                    mToGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
+            collectFlow(mView, mHubToLockscreenTransitionViewModel.getStatusBarAlpha(),
+                    mFromGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
+        }
         if (NewStatusBarIcons.isEnabled()) {
             ComposeView batteryComposeView = new ComposeView(mContext);
             UnifiedBatteryViewBinder.bind(
@@ -645,7 +648,7 @@
                         && !mDozing
                         && !hideForBypass
                         && !mDisableStateTracker.isDisabled()
-                        && (!mCommunalShowing || mExplicitAlpha != -1)
+                        && (Flags.glanceableHubV2() || (!mCommunalShowing || mExplicitAlpha != -1))
                         ? View.VISIBLE : View.INVISIBLE;
 
         updateViewState(newAlpha, newVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 74a42ef..f3d7202 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -29,6 +29,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
+import android.content.Context;
 import android.graphics.Color;
 import android.os.Handler;
 import android.util.Log;
@@ -226,6 +227,8 @@
 
     private ScrimState mState = ScrimState.UNINITIALIZED;
 
+    private Context mContext;
+
     private ScrimView mScrimInFront;
     private ScrimView mNotificationsScrim;
     private ScrimView mScrimBehind;
@@ -365,7 +368,9 @@
             @Main CoroutineDispatcher mainDispatcher,
             LargeScreenShadeInterpolator largeScreenShadeInterpolator,
             BlurConfig blurConfig,
+            @Main Context context,
             Lazy<WindowRootViewBlurInteractor> windowRootViewBlurInteractor) {
+        mContext = context;
         mScrimStateListener = lightBarController::setScrimState;
         mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
         mBlurConfig = blurConfig;
@@ -1627,16 +1632,16 @@
 
     private void updateThemeColors() {
         if (mScrimBehind == null) return;
-        int background = mScrimBehind.getContext().getColor(
+        int background = mContext.getColor(
                 com.android.internal.R.color.materialColorSurfaceDim);
-        int accent = mScrimBehind.getContext().getColor(
+        int accent = mContext.getColor(
                 com.android.internal.R.color.materialColorPrimary);
         mColors.setMainColor(background);
         mColors.setSecondaryColor(accent);
         final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background);
         mColors.setSupportsDarkText(isBackgroundLight);
 
-        int surface = mScrimBehind.getContext().getColor(
+        int surface = mContext.getColor(
                 com.android.internal.R.color.materialColorSurface);
         for (ScrimState state : ScrimState.values()) {
             state.setSurfaceColor(surface);
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 74b1c3b..2c8866f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -38,6 +38,7 @@
 import com.android.systemui.InitController;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
@@ -50,7 +51,6 @@
 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.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 05a46cd..8389aab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -219,7 +219,7 @@
                     if (NotificationBundleUi.isEnabled()) {
                         mGroupExpansionManager.toggleGroupExpansion(row.getEntryAdapter());
                     } else {
-                        mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+                        mGroupExpansionManager.toggleGroupExpansion(row.getEntryLegacy());
                     }
                 } else if (!row.isChildInGroup()) {
                     final boolean expandNotification;
@@ -241,7 +241,7 @@
                     if (NotificationBundleUi.isEnabled()) {
                         mGroupExpansionManager.toggleGroupExpansion(row.getEntryAdapter());
                     } else {
-                        mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+                        mGroupExpansionManager.toggleGroupExpansion(row.getEntryLegacy());
                     }
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManagerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManagerExt.kt
index fbc6b95..372e91f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManagerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManagerExt.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.phone
 
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index b662892..61b7d80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -25,7 +25,6 @@
 import androidx.annotation.VisibleForTesting
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.CoreStartable
-import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
@@ -162,6 +161,10 @@
                 notificationKey = currentInfo.key,
                 appName = currentInfo.appName,
                 promotedContent = currentInfo.promotedContent,
+                // [hasOngoingCall()] filters out the case in which the call is ongoing but the app
+                // is visible (we issue [OngoingCallModel.NoCall] below in that case), so this can
+                // be safely made false.
+                isAppVisible = false,
             )
         } else {
             return OngoingCallModel.NoCall
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 d6ca656..bed9e7c 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
@@ -51,9 +51,7 @@
  * This class monitors call notifications and the visibility of call apps to determine the
  * appropriate chip state. It emits:
  * * - [OngoingCallModel.NoCall] when there is no call notification
- * * - [OngoingCallModel.InCallWithVisibleApp] when there is a call notification but the call app is
- *   visible
- * * - [OngoingCallModel.InCall] when there is a call notification and the call app is not visible
+ * * - [OngoingCallModel.InCall] when there is a call notification
  */
 @SysUISingleton
 class OngoingCallInteractor
@@ -85,12 +83,14 @@
                 initialValue = OngoingCallModel.NoCall,
             )
 
+    // TODO(b/400720280): maybe put this inside [OngoingCallModel].
     @VisibleForTesting
     val isStatusBarRequiredForOngoingCall =
         combine(ongoingCallState, isChipSwipedAway) { callState, chipSwipedAway ->
-            callState is OngoingCallModel.InCall && !chipSwipedAway
+            callState.willCallChipBeVisible() && !chipSwipedAway
         }
 
+    // TODO(b/400720280): maybe put this inside [OngoingCallModel].
     @VisibleForTesting
     val isGestureListeningEnabled =
         combine(
@@ -98,9 +98,12 @@
             statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode,
             isChipSwipedAway,
         ) { callState, isFullscreen, chipSwipedAway ->
-            callState is OngoingCallModel.InCall && !chipSwipedAway && isFullscreen
+            callState.willCallChipBeVisible() && !chipSwipedAway && isFullscreen
         }
 
+    private fun OngoingCallModel.willCallChipBeVisible() =
+        this is OngoingCallModel.InCall && !isAppVisible
+
     private fun createOngoingCallStateFlow(
         notification: ActiveNotificationModel?
     ): Flow<OngoingCallModel> {
@@ -147,34 +150,23 @@
         model: ActiveNotificationModel,
         isVisible: Boolean,
     ): OngoingCallModel {
-        return when {
-            isVisible -> {
-                logger.d({ "Call app is visible: uid=$int1" }) { int1 = model.uid }
-                OngoingCallModel.InCallWithVisibleApp(
-                    startTimeMs = model.whenTime,
-                    notificationIconView = model.statusBarChipIconView,
-                    intent = model.contentIntent,
-                    notificationKey = model.key,
-                    appName = model.appName,
-                    promotedContent = model.promotedContent,
-                )
-            }
-
-            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,
-                    appName = model.appName,
-                    promotedContent = model.promotedContent,
-                )
-            }
+        logger.d({
+            "Active call detected: uid=$int1 startTime=$long1 hasIcon=$bool1 isAppVisible=$bool2"
+        }) {
+            int1 = model.uid
+            long1 = model.whenTime
+            bool1 = model.statusBarChipIconView != null
+            bool2 = isVisible
         }
+        return OngoingCallModel.InCall(
+            startTimeMs = model.whenTime,
+            notificationIconView = model.statusBarChipIconView,
+            intent = model.contentIntent,
+            notificationKey = model.key,
+            appName = model.appName,
+            promotedContent = model.promotedContent,
+            isAppVisible = isVisible,
+        )
     }
 
     private fun setStatusBarRequiredForOngoingCall(statusBarRequired: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
index 62f0ba0..322dfff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
@@ -26,25 +26,6 @@
     data object NoCall : OngoingCallModel
 
     /**
-     * There is an ongoing call but the call app is currently visible, so we don't need to show the
-     * chip.
-     *
-     * @property startTimeMs see [InCall.startTimeMs].
-     * @property notificationIconView see [InCall.notificationIconView].
-     * @property intent see [InCall.intent].
-     * @property appName see [InCall.appName].
-     * @property promotedContent see [InCall.promotedContent].
-     */
-    data class InCallWithVisibleApp(
-        val startTimeMs: Long,
-        val notificationIconView: StatusBarIconView?,
-        val intent: PendingIntent?,
-        val notificationKey: String,
-        val appName: String,
-        val promotedContent: PromotedNotificationContentModel?,
-    ) : OngoingCallModel
-
-    /**
      * There *is* an ongoing call.
      *
      * @property startTimeMs the time that the phone call started, based on the notification's
@@ -58,6 +39,7 @@
      * @property appName the user-readable name of the app that posted the call notification.
      * @property promotedContent if the call notification also meets promoted notification criteria,
      *   this field is filled in with the content related to promotion. Otherwise null.
+     * @property isAppVisible whether the app to which the call belongs is currently visible.
      */
     data class InCall(
         val startTimeMs: Long,
@@ -66,5 +48,6 @@
         val notificationKey: String,
         val appName: String,
         val promotedContent: PromotedNotificationContentModel?,
+        val isAppVisible: Boolean,
     ) : OngoingCallModel
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
index f82e681..e2fd4bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
@@ -19,7 +19,7 @@
 import android.net.ConnectivityManager
 import android.os.Handler
 import android.provider.Settings.Global
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.log.table.TableLogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/UnifiedBattery.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/UnifiedBattery.kt
index 732ea6a..5127e8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/UnifiedBattery.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/battery/ui/composable/UnifiedBattery.kt
@@ -101,7 +101,13 @@
             for (glyph in glyphs) {
                 // Move the glyph to the right spot
                 val verticalOffset = (BatteryFrame.innerHeight - glyph.height) / 2
-                inset(horizontalOffset, verticalOffset) { glyph.draw(this, colors) }
+                inset(
+                    // Never try and inset more than half of the available size - see b/400246091.
+                    minOf(horizontalOffset, size.width / 2),
+                    minOf(verticalOffset, size.height / 2),
+                ) {
+                    glyph.draw(this, colors)
+                }
 
                 horizontalOffset += glyph.width + INTER_GLYPH_PADDING_PX
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 2952850..db1977b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryKairosImpl
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairosAdapter
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairosImpl
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
@@ -92,8 +93,10 @@
             DemoModeMobileConnectionDataSourceKairosImpl.Module::class,
             MobileRepositorySwitcherKairos.Module::class,
             MobileConnectionsRepositoryKairosImpl.Module::class,
+            MobileIconsInteractorKairosImpl.Module::class,
             MobileConnectionRepositoryKairosFactoryImpl.Module::class,
             MobileConnectionsRepositoryKairosAdapter.Module::class,
+            MobileIconsInteractorKairosAdapter.Module::class,
         ]
 )
 abstract class StatusBarPipelineModule {
@@ -171,7 +174,7 @@
         @Provides
         fun mobileIconsInteractor(
             impl: Provider<MobileIconsInteractorImpl>,
-            kairosImpl: Provider<MobileIconsInteractorKairosImpl>,
+            kairosImpl: Provider<MobileIconsInteractorKairosAdapter>,
         ): MobileIconsInteractor {
             return if (Flags.statusBarMobileIconKairos()) {
                 kairosImpl.get()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherKairos.kt
index 1f5b849..f4076ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherKairos.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.KairosActivatable
 import com.android.systemui.KairosBuilder
 import com.android.systemui.activated
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt
index 1a8ca95..f4afc24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.kairos.BuildSpec
 import com.android.systemui.kairos.ExperimentalKairosApi
 import com.android.systemui.kairos.State
+import com.android.systemui.kairos.combine
 import com.android.systemui.kairos.flatMap
 import com.android.systemui.kairosBuilder
 import com.android.systemui.log.table.TableLogBuffer
@@ -55,9 +56,15 @@
     @Assisted private val isCarrierMerged: State<Boolean>,
 ) : MobileConnectionRepositoryKairos, KairosBuilder by kairosBuilder() {
 
+    private var dumpCache: DumpCache? = null
+
     init {
         onActivated {
             logDiffsForTable(isCarrierMerged, tableLogBuffer, columnName = "isCarrierMerged")
+            combine(isCarrierMerged, activeRepo) { isCarrierMerged, activeRepo ->
+                    DumpCache(isCarrierMerged, activeRepo)
+                }
+                .observe { dumpCache = it }
         }
     }
 
@@ -198,13 +205,6 @@
 
     override val isInEcmMode: State<Boolean> = activeRepo.flatMap { it.isInEcmMode }
 
-    private var dumpCache: DumpCache? = null
-
-    private data class DumpCache(
-        val isCarrierMerged: Boolean,
-        val activeRepo: MobileConnectionRepositoryKairos,
-    )
-
     fun dump(pw: PrintWriter) {
         val cache = dumpCache ?: return
         val ipw = IndentingPrintWriter(pw, "  ")
@@ -227,6 +227,11 @@
         ipw.decreaseIndent()
     }
 
+    private data class DumpCache(
+        val isCarrierMerged: Boolean,
+        val activeRepo: MobileConnectionRepositoryKairos,
+    )
+
     @AssistedFactory
     interface Factory {
         fun create(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt
index e468159..e6c2921 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt
@@ -131,6 +131,8 @@
     private val mobileRepoFactory: Lazy<ConnectionRepoFactory>,
 ) : MobileConnectionsRepositoryKairos, Dumpable, KairosBuilder by kairosBuilder() {
 
+    private var dumpCache: DumpCache? = null
+
     init {
         dumpManager.registerNormalDumpable("MobileConnectionsRepositoryKairos", this)
     }
@@ -253,6 +255,7 @@
                 .asIncremental()
                 .mapValues { (subId, sub) -> mobileRepoFactory.get().create(subId) }
                 .applyLatestSpecForKey()
+                .apply { observe { dumpCache = DumpCache(it) } }
         }
 
     private val telephonyManagerState: State<Pair<Int?, Set<Int>>> = buildState {
@@ -479,10 +482,6 @@
             profileClass = profileClass,
         )
 
-    private var dumpCache: DumpCache? = null
-
-    private data class DumpCache(val repos: Map<Int, FullMobileConnectionRepositoryKairos>)
-
     override fun dump(pw: PrintWriter, args: Array<String>) {
         val cache = dumpCache ?: return
         val ipw = IndentingPrintWriter(pw, " ")
@@ -494,10 +493,16 @@
 
         ipw.println("Connections (${cache.repos.size} total):")
         ipw.increaseIndent()
-        cache.repos.values.forEach { it.dump(ipw) }
+        cache.repos.values.forEach {
+            if (it is FullMobileConnectionRepositoryKairos) {
+                it.dump(ipw)
+            }
+        }
         ipw.decreaseIndent()
     }
 
+    private data class DumpCache(val repos: Map<Int, MobileConnectionRepositoryKairos>)
+
     fun interface ConnectionRepoFactory {
         fun create(subId: Int): BuildSpec<MobileConnectionRepositoryKairos>
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 0eabb4ec..af4e61a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -61,31 +61,31 @@
      * consider this connection to be serving data, and thus want to show a network type icon, when
      * data is connected. Other data connection states would typically cause us not to show the icon
      */
-    val isDataConnected: StateFlow<Boolean>
+    val isDataConnected: Flow<Boolean>
 
     /** True if we consider this connection to be in service, i.e. can make calls */
-    val isInService: StateFlow<Boolean>
+    val isInService: Flow<Boolean>
 
     /** True if this connection is emergency only */
-    val isEmergencyOnly: StateFlow<Boolean>
+    val isEmergencyOnly: Flow<Boolean>
 
     /** Observable for the data enabled state of this connection */
-    val isDataEnabled: StateFlow<Boolean>
+    val isDataEnabled: Flow<Boolean>
 
     /** True if the RAT icon should always be displayed and false otherwise. */
-    val alwaysShowDataRatIcon: StateFlow<Boolean>
+    val alwaysShowDataRatIcon: Flow<Boolean>
 
     /** Canonical representation of the current mobile signal strength as a triangle. */
-    val signalLevelIcon: StateFlow<SignalIconModel>
+    val signalLevelIcon: Flow<SignalIconModel>
 
     /** Observable for RAT type (network type) indicator */
-    val networkTypeIconGroup: StateFlow<NetworkTypeIconModel>
+    val networkTypeIconGroup: Flow<NetworkTypeIconModel>
 
     /** Whether or not to show the slice attribution */
-    val showSliceAttribution: StateFlow<Boolean>
+    val showSliceAttribution: Flow<Boolean>
 
     /** True if this connection is satellite-based */
-    val isNonTerrestrial: StateFlow<Boolean>
+    val isNonTerrestrial: Flow<Boolean>
 
     /**
      * Provider name for this network connection. The name can be one of 3 values:
@@ -95,7 +95,7 @@
      *    override in [connectionInfo.operatorAlphaShort], a value that is derived from
      *    [ServiceState]
      */
-    val networkName: StateFlow<NetworkNameModel>
+    val networkName: Flow<NetworkNameModel>
 
     /**
      * Provider name for this network connection. The name can be one of 3 values:
@@ -108,26 +108,26 @@
      * TODO(b/296600321): De-duplicate this field with [networkName] after determining the data
      *   provided is identical
      */
-    val carrierName: StateFlow<String>
+    val carrierName: Flow<String>
 
     /** True if there is only one active subscription. */
-    val isSingleCarrier: StateFlow<Boolean>
+    val isSingleCarrier: Flow<Boolean>
 
     /**
      * True if this connection is considered roaming. The roaming bit can come from [ServiceState],
      * or directly from the telephony manager's CDMA ERI number value. Note that we don't consider a
      * connection to be roaming while carrier network change is active
      */
-    val isRoaming: StateFlow<Boolean>
+    val isRoaming: Flow<Boolean>
 
     /** See [MobileIconsInteractor.isForceHidden]. */
     val isForceHidden: Flow<Boolean>
 
     /** See [MobileConnectionRepository.isAllowedDuringAirplaneMode]. */
-    val isAllowedDuringAirplaneMode: StateFlow<Boolean>
+    val isAllowedDuringAirplaneMode: Flow<Boolean>
 
     /** True when in carrier network change mode */
-    val carrierNetworkChangeActive: StateFlow<Boolean>
+    val carrierNetworkChangeActive: Flow<Boolean>
 }
 
 /** Interactor for a single mobile connection. This connection _should_ have one subscription ID */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt
index 4580ad9..a939959 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,38 +22,37 @@
 import com.android.settingslib.graph.SignalDrawable
 import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
 import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
-import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.KairosBuilder
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.combine
+import com.android.systemui.kairos.flatMap
+import com.android.systemui.kairos.map
+import com.android.systemui.kairosBuilder
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Connected
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepositoryKairos
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel.DefaultIcon
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel.OverriddenIcon
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
 
+@ExperimentalKairosApi
 interface MobileIconInteractorKairos {
     /** The table log created for this connection */
     val tableLogBuffer: TableLogBuffer
 
     /** The current mobile data activity */
-    val activity: Flow<DataActivityModel>
+    val activity: State<DataActivityModel>
 
     /** See [MobileConnectionsRepository.mobileIsDefault]. */
-    val mobileIsDefault: Flow<Boolean>
+    val mobileIsDefault: State<Boolean>
 
     /**
      * True when telephony tells us that the data state is CONNECTED. See
@@ -61,31 +60,31 @@
      * consider this connection to be serving data, and thus want to show a network type icon, when
      * data is connected. Other data connection states would typically cause us not to show the icon
      */
-    val isDataConnected: StateFlow<Boolean>
+    val isDataConnected: State<Boolean>
 
     /** True if we consider this connection to be in service, i.e. can make calls */
-    val isInService: StateFlow<Boolean>
+    val isInService: State<Boolean>
 
     /** True if this connection is emergency only */
-    val isEmergencyOnly: StateFlow<Boolean>
+    val isEmergencyOnly: State<Boolean>
 
     /** Observable for the data enabled state of this connection */
-    val isDataEnabled: StateFlow<Boolean>
+    val isDataEnabled: State<Boolean>
 
     /** True if the RAT icon should always be displayed and false otherwise. */
-    val alwaysShowDataRatIcon: StateFlow<Boolean>
+    val alwaysShowDataRatIcon: State<Boolean>
 
     /** Canonical representation of the current mobile signal strength as a triangle. */
-    val signalLevelIcon: StateFlow<SignalIconModel>
+    val signalLevelIcon: State<SignalIconModel>
 
     /** Observable for RAT type (network type) indicator */
-    val networkTypeIconGroup: StateFlow<NetworkTypeIconModel>
+    val networkTypeIconGroup: State<NetworkTypeIconModel>
 
     /** Whether or not to show the slice attribution */
-    val showSliceAttribution: StateFlow<Boolean>
+    val showSliceAttribution: State<Boolean>
 
     /** True if this connection is satellite-based */
-    val isNonTerrestrial: StateFlow<Boolean>
+    val isNonTerrestrial: State<Boolean>
 
     /**
      * Provider name for this network connection. The name can be one of 3 values:
@@ -95,7 +94,7 @@
      *    override in [connectionInfo.operatorAlphaShort], a value that is derived from
      *    [ServiceState]
      */
-    val networkName: StateFlow<NetworkNameModel>
+    val networkName: State<NetworkNameModel>
 
     /**
      * Provider name for this network connection. The name can be one of 3 values:
@@ -108,119 +107,110 @@
      * TODO(b/296600321): De-duplicate this field with [networkName] after determining the data
      *   provided is identical
      */
-    val carrierName: StateFlow<String>
+    val carrierName: State<String>
 
     /** True if there is only one active subscription. */
-    val isSingleCarrier: StateFlow<Boolean>
+    val isSingleCarrier: State<Boolean>
 
     /**
      * True if this connection is considered roaming. The roaming bit can come from [ServiceState],
      * or directly from the telephony manager's CDMA ERI number value. Note that we don't consider a
      * connection to be roaming while carrier network change is active
      */
-    val isRoaming: StateFlow<Boolean>
+    val isRoaming: State<Boolean>
 
     /** See [MobileIconsInteractor.isForceHidden]. */
-    val isForceHidden: Flow<Boolean>
+    val isForceHidden: State<Boolean>
 
     /** See [MobileConnectionRepository.isAllowedDuringAirplaneMode]. */
-    val isAllowedDuringAirplaneMode: StateFlow<Boolean>
+    val isAllowedDuringAirplaneMode: State<Boolean>
 
     /** True when in carrier network change mode */
-    val carrierNetworkChangeActive: StateFlow<Boolean>
+    val carrierNetworkChangeActive: State<Boolean>
 }
 
 /** Interactor for a single mobile connection. This connection _should_ have one subscription ID */
-@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@ExperimentalKairosApi
 class MobileIconInteractorKairosImpl(
-    @Background scope: CoroutineScope,
-    defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
-    override val alwaysShowDataRatIcon: StateFlow<Boolean>,
-    alwaysUseCdmaLevel: StateFlow<Boolean>,
-    override val isSingleCarrier: StateFlow<Boolean>,
-    override val mobileIsDefault: StateFlow<Boolean>,
-    defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
-    defaultMobileIconGroup: StateFlow<MobileIconGroup>,
-    isDefaultConnectionFailed: StateFlow<Boolean>,
-    override val isForceHidden: Flow<Boolean>,
-    connectionRepository: MobileConnectionRepository,
+    defaultSubscriptionHasDataEnabled: State<Boolean>,
+    override val alwaysShowDataRatIcon: State<Boolean>,
+    alwaysUseCdmaLevel: State<Boolean>,
+    override val isSingleCarrier: State<Boolean>,
+    override val mobileIsDefault: State<Boolean>,
+    defaultMobileIconMapping: State<Map<String, MobileIconGroup>>,
+    defaultMobileIconGroup: State<MobileIconGroup>,
+    isDefaultConnectionFailed: State<Boolean>,
+    override val isForceHidden: State<Boolean>,
+    private val connectionRepository: MobileConnectionRepositoryKairos,
     private val context: Context,
-    val carrierIdOverrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl(),
-) : MobileIconInteractor, MobileIconInteractorKairos {
-    override val tableLogBuffer: TableLogBuffer = connectionRepository.tableLogBuffer
+    private val carrierIdOverrides: MobileIconCarrierIdOverrides =
+        MobileIconCarrierIdOverridesImpl(),
+) : MobileIconInteractorKairos, KairosBuilder by kairosBuilder() {
+    override val tableLogBuffer: TableLogBuffer
+        get() = connectionRepository.tableLogBuffer
 
-    override val activity = connectionRepository.dataActivityDirection
+    override val activity: State<DataActivityModel>
+        get() = connectionRepository.dataActivityDirection
 
-    override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled
+    override val isDataEnabled: State<Boolean> = connectionRepository.dataEnabled
 
-    override val carrierNetworkChangeActive: StateFlow<Boolean> =
-        connectionRepository.carrierNetworkChangeActive
+    override val carrierNetworkChangeActive: State<Boolean>
+        get() = connectionRepository.carrierNetworkChangeActive
 
     // True if there exists _any_ icon override for this carrierId. Note that overrides can include
     // any or none of the icon groups defined in MobileMappings, so we still need to check on a
     // per-network-type basis whether or not the given icon group is overridden
-    private val carrierIdIconOverrideExists =
-        connectionRepository.carrierId
-            .map { carrierIdOverrides.carrierIdEntryExists(it) }
-            .distinctUntilChanged()
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+    private val carrierIdIconOverrideExists: State<Boolean> =
+        connectionRepository.carrierId.map { carrierIdOverrides.carrierIdEntryExists(it) }
 
-    override val networkName =
+    override val networkName: State<NetworkNameModel> =
         combine(connectionRepository.operatorAlphaShort, connectionRepository.networkName) {
-                operatorAlphaShort,
-                networkName ->
-                if (networkName is NetworkNameModel.Default && operatorAlphaShort != null) {
-                    NetworkNameModel.IntentDerived(operatorAlphaShort)
-                } else {
-                    networkName
-                }
+            operatorAlphaShort,
+            networkName ->
+            if (networkName is NetworkNameModel.Default && operatorAlphaShort != null) {
+                NetworkNameModel.IntentDerived(operatorAlphaShort)
+            } else {
+                networkName
             }
-            .stateIn(
-                scope,
-                SharingStarted.WhileSubscribed(),
-                connectionRepository.networkName.value,
-            )
+        }
 
-    override val carrierName =
+    override val carrierName: State<String> =
         combine(connectionRepository.operatorAlphaShort, connectionRepository.carrierName) {
-                operatorAlphaShort,
-                networkName ->
-                if (networkName is NetworkNameModel.Default && operatorAlphaShort != null) {
-                    operatorAlphaShort
-                } else {
-                    networkName.name
-                }
+            operatorAlphaShort,
+            networkName ->
+            if (networkName is NetworkNameModel.Default && operatorAlphaShort != null) {
+                operatorAlphaShort
+            } else {
+                networkName.name
             }
-            .stateIn(
-                scope,
-                SharingStarted.WhileSubscribed(),
-                connectionRepository.carrierName.value.name,
-            )
+        }
 
     /** What the mobile icon would be before carrierId overrides */
-    private val defaultNetworkType: StateFlow<MobileIconGroup> =
+    private val defaultNetworkType: State<MobileIconGroup> =
         combine(
-                connectionRepository.resolvedNetworkType,
-                defaultMobileIconMapping,
-                defaultMobileIconGroup,
-            ) { resolvedNetworkType, mapping, defaultGroup ->
-                when (resolvedNetworkType) {
-                    is ResolvedNetworkType.CarrierMergedNetworkType ->
-                        resolvedNetworkType.iconGroupOverride
-                    else -> {
-                        mapping[resolvedNetworkType.lookupKey] ?: defaultGroup
-                    }
+            connectionRepository.resolvedNetworkType,
+            defaultMobileIconMapping,
+            defaultMobileIconGroup,
+        ) { resolvedNetworkType, mapping, defaultGroup ->
+            when (resolvedNetworkType) {
+                is ResolvedNetworkType.CarrierMergedNetworkType ->
+                    resolvedNetworkType.iconGroupOverride
+
+                else -> {
+                    mapping[resolvedNetworkType.lookupKey] ?: defaultGroup
                 }
             }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), defaultMobileIconGroup.value)
+        }
 
-    override val networkTypeIconGroup =
-        combine(defaultNetworkType, carrierIdIconOverrideExists) { networkType, overrideExists ->
+    override val networkTypeIconGroup: State<NetworkTypeIconModel> = buildState {
+        combineTransactionally(defaultNetworkType, carrierIdIconOverrideExists) {
+                networkType,
+                overrideExists ->
                 // DefaultIcon comes out of the icongroup lookup, we check for overrides here
                 if (overrideExists) {
                     val iconOverride =
                         carrierIdOverrides.getOverrideFor(
-                            connectionRepository.carrierId.value,
+                            connectionRepository.carrierId.sample(),
                             networkType.name,
                             context.resources,
                         )
@@ -233,106 +223,101 @@
                     DefaultIcon(networkType)
                 }
             }
-            .distinctUntilChanged()
-            .logDiffsForTable(
-                tableLogBuffer = tableLogBuffer,
-                initialValue = DefaultIcon(defaultMobileIconGroup.value),
-            )
-            .stateIn(
-                scope,
-                SharingStarted.WhileSubscribed(),
-                DefaultIcon(defaultMobileIconGroup.value),
-            )
+            .also { logDiffsForTable(it, tableLogBuffer = tableLogBuffer) }
+    }
 
-    override val showSliceAttribution: StateFlow<Boolean> =
+    override val showSliceAttribution: State<Boolean> =
         combine(
-                connectionRepository.allowNetworkSliceIndicator,
-                connectionRepository.hasPrioritizedNetworkCapabilities,
-            ) { allowed, hasPrioritizedNetworkCapabilities ->
-                allowed && hasPrioritizedNetworkCapabilities
-            }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+            connectionRepository.allowNetworkSliceIndicator,
+            connectionRepository.hasPrioritizedNetworkCapabilities,
+        ) { allowed, hasPrioritizedNetworkCapabilities ->
+            allowed && hasPrioritizedNetworkCapabilities
+        }
 
-    override val isNonTerrestrial: StateFlow<Boolean> = connectionRepository.isNonTerrestrial
+    override val isNonTerrestrial: State<Boolean>
+        get() = connectionRepository.isNonTerrestrial
 
-    override val isRoaming: StateFlow<Boolean> =
+    override val isRoaming: State<Boolean> =
         combine(
-                connectionRepository.carrierNetworkChangeActive,
-                connectionRepository.isGsm,
-                connectionRepository.isRoaming,
-                connectionRepository.cdmaRoaming,
-            ) { carrierNetworkChangeActive, isGsm, isRoaming, cdmaRoaming ->
-                if (carrierNetworkChangeActive) {
-                    false
-                } else if (isGsm) {
-                    isRoaming
-                } else {
-                    cdmaRoaming
-                }
+            connectionRepository.carrierNetworkChangeActive,
+            connectionRepository.isGsm,
+            connectionRepository.isRoaming,
+            connectionRepository.cdmaRoaming,
+        ) { carrierNetworkChangeActive, isGsm, isRoaming, cdmaRoaming ->
+            if (carrierNetworkChangeActive) {
+                false
+            } else if (isGsm) {
+                isRoaming
+            } else {
+                cdmaRoaming
             }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+        }
 
-    private val level: StateFlow<Int> =
+    private val level: State<Int> =
         combine(
-                connectionRepository.isGsm,
-                connectionRepository.primaryLevel,
-                connectionRepository.cdmaLevel,
-                alwaysUseCdmaLevel,
-            ) { isGsm, primaryLevel, cdmaLevel, alwaysUseCdmaLevel ->
-                when {
-                    // GSM connections should never use the CDMA level
-                    isGsm -> primaryLevel
-                    alwaysUseCdmaLevel -> cdmaLevel
-                    else -> primaryLevel
-                }
+            connectionRepository.isGsm,
+            connectionRepository.primaryLevel,
+            connectionRepository.cdmaLevel,
+            alwaysUseCdmaLevel,
+        ) { isGsm, primaryLevel, cdmaLevel, alwaysUseCdmaLevel ->
+            when {
+                // GSM connections should never use the CDMA level
+                isGsm -> primaryLevel
+                alwaysUseCdmaLevel -> cdmaLevel
+                else -> primaryLevel
             }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
+        }
 
-    private val numberOfLevels: StateFlow<Int> = connectionRepository.numberOfLevels
+    private val numberOfLevels: State<Int>
+        get() = connectionRepository.numberOfLevels
 
-    override val isDataConnected: StateFlow<Boolean> =
+    override val isDataConnected: State<Boolean> =
         connectionRepository.dataConnectionState
             .map { it == Connected }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+            .also {
+                onActivated { logDiffsForTable(it, tableLogBuffer, "icon", "isDataConnected") }
+            }
 
-    override val isInService = connectionRepository.isInService
+    override val isInService
+        get() = connectionRepository.isInService
 
-    override val isEmergencyOnly: StateFlow<Boolean> = connectionRepository.isEmergencyOnly
+    override val isEmergencyOnly: State<Boolean>
+        get() = connectionRepository.isEmergencyOnly
 
-    override val isAllowedDuringAirplaneMode = connectionRepository.isAllowedDuringAirplaneMode
+    override val isAllowedDuringAirplaneMode: State<Boolean>
+        get() = connectionRepository.isAllowedDuringAirplaneMode
 
     /** Whether or not to show the error state of [SignalDrawable] */
-    private val showExclamationMark: StateFlow<Boolean> =
+    private val showExclamationMark: State<Boolean> =
         combine(defaultSubscriptionHasDataEnabled, isDefaultConnectionFailed, isInService) {
-                isDefaultDataEnabled,
-                isDefaultConnectionFailed,
-                isInService ->
-                !isDefaultDataEnabled || isDefaultConnectionFailed || !isInService
-            }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), true)
+            isDefaultDataEnabled,
+            isDefaultConnectionFailed,
+            isInService ->
+            !isDefaultDataEnabled || isDefaultConnectionFailed || !isInService
+        }
 
-    private val cellularShownLevel: StateFlow<Int> =
+    private val cellularShownLevel: State<Int> =
         combine(level, isInService, connectionRepository.inflateSignalStrength) {
-                level,
-                isInService,
-                inflate ->
-                if (isInService) {
-                    if (inflate) level + 1 else level
-                } else 0
+            level,
+            isInService,
+            inflate ->
+            when {
+                !isInService -> 0
+                inflate -> level + 1
+                else -> level
             }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
+        }
 
     // Satellite level is unaffected by the inflateSignalStrength property
     // See b/346904529 for details
-    private val satelliteShownLevel: StateFlow<Int> =
+    private val satelliteShownLevel: State<Int> =
         if (Flags.carrierRoamingNbIotNtn()) {
-                connectionRepository.satelliteLevel
-            } else {
-                combine(level, isInService) { level, isInService -> if (isInService) level else 0 }
-            }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
+            connectionRepository.satelliteLevel
+        } else {
+            combine(level, isInService) { level, isInService -> if (isInService) level else 0 }
+        }
 
-    private val cellularIcon: Flow<SignalIconModel.Cellular> =
+    private val cellularIcon: State<SignalIconModel.Cellular> =
         combine(
             cellularShownLevel,
             numberOfLevels,
@@ -347,7 +332,7 @@
             )
         }
 
-    private val satelliteIcon: Flow<SignalIconModel.Satellite> =
+    private val satelliteIcon: State<SignalIconModel.Satellite> =
         satelliteShownLevel.map {
             SignalIconModel.Satellite(
                 level = it,
@@ -357,24 +342,14 @@
             )
         }
 
-    override val signalLevelIcon: StateFlow<SignalIconModel> = run {
-        val initial =
-            SignalIconModel.Cellular(
-                cellularShownLevel.value,
-                numberOfLevels.value,
-                showExclamationMark.value,
-                carrierNetworkChangeActive.value,
-            )
+    override val signalLevelIcon: State<SignalIconModel> =
         isNonTerrestrial
-            .flatMapLatest { ntn ->
+            .flatMap { ntn ->
                 if (ntn) {
                     satelliteIcon
                 } else {
                     cellularIcon
                 }
             }
-            .distinctUntilChanged()
-            .logDiffsForTable(tableLogBuffer, columnPrefix = "icon", initialValue = initial)
-            .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
-    }
+            .also { onActivated { logDiffsForTable(it, tableLogBuffer, columnPrefix = "icon") } }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapter.kt
new file mode 100644
index 0000000..a3b9b17
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapter.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.toColdConflatedFlow
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+
+@ExperimentalKairosApi
+fun BuildScope.MobileIconInteractorKairosAdapter(
+    kairosImpl: MobileIconInteractorKairos
+): MobileIconInteractor =
+    with(kairosImpl) {
+        MobileIconInteractorKairosAdapter(
+            tableLogBuffer = tableLogBuffer,
+            activity = activity.toColdConflatedFlow(kairosNetwork),
+            mobileIsDefault = mobileIsDefault.toColdConflatedFlow(kairosNetwork),
+            isDataConnected = isDataConnected.toStateFlow(),
+            isInService = isInService.toStateFlow(),
+            isEmergencyOnly = isEmergencyOnly.toStateFlow(),
+            isDataEnabled = isDataEnabled.toStateFlow(),
+            alwaysShowDataRatIcon = alwaysShowDataRatIcon.toStateFlow(),
+            signalLevelIcon = signalLevelIcon.toStateFlow(),
+            networkTypeIconGroup = networkTypeIconGroup.toStateFlow(),
+            showSliceAttribution = showSliceAttribution.toStateFlow(),
+            isNonTerrestrial = isNonTerrestrial.toStateFlow(),
+            networkName = networkName.toStateFlow(),
+            carrierName = carrierName.toStateFlow(),
+            isSingleCarrier = isSingleCarrier.toStateFlow(),
+            isRoaming = isRoaming.toStateFlow(),
+            isForceHidden = isForceHidden.toColdConflatedFlow(kairosNetwork),
+            isAllowedDuringAirplaneMode = isAllowedDuringAirplaneMode.toStateFlow(),
+            carrierNetworkChangeActive = carrierNetworkChangeActive.toStateFlow(),
+        )
+    }
+
+private class MobileIconInteractorKairosAdapter(
+    override val tableLogBuffer: TableLogBuffer,
+    override val activity: Flow<DataActivityModel>,
+    override val mobileIsDefault: Flow<Boolean>,
+    override val isDataConnected: StateFlow<Boolean>,
+    override val isInService: StateFlow<Boolean>,
+    override val isEmergencyOnly: StateFlow<Boolean>,
+    override val isDataEnabled: StateFlow<Boolean>,
+    override val alwaysShowDataRatIcon: StateFlow<Boolean>,
+    override val signalLevelIcon: StateFlow<SignalIconModel>,
+    override val networkTypeIconGroup: StateFlow<NetworkTypeIconModel>,
+    override val showSliceAttribution: StateFlow<Boolean>,
+    override val isNonTerrestrial: StateFlow<Boolean>,
+    override val networkName: StateFlow<NetworkNameModel>,
+    override val carrierName: StateFlow<String>,
+    override val isSingleCarrier: StateFlow<Boolean>,
+    override val isRoaming: StateFlow<Boolean>,
+    override val isForceHidden: Flow<Boolean>,
+    override val isAllowedDuringAirplaneMode: StateFlow<Boolean>,
+    override val carrierNetworkChangeActive: StateFlow<Boolean>,
+) : MobileIconInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairos.kt
index e8e0a83..14a276b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,41 +21,47 @@
 import android.telephony.SubscriptionManager
 import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING
 import com.android.settingslib.SignalIcon.MobileIconGroup
-import com.android.settingslib.mobile.TelephonyIcons
+import com.android.systemui.Flags
+import com.android.systemui.KairosActivatable
+import com.android.systemui.KairosBuilder
+import com.android.systemui.activated
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.Incremental
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.asyncEvent
+import com.android.systemui.kairos.buildSpec
+import com.android.systemui.kairos.combine
+import com.android.systemui.kairos.filter
+import com.android.systemui.kairos.flatMap
+import com.android.systemui.kairos.flatten
+import com.android.systemui.kairos.map
+import com.android.systemui.kairos.mapValues
+import com.android.systemui.kairos.stateOf
+import com.android.systemui.kairosBuilder
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.core.NewStatusBarIcons
 import com.android.systemui.statusbar.core.StatusBarRootModernization
 import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairos
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
 import com.android.systemui.util.CarrierConfigTracker
-import java.lang.ref.WeakReference
+import dagger.Binds
+import dagger.Provides
+import dagger.multibindings.ElementsIntoSet
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
+import javax.inject.Provider
+import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.flow.transformLatest
 
 /**
  * Business layer logic for the set of mobile subscription icons.
@@ -67,98 +73,79 @@
  * represents each RAT (LTE, 3G, etc.), as well as can produce an interactor for each individual
  * icon
  */
+@ExperimentalKairosApi
 interface MobileIconsInteractorKairos {
     /** See [MobileConnectionsRepository.mobileIsDefault]. */
-    val mobileIsDefault: StateFlow<Boolean>
+    val mobileIsDefault: State<Boolean>
 
     /** List of subscriptions, potentially filtered for CBRS */
-    val filteredSubscriptions: Flow<List<SubscriptionModel>>
-
-    /** Subscription ID of the current default data subscription */
-    val defaultDataSubId: Flow<Int?>
+    val filteredSubscriptions: State<List<SubscriptionModel>>
 
     /**
      * The current list of [MobileIconInteractor]s associated with the current list of
      * [filteredSubscriptions]
      */
-    val icons: StateFlow<List<MobileIconInteractor>>
+    val icons: Incremental<Int, MobileIconInteractorKairos>
 
     /** Whether the mobile icons can be stacked vertically. */
-    val isStackable: StateFlow<Boolean>
-
-    /**
-     * Observable for the subscriptionId of the current mobile data connection. Null if we don't
-     * have a valid subscription id
-     */
-    val activeMobileDataSubscriptionId: StateFlow<Int?>
+    val isStackable: State<Boolean>
 
     /** True if the active mobile data subscription has data enabled */
-    val activeDataConnectionHasDataEnabled: StateFlow<Boolean>
+    val activeDataConnectionHasDataEnabled: State<Boolean>
 
     /**
      * Flow providing a reference to the Interactor for the active data subId. This represents the
-     * [MobileIconInteractor] responsible for the active data connection, if any.
+     * [MobileIconInteractorKairos] responsible for the active data connection, if any.
      */
-    val activeDataIconInteractor: StateFlow<MobileIconInteractor?>
+    val activeDataIconInteractor: State<MobileIconInteractorKairos?>
 
     /** True if the RAT icon should always be displayed and false otherwise. */
-    val alwaysShowDataRatIcon: StateFlow<Boolean>
+    val alwaysShowDataRatIcon: State<Boolean>
 
     /** True if the CDMA level should be preferred over the primary level. */
-    val alwaysUseCdmaLevel: StateFlow<Boolean>
+    val alwaysUseCdmaLevel: State<Boolean>
 
     /** True if there is only one active subscription. */
-    val isSingleCarrier: StateFlow<Boolean>
+    val isSingleCarrier: State<Boolean>
 
     /** The icon mapping from network type to [MobileIconGroup] for the default subscription */
-    val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
+    val defaultMobileIconMapping: State<Map<String, MobileIconGroup>>
 
     /** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
-    val defaultMobileIconGroup: StateFlow<MobileIconGroup>
+    val defaultMobileIconGroup: State<MobileIconGroup>
 
     /** True only if the default network is mobile, and validation also failed */
-    val isDefaultConnectionFailed: StateFlow<Boolean>
+    val isDefaultConnectionFailed: State<Boolean>
 
     /** True once the user has been set up */
-    val isUserSetUp: StateFlow<Boolean>
+    val isUserSetUp: State<Boolean>
 
     /** True if we're configured to force-hide the mobile icons and false otherwise. */
-    val isForceHidden: Flow<Boolean>
+    val isForceHidden: State<Boolean>
 
     /**
      * True if the device-level service state (with -1 subscription id) reports emergency calls
      * only. This value is only useful when there are no other subscriptions OR all existing
      * subscriptions report that they are not in service.
      */
-    val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean>
-
-    /**
-     * Vends out a [MobileIconInteractor] tracking the [MobileConnectionRepository] for the given
-     * subId.
-     */
-    fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor
+    val isDeviceInEmergencyCallsOnlyMode: State<Boolean>
 }
 
-@OptIn(ExperimentalCoroutinesApi::class)
-@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@ExperimentalKairosApi
 @SysUISingleton
 class MobileIconsInteractorKairosImpl
 @Inject
 constructor(
-    private val mobileConnectionsRepo: MobileConnectionsRepository,
+    private val mobileConnectionsRepo: MobileConnectionsRepositoryKairos,
     private val carrierConfigTracker: CarrierConfigTracker,
     @MobileSummaryLog private val tableLogger: TableLogBuffer,
     connectivityRepository: ConnectivityRepository,
     userSetupRepo: UserSetupRepository,
-    @Background private val scope: CoroutineScope,
     private val context: Context,
     private val featureFlagsClassic: FeatureFlagsClassic,
-) : MobileIconsInteractor, MobileIconsInteractorKairos {
+) : MobileIconsInteractorKairos, KairosBuilder by kairosBuilder() {
 
-    // Weak reference lookup for created interactors
-    private val reuseCache = mutableMapOf<Int, WeakReference<MobileIconInteractor>>()
-
-    override val mobileIsDefault =
+    override val mobileIsDefault: State<Boolean> =
         combine(
                 mobileConnectionsRepo.mobileIsDefault,
                 mobileConnectionsRepo.hasCarrierMergedConnection,
@@ -167,47 +154,36 @@
                 // the `isDefault` calculation. See b/272586234.
                 mobileIsDefault || hasCarrierMergedConnection
             }
-            .logDiffsForTable(
-                tableLogger,
-                LOGGING_PREFIX,
-                columnName = "mobileIsDefault",
-                initialValue = false,
-            )
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
-
-    override val activeMobileDataSubscriptionId: StateFlow<Int?> =
-        mobileConnectionsRepo.activeMobileDataSubscriptionId
-
-    override val activeDataConnectionHasDataEnabled: StateFlow<Boolean> =
-        mobileConnectionsRepo.activeMobileDataRepository
-            .flatMapLatest { it?.dataEnabled ?: flowOf(false) }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
-
-    override val activeDataIconInteractor: StateFlow<MobileIconInteractor?> =
-        mobileConnectionsRepo.activeMobileDataSubscriptionId
-            .mapLatest {
-                if (it != null) {
-                    getMobileConnectionInteractorForSubId(it)
-                } else {
-                    null
+            .also {
+                onActivated {
+                    logDiffsForTable(
+                        it,
+                        tableLogger,
+                        LOGGING_PREFIX,
+                        columnName = "mobileIsDefault",
+                    )
                 }
             }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
 
-    private val unfilteredSubscriptions: Flow<List<SubscriptionModel>> =
-        mobileConnectionsRepo.subscriptions
+    override val activeDataConnectionHasDataEnabled: State<Boolean> =
+        mobileConnectionsRepo.activeMobileDataRepository.flatMap {
+            it?.dataEnabled ?: stateOf(false)
+        }
+
+    private val unfilteredSubscriptions: State<Collection<SubscriptionModel>>
+        get() = mobileConnectionsRepo.subscriptions
 
     /** Any filtering that we can do based purely on the info of each subscription individually. */
-    private val subscriptionsBasedFilteredSubs =
-        unfilteredSubscriptions
-            .map { it.filterBasedOnProvisioning().filterBasedOnNtn() }
-            .distinctUntilChanged()
+    private val subscriptionsBasedFilteredSubs: State<List<SubscriptionModel>> =
+        unfilteredSubscriptions.map {
+            it.asSequence().filterBasedOnProvisioning().filterBasedOnNtn().toList()
+        }
 
-    private fun List<SubscriptionModel>.filterBasedOnProvisioning(): List<SubscriptionModel> =
+    private fun Sequence<SubscriptionModel>.filterBasedOnProvisioning() =
         if (!featureFlagsClassic.isEnabled(FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS)) {
             this
         } else {
-            this.filter { it.profileClass != PROFILE_CLASS_PROVISIONING }
+            filter { it.profileClass != PROFILE_CLASS_PROVISIONING }
         }
 
     /**
@@ -219,9 +195,10 @@
      * need to filter out those subscriptions here so we guarantee the subscription never turns into
      * an icon. See b/336881301.
      */
-    private fun List<SubscriptionModel>.filterBasedOnNtn(): List<SubscriptionModel> {
-        return this.filter { !it.isExclusivelyNonTerrestrial }
-    }
+    private fun Sequence<SubscriptionModel>.filterBasedOnNtn(): Sequence<SubscriptionModel> =
+        filter {
+            !it.isExclusivelyNonTerrestrial
+        }
 
     /**
      * Generally, SystemUI wants to show iconography for each subscription that is listed by
@@ -236,22 +213,23 @@
      * [CarrierConfigManager.KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN],
      * and by checking which subscription is opportunistic, or which one is active.
      */
-    override val filteredSubscriptions: Flow<List<SubscriptionModel>> =
+    override val filteredSubscriptions: State<List<SubscriptionModel>> = buildState {
         combine(
                 subscriptionsBasedFilteredSubs,
                 mobileConnectionsRepo.activeMobileDataSubscriptionId,
-                connectivityRepository.vcnSubId,
+                connectivityRepository.vcnSubId.toState(),
             ) { preFilteredSubs, activeId, vcnSubId ->
                 filterSubsBasedOnOpportunistic(preFilteredSubs, activeId, vcnSubId)
             }
-            .distinctUntilChanged()
-            .logDiffsForTable(
-                tableLogger,
-                LOGGING_PREFIX,
-                columnName = "filteredSubscriptions",
-                initialValue = listOf(),
-            )
-            .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
+            .also {
+                logDiffsForTable(
+                    it,
+                    tableLogger,
+                    LOGGING_PREFIX,
+                    columnName = "filteredSubscriptions",
+                )
+            }
+    }
 
     private fun filterSubsBasedOnOpportunistic(
         subList: List<SubscriptionModel>,
@@ -298,19 +276,25 @@
         }
     }
 
-    override val defaultDataSubId = mobileConnectionsRepo.defaultDataSubId
-
-    override val icons =
-        filteredSubscriptions
-            .mapLatest { subs ->
-                subs.map { getMobileConnectionInteractorForSubId(it.subscriptionId) }
+    override val icons: Incremental<Int, MobileIconInteractorKairos> = buildIncremental {
+        val filteredSubIds =
+            filteredSubscriptions.map { it.asSequence().map { sub -> sub.subscriptionId }.toSet() }
+        mobileConnectionsRepo.mobileConnectionsBySubId
+            .filterIncrementally { (subId, _) ->
+                // Filter out repo if subId is not present in the filtered set
+                filteredSubIds.map { subId in it }
             }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
+            // Just map the repos to interactors
+            .mapValues { (subId, repo) -> buildSpec { mobileConnection(repo) } }
+            .applyLatestSpecForKey()
+    }
 
-    override val isStackable =
+    override val isStackable: State<Boolean> =
         if (NewStatusBarIcons.isEnabled && StatusBarRootModernization.isEnabled) {
-                icons.flatMapLatest { icons ->
-                    combine(icons.map { it.signalLevelIcon }) { signalLevelIcons ->
+            icons.flatMap { iconsBySubId: Map<Int, MobileIconInteractorKairos> ->
+                iconsBySubId.values
+                    .map { it.signalLevelIcon }
+                    .combine { signalLevelIcons ->
                         // These are only stackable if:
                         // - They are cellular
                         // - There's exactly two
@@ -319,11 +303,15 @@
                             it.size == 2 && it[0].numberOfLevels == it[1].numberOfLevels
                         }
                     }
-                }
-            } else {
-                flowOf(false)
             }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+        } else {
+            stateOf(false)
+        }
+
+    override val activeDataIconInteractor: State<MobileIconInteractorKairos?> =
+        combine(mobileConnectionsRepo.activeMobileDataSubscriptionId, icons) { activeSubId, icons ->
+            activeSubId?.let { icons[activeSubId] }
+        }
 
     /**
      * Copied from the old pipeline. We maintain a 2s period of time where we will keep the
@@ -335,67 +323,59 @@
      *
      * The goal of this is to minimize the flickering in the UI of the cellular indicator
      */
-    private val forcingCellularValidation =
+    private val forcingCellularValidation: State<Boolean> = buildState {
         mobileConnectionsRepo.activeSubChangedInGroupEvent
-            .filter { mobileConnectionsRepo.defaultConnectionIsValidated.value }
-            .transformLatest {
-                emit(true)
-                delay(2000)
-                emit(false)
+            .filter(mobileConnectionsRepo.defaultConnectionIsValidated)
+            .mapLatestBuild {
+                asyncEvent {
+                        delay(2.seconds)
+                        false
+                    }
+                    .holdState(true)
             }
-            .logDiffsForTable(
-                tableLogger,
-                LOGGING_PREFIX,
-                columnName = "forcingValidation",
-                initialValue = false,
-            )
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+            .holdState(stateOf(false))
+            .flatten()
+            .also {
+                logDiffsForTable(it, tableLogger, LOGGING_PREFIX, columnName = "forcingValidation")
+            }
+    }
 
     /**
      * Mapping from network type to [MobileIconGroup] using the config generated for the default
      * subscription Id. This mapping is the same for every subscription.
      */
-    override val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>> =
-        mobileConnectionsRepo.defaultMobileIconMapping.stateIn(
-            scope,
-            SharingStarted.WhileSubscribed(),
-            initialValue = mapOf(),
-        )
+    override val defaultMobileIconMapping: State<Map<String, MobileIconGroup>>
+        get() = mobileConnectionsRepo.defaultMobileIconMapping
 
-    override val alwaysShowDataRatIcon: StateFlow<Boolean> =
-        mobileConnectionsRepo.defaultDataSubRatConfig
-            .mapLatest { it.alwaysShowDataRatIcon }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+    override val alwaysShowDataRatIcon: State<Boolean> =
+        mobileConnectionsRepo.defaultDataSubRatConfig.map { it.alwaysShowDataRatIcon }
 
-    override val alwaysUseCdmaLevel: StateFlow<Boolean> =
-        mobileConnectionsRepo.defaultDataSubRatConfig
-            .mapLatest { it.alwaysShowCdmaRssi }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+    override val alwaysUseCdmaLevel: State<Boolean> =
+        mobileConnectionsRepo.defaultDataSubRatConfig.map { it.alwaysShowCdmaRssi }
 
-    override val isSingleCarrier: StateFlow<Boolean> =
+    override val isSingleCarrier: State<Boolean> =
         mobileConnectionsRepo.subscriptions
             .map { it.size == 1 }
-            .logDiffsForTable(
-                tableLogger,
-                columnPrefix = LOGGING_PREFIX,
-                columnName = "isSingleCarrier",
-                initialValue = false,
-            )
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+            .also {
+                onActivated {
+                    logDiffsForTable(
+                        it,
+                        tableLogger,
+                        columnPrefix = LOGGING_PREFIX,
+                        columnName = "isSingleCarrier",
+                    )
+                }
+            }
 
     /** If there is no mapping in [defaultMobileIconMapping], then use this default icon group */
-    override val defaultMobileIconGroup: StateFlow<MobileIconGroup> =
-        mobileConnectionsRepo.defaultMobileIconGroup.stateIn(
-            scope,
-            SharingStarted.WhileSubscribed(),
-            initialValue = TelephonyIcons.G,
-        )
+    override val defaultMobileIconGroup: State<MobileIconGroup>
+        get() = mobileConnectionsRepo.defaultMobileIconGroup
 
     /**
      * We want to show an error state when cellular has actually failed to validate, but not if some
      * other transport type is active, because then we expect there not to be validation.
      */
-    override val isDefaultConnectionFailed: StateFlow<Boolean> =
+    override val isDefaultConnectionFailed: State<Boolean> =
         combine(
                 mobileIsDefault,
                 mobileConnectionsRepo.defaultConnectionIsValidated,
@@ -407,46 +387,63 @@
                     else -> !defaultConnectionIsValidated
                 }
             }
-            .logDiffsForTable(
-                tableLogger,
-                LOGGING_PREFIX,
-                columnName = "isDefaultConnectionFailed",
-                initialValue = false,
-            )
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+            .also {
+                onActivated {
+                    logDiffsForTable(
+                        it,
+                        tableLogger,
+                        LOGGING_PREFIX,
+                        columnName = "isDefaultConnectionFailed",
+                    )
+                }
+            }
 
-    override val isUserSetUp: StateFlow<Boolean> = userSetupRepo.isUserSetUp
+    override val isUserSetUp: State<Boolean> = buildState { userSetupRepo.isUserSetUp.toState() }
 
-    override val isForceHidden: Flow<Boolean> =
-        connectivityRepository.forceHiddenSlots
-            .map { it.contains(ConnectivitySlot.MOBILE) }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+    override val isForceHidden: State<Boolean> = buildState {
+        connectivityRepository.forceHiddenSlots.toState().map {
+            it.contains(ConnectivitySlot.MOBILE)
+        }
+    }
 
-    override val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean> =
-        mobileConnectionsRepo.isDeviceEmergencyCallCapable
+    override val isDeviceInEmergencyCallsOnlyMode: State<Boolean>
+        get() = mobileConnectionsRepo.isDeviceEmergencyCallCapable
 
-    /** Vends out new [MobileIconInteractor] for a particular subId */
-    override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
-        reuseCache[subId]?.get() ?: createMobileConnectionInteractorForSubId(subId)
-
-    private fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
-        MobileIconInteractorImpl(
-                scope,
-                activeDataConnectionHasDataEnabled,
-                alwaysShowDataRatIcon,
-                alwaysUseCdmaLevel,
-                isSingleCarrier,
-                mobileIsDefault,
-                defaultMobileIconMapping,
-                defaultMobileIconGroup,
-                isDefaultConnectionFailed,
-                isForceHidden,
-                mobileConnectionsRepo.getRepoForSubId(subId),
-                context,
-            )
-            .also { reuseCache[subId] = WeakReference(it) }
+    /** Vends out a new [MobileIconInteractorKairos] for a particular subId */
+    private fun BuildScope.mobileConnection(
+        repo: MobileConnectionRepositoryKairos
+    ): MobileIconInteractorKairos = activated {
+        MobileIconInteractorKairosImpl(
+            activeDataConnectionHasDataEnabled,
+            alwaysShowDataRatIcon,
+            alwaysUseCdmaLevel,
+            isSingleCarrier,
+            mobileIsDefault,
+            defaultMobileIconMapping,
+            defaultMobileIconGroup,
+            isDefaultConnectionFailed,
+            isForceHidden,
+            repo,
+            context,
+        )
+    }
 
     companion object {
         private const val LOGGING_PREFIX = "Intr"
     }
+
+    @dagger.Module
+    interface Module {
+
+        @Binds fun bindImpl(impl: MobileIconsInteractorKairosImpl): MobileIconsInteractorKairos
+
+        companion object {
+            @Provides
+            @ElementsIntoSet
+            fun kairosActivatable(
+                impl: Provider<MobileIconsInteractorKairosImpl>
+            ): Set<@JvmSuppressWildcards KairosActivatable> =
+                if (Flags.statusBarMobileIconKairos()) setOf(impl.get()) else emptySet()
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt
new file mode 100644
index 0000000..6b9c537
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import android.content.Context
+import com.android.settingslib.SignalIcon
+import com.android.settingslib.mobile.MobileMappings
+import com.android.systemui.Flags
+import com.android.systemui.KairosActivatable
+import com.android.systemui.KairosBuilder
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.kairos.buildSpec
+import com.android.systemui.kairos.combine
+import com.android.systemui.kairos.map
+import com.android.systemui.kairos.mapValues
+import com.android.systemui.kairos.toColdConflatedFlow
+import com.android.systemui.kairosBuilder
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.MOBILE_CONNECTION_BUFFER_SIZE
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
+import dagger.Provides
+import dagger.multibindings.ElementsIntoSet
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@ExperimentalKairosApi
+@SysUISingleton
+class MobileIconsInteractorKairosAdapter
+@Inject
+constructor(
+    private val kairosInteractor: MobileIconsInteractorKairos,
+    private val repo: MobileConnectionsRepository,
+    repoK: MobileConnectionsRepositoryKairos,
+    kairosNetwork: KairosNetwork,
+    @Application scope: CoroutineScope,
+    context: Context,
+    mobileMappingsProxy: MobileMappingsProxy,
+    private val userSetupRepo: UserSetupRepository,
+    private val logFactory: TableLogBufferFactory,
+) : MobileIconsInteractor, KairosBuilder by kairosBuilder() {
+
+    private val interactorsBySubIdK = buildIncremental {
+        kairosInteractor.icons
+            .mapValues { (subId, interactor) ->
+                buildSpec { MobileIconInteractorKairosAdapter(interactor) }
+            }
+            .applyLatestSpecForKey()
+    }
+
+    private val interactorsBySubId =
+        interactorsBySubIdK
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.Eagerly, emptyMap())
+
+    override val defaultDataSubId: Flow<Int?>
+        get() = repo.defaultDataSubId
+
+    override val mobileIsDefault: StateFlow<Boolean> =
+        kairosInteractor.mobileIsDefault
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), repo.mobileIsDefault.value)
+
+    override val filteredSubscriptions: Flow<List<SubscriptionModel>> =
+        kairosInteractor.filteredSubscriptions.toColdConflatedFlow(kairosNetwork)
+
+    override val icons: StateFlow<List<MobileIconInteractor>> =
+        interactorsBySubIdK
+            .map { it.values.toList() }
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
+
+    override val isStackable: StateFlow<Boolean> =
+        kairosInteractor.isStackable
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val activeMobileDataSubscriptionId: StateFlow<Int?>
+        get() = repo.activeMobileDataSubscriptionId
+
+    override val activeDataConnectionHasDataEnabled: StateFlow<Boolean> =
+        kairosInteractor.activeDataConnectionHasDataEnabled
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val activeDataIconInteractor: StateFlow<MobileIconInteractor?> =
+        combine(repoK.activeMobileDataSubscriptionId, interactorsBySubIdK) { subId, interactors ->
+                interactors[subId]
+            }
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
+    override val alwaysShowDataRatIcon: StateFlow<Boolean> =
+        kairosInteractor.alwaysShowDataRatIcon
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val alwaysUseCdmaLevel: StateFlow<Boolean> =
+        kairosInteractor.alwaysUseCdmaLevel
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val isSingleCarrier: StateFlow<Boolean> =
+        kairosInteractor.isSingleCarrier
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val defaultMobileIconMapping: StateFlow<Map<String, SignalIcon.MobileIconGroup>> =
+        kairosInteractor.defaultMobileIconMapping
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), emptyMap())
+
+    override val defaultMobileIconGroup: StateFlow<SignalIcon.MobileIconGroup> =
+        kairosInteractor.defaultMobileIconGroup
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(
+                scope,
+                SharingStarted.WhileSubscribed(),
+                mobileMappingsProxy.getDefaultIcons(MobileMappings.Config.readConfig(context)),
+            )
+
+    override val isDefaultConnectionFailed: StateFlow<Boolean> =
+        kairosInteractor.isDefaultConnectionFailed
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val isUserSetUp: StateFlow<Boolean>
+        get() = userSetupRepo.isUserSetUp
+
+    override val isForceHidden: Flow<Boolean> =
+        kairosInteractor.isForceHidden
+            .toColdConflatedFlow(kairosNetwork)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean>
+        get() = repo.isDeviceEmergencyCallCapable
+
+    override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
+        object : MobileIconInteractor {
+            override val tableLogBuffer: TableLogBuffer =
+                logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE)
+            override val activity: Flow<DataActivityModel> = latest { activity }
+            override val mobileIsDefault: Flow<Boolean> = latest { mobileIsDefault }
+            override val isDataConnected: Flow<Boolean> = latest { isDataConnected }
+            override val isInService: Flow<Boolean> = latest { isInService }
+            override val isEmergencyOnly: Flow<Boolean> = latest { isEmergencyOnly }
+            override val isDataEnabled: Flow<Boolean> = latest { isDataEnabled }
+            override val alwaysShowDataRatIcon: Flow<Boolean> = latest { alwaysShowDataRatIcon }
+            override val signalLevelIcon: Flow<SignalIconModel> = latest { signalLevelIcon }
+            override val networkTypeIconGroup: Flow<NetworkTypeIconModel> = latest {
+                networkTypeIconGroup
+            }
+            override val showSliceAttribution: Flow<Boolean> = latest { showSliceAttribution }
+            override val isNonTerrestrial: Flow<Boolean> = latest { isNonTerrestrial }
+            override val networkName: Flow<NetworkNameModel> = latest { networkName }
+            override val carrierName: Flow<String> = latest { carrierName }
+            override val isSingleCarrier: Flow<Boolean> = latest { isSingleCarrier }
+            override val isRoaming: Flow<Boolean> = latest { isRoaming }
+            override val isForceHidden: Flow<Boolean> = latest { isForceHidden }
+            override val isAllowedDuringAirplaneMode: Flow<Boolean> = latest {
+                isAllowedDuringAirplaneMode
+            }
+            override val carrierNetworkChangeActive: Flow<Boolean> = latest {
+                carrierNetworkChangeActive
+            }
+
+            private fun <T> latest(block: MobileIconInteractor.() -> Flow<T>): Flow<T> =
+                interactorsBySubId.flatMapLatestConflated { it[subId]?.block() ?: emptyFlow() }
+        }
+
+    @dagger.Module
+    object Module {
+        @Provides
+        @ElementsIntoSet
+        fun kairosActivatable(
+            impl: Provider<MobileIconsInteractorKairosAdapter>
+        ): Set<@JvmSuppressWildcards KairosActivatable> =
+            if (Flags.statusBarMobileIconKairos()) setOf(impl.get()) else emptySet()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt
new file mode 100644
index 0000000..fce8c85
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt
@@ -0,0 +1,113 @@
+/*
+ * 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.pipeline.mobile.ui.viewmodel
+
+import android.graphics.Color
+import com.android.systemui.statusbar.phone.StatusBarLocation
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * A view model for an individual mobile icon that embeds the notion of a [StatusBarLocation]. This
+ * allows the mobile icon to change some view parameters at different locations
+ *
+ * @param commonImpl for convenience, this class wraps a base interface that can provides all of the
+ *   common implementations between locations. See [MobileIconViewModel]
+ * @property location the [StatusBarLocation] of this VM.
+ * @property verboseLogger an optional logger to log extremely verbose view updates.
+ */
+abstract class LocationBasedMobileViewModelKairos(
+    val commonImpl: MobileIconViewModelCommonKairos,
+    val location: StatusBarLocation,
+    val verboseLogger: VerboseMobileViewLogger?,
+) : MobileIconViewModelCommonKairos by commonImpl {
+    val defaultColor: Int = Color.WHITE
+
+    companion object {
+        fun viewModelForLocation(
+            commonImpl: MobileIconViewModelCommon,
+            interactor: MobileIconInteractor,
+            verboseMobileViewLogger: VerboseMobileViewLogger,
+            location: StatusBarLocation,
+            scope: CoroutineScope,
+        ): LocationBasedMobileViewModel =
+            when (location) {
+                StatusBarLocation.HOME ->
+                    HomeMobileIconViewModel(commonImpl, verboseMobileViewLogger)
+                StatusBarLocation.KEYGUARD -> KeyguardMobileIconViewModel(commonImpl)
+                StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl)
+                StatusBarLocation.SHADE_CARRIER_GROUP ->
+                    ShadeCarrierGroupMobileIconViewModel(commonImpl, interactor, scope)
+            }
+    }
+}
+
+class HomeMobileIconViewModelKairos(
+    commonImpl: MobileIconViewModelCommonKairos,
+    verboseMobileViewLogger: VerboseMobileViewLogger,
+) :
+    MobileIconViewModelCommonKairos,
+    LocationBasedMobileViewModelKairos(
+        commonImpl,
+        location = StatusBarLocation.HOME,
+        verboseMobileViewLogger,
+    )
+
+class QsMobileIconViewModelKairos(commonImpl: MobileIconViewModelCommonKairos) :
+    MobileIconViewModelCommonKairos,
+    LocationBasedMobileViewModelKairos(
+        commonImpl,
+        location = StatusBarLocation.QS,
+        // Only do verbose logging for the Home location.
+        verboseLogger = null,
+    )
+
+class ShadeCarrierGroupMobileIconViewModelKairos(
+    commonImpl: MobileIconViewModelCommonKairos,
+    interactor: MobileIconInteractor,
+    scope: CoroutineScope,
+) :
+    MobileIconViewModelCommonKairos,
+    LocationBasedMobileViewModelKairos(
+        commonImpl,
+        location = StatusBarLocation.SHADE_CARRIER_GROUP,
+        // Only do verbose logging for the Home location.
+        verboseLogger = null,
+    ) {
+    private val isSingleCarrier = interactor.isSingleCarrier
+    val carrierName = interactor.carrierName
+
+    override val isVisible: StateFlow<Boolean> =
+        combine(super.isVisible, isSingleCarrier) { isVisible, isSingleCarrier ->
+                if (isSingleCarrier) false else isVisible
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), super.isVisible.value)
+}
+
+class KeyguardMobileIconViewModelKairos(commonImpl: MobileIconViewModelCommonKairos) :
+    MobileIconViewModelCommonKairos,
+    LocationBasedMobileViewModelKairos(
+        commonImpl,
+        location = StatusBarLocation.KEYGUARD,
+        // Only do verbose logging for the Home location.
+        verboseLogger = null,
+    )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt
new file mode 100644
index 0000000..cc7fc09
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt
@@ -0,0 +1,328 @@
+/*
+ * 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.pipeline.mobile.ui.viewmodel
+
+import com.android.systemui.Flags.statusBarStaticInoutIndicators
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.core.NewStatusBarIcons
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
+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
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+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
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.stateIn
+
+/** Common interface for all of the location-based mobile icon view models. */
+interface MobileIconViewModelCommonKairos : MobileIconViewModelCommon {
+    override val subscriptionId: Int
+    /** True if this view should be visible at all. */
+    override val isVisible: StateFlow<Boolean>
+    override val icon: Flow<SignalIconModel>
+    override val contentDescription: Flow<MobileContentDescription?>
+    override val roaming: Flow<Boolean>
+    /** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
+    override val networkTypeIcon: Flow<Icon.Resource?>
+    /** The slice attribution. Drawn as a background layer */
+    override val networkTypeBackground: StateFlow<Icon.Resource?>
+    override val activityInVisible: Flow<Boolean>
+    override val activityOutVisible: Flow<Boolean>
+    override val activityContainerVisible: Flow<Boolean>
+}
+
+/**
+ * View model for the state of a single mobile icon. Each [MobileIconViewModel] will keep watch over
+ * a single line of service via [MobileIconInteractor] and update the UI based on that
+ * subscription's information.
+ *
+ * There will be exactly one [MobileIconViewModel] per filtered subscription offered from
+ * [MobileIconsInteractor.filteredSubscriptions].
+ *
+ * For the sake of keeping log spam in check, every flow funding the [MobileIconViewModelCommon]
+ * interface is implemented as a [StateFlow]. This ensures that each location-based mobile icon view
+ * model gets the exact same information, as well as allows us to log that unified state only once
+ * per icon.
+ */
+class MobileIconViewModelKairos(
+    override val subscriptionId: Int,
+    iconInteractor: MobileIconInteractor,
+    airplaneModeInteractor: AirplaneModeInteractor,
+    constants: ConnectivityConstants,
+    scope: CoroutineScope,
+) : MobileIconViewModelCommonKairos {
+    private val cellProvider by lazy {
+        CellularIconViewModelKairos(
+            subscriptionId,
+            iconInteractor,
+            airplaneModeInteractor,
+            constants,
+            scope,
+        )
+    }
+
+    private val satelliteProvider by lazy {
+        CarrierBasedSatelliteViewModelKairosImpl(
+            subscriptionId,
+            airplaneModeInteractor,
+            iconInteractor,
+            scope,
+        )
+    }
+
+    /**
+     * Similar to repository switching, this allows us to split up the logic of satellite/cellular
+     * states, since they are different by nature
+     */
+    private val vmProvider: Flow<MobileIconViewModelCommon> =
+        iconInteractor.isNonTerrestrial
+            .mapLatest { nonTerrestrial ->
+                if (nonTerrestrial) {
+                    satelliteProvider
+                } else {
+                    cellProvider
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), cellProvider)
+
+    override val isVisible: StateFlow<Boolean> =
+        vmProvider
+            .flatMapLatest { it.isVisible }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val icon: Flow<SignalIconModel> = vmProvider.flatMapLatest { it.icon }
+
+    override val contentDescription: Flow<MobileContentDescription?> =
+        vmProvider.flatMapLatest { it.contentDescription }
+
+    override val roaming: Flow<Boolean> = vmProvider.flatMapLatest { it.roaming }
+
+    override val networkTypeIcon: Flow<Icon.Resource?> =
+        vmProvider.flatMapLatest { it.networkTypeIcon }
+
+    override val networkTypeBackground: StateFlow<Icon.Resource?> =
+        vmProvider
+            .flatMapLatest { it.networkTypeBackground }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
+    override val activityInVisible: Flow<Boolean> =
+        vmProvider.flatMapLatest { it.activityInVisible }
+
+    override val activityOutVisible: Flow<Boolean> =
+        vmProvider.flatMapLatest { it.activityOutVisible }
+
+    override val activityContainerVisible: Flow<Boolean> =
+        vmProvider.flatMapLatest { it.activityContainerVisible }
+}
+
+/** Representation of this network when it is non-terrestrial (e.g., satellite) */
+private class CarrierBasedSatelliteViewModelKairosImpl(
+    override val subscriptionId: Int,
+    airplaneModeInteractor: AirplaneModeInteractor,
+    interactor: MobileIconInteractor,
+    scope: CoroutineScope,
+) : MobileIconViewModelCommon, MobileIconViewModelCommonKairos {
+    override val isVisible: StateFlow<Boolean> =
+        airplaneModeInteractor.isAirplaneMode
+            .map { !it }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val icon: Flow<SignalIconModel> = interactor.signalLevelIcon
+
+    override val contentDescription: Flow<MobileContentDescription?> = MutableStateFlow(null)
+
+    /** These fields are not used for satellite icons currently */
+    override val roaming: Flow<Boolean> = flowOf(false)
+    override val networkTypeIcon: Flow<Icon.Resource?> = flowOf(null)
+    override val networkTypeBackground: StateFlow<Icon.Resource?> = MutableStateFlow(null)
+    override val activityInVisible: Flow<Boolean> = flowOf(false)
+    override val activityOutVisible: Flow<Boolean> = flowOf(false)
+    override val activityContainerVisible: Flow<Boolean> = flowOf(false)
+}
+
+/** Terrestrial (cellular) icon. */
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+private class CellularIconViewModelKairos(
+    override val subscriptionId: Int,
+    iconInteractor: MobileIconInteractor,
+    airplaneModeInteractor: AirplaneModeInteractor,
+    constants: ConnectivityConstants,
+    scope: CoroutineScope,
+) : MobileIconViewModelCommon, MobileIconViewModelCommonKairos {
+    override val isVisible: StateFlow<Boolean> =
+        if (!constants.hasDataCapabilities) {
+                flowOf(false)
+            } else {
+                combine(
+                    airplaneModeInteractor.isAirplaneMode,
+                    iconInteractor.isAllowedDuringAirplaneMode,
+                    iconInteractor.isForceHidden,
+                ) { isAirplaneMode, isAllowedDuringAirplaneMode, isForceHidden ->
+                    if (isForceHidden) {
+                        false
+                    } else if (isAirplaneMode) {
+                        isAllowedDuringAirplaneMode
+                    } else {
+                        true
+                    }
+                }
+            }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                iconInteractor.tableLogBuffer,
+                columnName = "visible",
+                initialValue = false,
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val icon: Flow<SignalIconModel> = iconInteractor.signalLevelIcon
+
+    override val contentDescription: Flow<MobileContentDescription?> =
+        combine(iconInteractor.signalLevelIcon, iconInteractor.networkName) { icon, nameModel ->
+                when (icon) {
+                    is SignalIconModel.Cellular ->
+                        MobileContentDescription.Cellular(
+                            nameModel.name,
+                            icon.levelDescriptionRes(),
+                        )
+                    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,
+                iconInteractor.isDataEnabled,
+                iconInteractor.alwaysShowDataRatIcon,
+                iconInteractor.mobileIsDefault,
+                iconInteractor.carrierNetworkChangeActive,
+            ) { dataConnected, dataEnabled, alwaysShow, mobileIsDefault, carrierNetworkChange ->
+                alwaysShow ||
+                    (!carrierNetworkChange && (dataEnabled && dataConnected && mobileIsDefault))
+            }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                iconInteractor.tableLogBuffer,
+                columnName = "showNetworkTypeIcon",
+                initialValue = false,
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val networkTypeIcon: Flow<Icon.Resource?> =
+        combine(iconInteractor.networkTypeIconGroup, showNetworkTypeIcon) {
+                networkTypeIconGroup,
+                shouldShow ->
+                val desc =
+                    if (networkTypeIconGroup.contentDescription != 0)
+                        ContentDescription.Resource(networkTypeIconGroup.contentDescription)
+                    else null
+                val icon =
+                    if (networkTypeIconGroup.iconId != 0)
+                        Icon.Resource(networkTypeIconGroup.iconId, desc)
+                    else null
+                return@combine when {
+                    !shouldShow -> null
+                    else -> icon
+                }
+            }
+            .distinctUntilChanged()
+            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
+    override val networkTypeBackground =
+        iconInteractor.showSliceAttribution
+            .map {
+                when {
+                    it && NewStatusBarIcons.isEnabled ->
+                        Icon.Resource(R.drawable.mobile_network_type_background_updated, null)
+                    it -> Icon.Resource(R.drawable.mobile_network_type_background, null)
+                    else -> null
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
+    override val roaming: StateFlow<Boolean> =
+        iconInteractor.isRoaming
+            .logDiffsForTable(
+                iconInteractor.tableLogBuffer,
+                columnName = "roaming",
+                initialValue = false,
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    private val activity: Flow<DataActivityModel?> =
+        if (!constants.shouldShowActivityConfig) {
+            flowOf(null)
+        } else {
+            iconInteractor.activity
+        }
+
+    override val activityInVisible: Flow<Boolean> =
+        activity
+            .map { it?.hasActivityIn ?: false }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val activityOutVisible: Flow<Boolean> =
+        activity
+            .map { it?.hasActivityOut ?: false }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val activityContainerVisible: Flow<Boolean> =
+        if (statusBarStaticInoutIndicators()) {
+                flowOf(constants.shouldShowActivityConfig)
+            } else {
+                activity.map { it != null && (it.hasActivityIn || it.hasActivityOut) }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt
new file mode 100644
index 0000000..a655407
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.pipeline.mobile.ui.viewmodel
+
+import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.coroutines.newTracingContext
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.phone.StatusBarLocation
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
+import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
+import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
+import java.util.concurrent.ConcurrentHashMap
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * View model for describing the system's current mobile cellular connections. The result is a list
+ * of [MobileIconViewModel]s which describe the individual icons and can be bound to
+ * [ModernStatusBarMobileView].
+ */
+@SysUISingleton
+class MobileIconsViewModelKairos
+@Inject
+constructor(
+    val logger: MobileViewLogger,
+    private val verboseLogger: VerboseMobileViewLogger,
+    private val interactor: MobileIconsInteractor,
+    private val airplaneModeInteractor: AirplaneModeInteractor,
+    private val constants: ConnectivityConstants,
+    @Background private val scope: CoroutineScope,
+) {
+    @VisibleForTesting
+    val reuseCache = ConcurrentHashMap<Int, Pair<MobileIconViewModel, CoroutineScope>>()
+
+    val activeMobileDataSubscriptionId: StateFlow<Int?> = interactor.activeMobileDataSubscriptionId
+
+    val subscriptionIdsFlow: StateFlow<List<Int>> =
+        interactor.filteredSubscriptions
+            .mapLatest { subscriptions ->
+                subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
+
+    val mobileSubViewModels: StateFlow<List<MobileIconViewModelCommon>> =
+        subscriptionIdsFlow
+            .map { ids -> ids.map { commonViewModelForSub(it) } }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
+
+    private val firstMobileSubViewModel: StateFlow<MobileIconViewModelCommon?> =
+        mobileSubViewModels
+            .map {
+                if (it.isEmpty()) {
+                    null
+                } else {
+                    // Mobile icons get reversed by [StatusBarIconController], so the last element
+                    // in this list will show up visually first.
+                    it.last()
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
+    /**
+     * A flow that emits `true` if the mobile sub that's displayed first visually is showing its
+     * network type icon and `false` otherwise.
+     */
+    val firstMobileSubShowingNetworkTypeIcon: StateFlow<Boolean> =
+        firstMobileSubViewModel
+            .flatMapLatest { firstMobileSubViewModel ->
+                firstMobileSubViewModel?.networkTypeIcon?.map { it != null } ?: flowOf(false)
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    val isStackable: StateFlow<Boolean> = interactor.isStackable
+
+    init {
+        scope.launch { subscriptionIdsFlow.collect { invalidateCaches(it) } }
+    }
+
+    fun viewModelForSub(subId: Int, location: StatusBarLocation): LocationBasedMobileViewModel {
+        val common = commonViewModelForSub(subId)
+        return LocationBasedMobileViewModel.viewModelForLocation(
+            common,
+            interactor.getMobileConnectionInteractorForSubId(subId),
+            verboseLogger,
+            location,
+            scope,
+        )
+    }
+
+    private fun commonViewModelForSub(subId: Int): MobileIconViewModelCommon {
+        return reuseCache.getOrPut(subId) { createViewModel(subId) }.first
+    }
+
+    private fun createViewModel(subId: Int): Pair<MobileIconViewModel, CoroutineScope> {
+        // Create a child scope so we can cancel it
+        val vmScope = scope.createChildScope(newTracingContext("MobileIconViewModel"))
+        val vm =
+            MobileIconViewModel(
+                subId,
+                interactor.getMobileConnectionInteractorForSubId(subId),
+                airplaneModeInteractor,
+                constants,
+                vmScope,
+            )
+
+        return Pair(vm, vmScope)
+    }
+
+    private fun CoroutineScope.createChildScope(extraContext: CoroutineContext) =
+        CoroutineScope(coroutineContext + Job(coroutineContext[Job]) + extraContext)
+
+    private fun invalidateCaches(subIds: List<Int>) {
+        reuseCache.keys
+            .filter { !subIds.contains(it) }
+            .forEach { id ->
+                reuseCache
+                    .remove(id)
+                    // Cancel the view model's scope after removing it
+                    ?.second
+                    ?.cancel()
+            }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt
new file mode 100644
index 0000000..2dbb02c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.viewmodel
+
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class StackedMobileIconViewModelKairos
+@AssistedInject
+constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable() {
+    private val hydrator = Hydrator("StackedMobileIconViewModel")
+
+    private val isStackable: Boolean by
+        hydrator.hydratedStateOf(
+            traceName = "isStackable",
+            source = mobileIconsViewModel.isStackable,
+            initialValue = false,
+        )
+
+    private val iconViewModelFlow: Flow<List<MobileIconViewModelCommon>> =
+        combine(
+            mobileIconsViewModel.mobileSubViewModels,
+            mobileIconsViewModel.activeMobileDataSubscriptionId,
+        ) { viewModels, activeSubId ->
+            // Sort to get the active subscription first, if it's set
+            viewModels.sortedByDescending { it.subscriptionId == activeSubId }
+        }
+
+    val dualSim: DualSim? by
+        hydrator.hydratedStateOf(
+            traceName = "dualSim",
+            source =
+                iconViewModelFlow.flatMapLatest { viewModels ->
+                    combine(viewModels.map { it.icon }) { icons ->
+                        icons
+                            .toList()
+                            .filterIsInstance<SignalIconModel.Cellular>()
+                            .takeIf { it.size == 2 }
+                            ?.let { DualSim(it[0], it[1]) }
+                    }
+                },
+            initialValue = null,
+        )
+
+    val networkTypeIcon: Icon.Resource? by
+        hydrator.hydratedStateOf(
+            traceName = "networkTypeIcon",
+            source =
+                iconViewModelFlow.flatMapLatest { viewModels ->
+                    viewModels.firstOrNull()?.networkTypeIcon ?: flowOf(null)
+                },
+            initialValue = null,
+        )
+
+    val isIconVisible: Boolean by derivedStateOf { isStackable && dualSim != null }
+
+    override suspend fun onActivated(): Nothing {
+        hydrator.activate()
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): StackedMobileIconViewModelKairos
+    }
+
+    data class DualSim(
+        val primary: SignalIconModel.Cellular,
+        val secondary: SignalIconModel.Cellular,
+    )
+}
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 982f6ec..2a6ff3b 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
@@ -28,7 +28,7 @@
 import android.telephony.satellite.SatelliteProvisionStateCallback
 import androidx.annotation.VisibleForTesting
 import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index cfd5097..6fada26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -203,6 +203,8 @@
                         OngoingActivityChipBinder.createBinding(
                             view.requireViewById(R.id.ongoing_activity_chip_secondary)
                         )
+                    OngoingActivityChipBinder.updateTypefaces(primaryChipViewBinding)
+                    OngoingActivityChipBinder.updateTypefaces(secondaryChipViewBinding)
                     launch {
                         combine(
                                 viewModel.ongoingActivityChipsLegacy,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index c91ea9a..2f9cff4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -196,9 +196,8 @@
 
                                 setContent {
                                     PlatformTheme {
-                                        val chipsVisibilityModel by
+                                        val chipsVisibilityModel =
                                             statusBarViewModel.ongoingActivityChips
-                                                .collectAsStateWithLifecycle()
                                         if (chipsVisibilityModel.areChipsAllowed) {
                                             OngoingActivityChips(
                                                 chips = chipsVisibilityModel.chips,
@@ -255,7 +254,7 @@
                             expandedMatchesParentHeight = true
                             showsOnlyActiveMedia = true
                             falsingProtectionNeeded = false
-                            disablePagination = true
+                            disableScrolling = true
                             init(MediaHierarchyManager.LOCATION_STATUS_BAR_POPUP)
                         }
 
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 3bae91a..540baba 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
@@ -21,6 +21,8 @@
 import android.view.Display
 import android.view.View
 import androidx.compose.runtime.getValue
+import com.android.app.tracing.FlowTracing.traceEach
+import com.android.app.tracing.TrackGroupUtils.trackGroup
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -130,7 +132,7 @@
     val primaryOngoingActivityChip: StateFlow<OngoingActivityChipModel>
 
     /** All supported activity chips, whether they are currently active or not. */
-    val ongoingActivityChips: StateFlow<ChipsVisibilityModel>
+    val ongoingActivityChips: ChipsVisibilityModel
 
     /**
      * The multiple ongoing activity chips that should be shown on the left-hand side of the status
@@ -262,6 +264,12 @@
     override val popupChips
         get() = statusBarPopupChips.shownPopupChips
 
+    private val isShadeExpandedEnough =
+        // Keep the status bar visible while the shade is just starting to open, but otherwise
+        // hide it so that the status bar doesn't draw while it can't be seen.
+        // See b/394257529#comment24.
+        shadeInteractor.anyExpansion.map { it >= 0.2 }.distinctUntilChanged()
+
     /**
      * Whether the display of this statusbar has the shade window (that is hosting shade container
      * and lockscreen, among other things).
@@ -283,7 +291,7 @@
                     Overlays.QuickSettingsShade in currentOverlays)
             }
         } else {
-            shadeInteractor.isAnyFullyExpanded
+            isShadeExpandedEnough
         }
 
     private val isShadeVisibleOnThisDisplay: Flow<Boolean> =
@@ -380,11 +388,9 @@
         }
 
     override val isHomeStatusBarAllowed =
-        isHomeStatusBarAllowedCompat.stateIn(
-            bgScope,
-            SharingStarted.WhileSubscribed(),
-            initialValue = false,
-        )
+        isHomeStatusBarAllowedCompat
+            .traceEach(trackGroup(TRACK_GROUP, "isHomeStatusBarAllowed"), logcat = true)
+            .stateIn(bgScope, SharingStarted.WhileSubscribed(), initialValue = false)
 
     private val shouldHomeStatusBarBeVisible =
         combine(
@@ -455,24 +461,29 @@
             isHomeStatusBarAllowed && !isSecureCameraActive && !hideStartSideContentForHeadsUp
         }
 
-    override val ongoingActivityChips =
+    private val chipsVisibilityModel: Flow<ChipsVisibilityModel> =
         combine(ongoingActivityChipsViewModel.chips, canShowOngoingActivityChips) { chips, canShow
                 ->
                 ChipsVisibilityModel(chips, areChipsAllowed = canShow)
             }
-            .stateIn(
-                bgScope,
-                SharingStarted.WhileSubscribed(),
-                initialValue =
-                    ChipsVisibilityModel(
-                        chips = MultipleOngoingActivityChipsModel(),
-                        areChipsAllowed = false,
-                    ),
-            )
+            .traceEach(trackGroup(TRACK_GROUP, "chips"), logcat = true) {
+                "Chips[allowed=${it.areChipsAllowed} numChips=${it.chips.active.size}]"
+            }
+
+    override val ongoingActivityChips: ChipsVisibilityModel by
+        hydrator.hydratedStateOf(
+            traceName = "ongoingActivityChips",
+            initialValue =
+                ChipsVisibilityModel(
+                    chips = MultipleOngoingActivityChipsModel(),
+                    areChipsAllowed = false,
+                ),
+            source = chipsVisibilityModel,
+        )
 
     private val hasOngoingActivityChips =
         if (StatusBarChipsModernization.isEnabled) {
-            ongoingActivityChips.map { it.chips.active.any { chip -> !chip.isHidden } }
+            chipsVisibilityModel.map { it.chips.active.any { chip -> !chip.isHidden } }
         } else if (StatusBarNotifChips.isEnabled) {
             ongoingActivityChipsLegacy.map { it.primary is OngoingActivityChipModel.Active }
         } else {
@@ -601,6 +612,8 @@
         private const val COL_PREFIX_NOTIF_CONTAINER = "notifContainer"
         private const val COL_PREFIX_SYSTEM_INFO = "systemInfo"
 
+        private const val TRACK_GROUP = "StatusBar"
+
         fun tableLogBufferName(displayId: Int) = "HomeStatusBarViewModel[$displayId]"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index f9bba9d..eaceb5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -26,7 +26,7 @@
 import androidx.lifecycle.LifecycleRegistry
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.Flags.multiuserWifiPickerTrackerSupport
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 9a81992..7f778bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -256,6 +256,9 @@
                     if (mClockFormat != null) {
                         mClockFormat.setTimeZone(mCalendar.getTimeZone());
                     }
+                    if (mContentDescriptionFormat != null) {
+                        mContentDescriptionFormat.setTimeZone(mCalendar.getTimeZone());
+                    }
                 });
             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
                 final Locale newLocale = getResources().getConfiguration().locale;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt
index 0a2bbe5..14cadd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt
@@ -14,7 +14,7 @@
 package com.android.systemui.statusbar.policy
 
 import android.content.res.Configuration
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index b13e01b..fa022b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -27,6 +27,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -56,8 +57,8 @@
     private int mDeviceState = -1;
     @Nullable
     private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
-    private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
-            mDeviceStateRotationLockSettingsListener;
+    private DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+            mDeviceStateAutoRotateSettingListener;
 
     @Inject
     public DeviceStateRotationLockSettingController(
@@ -83,17 +84,17 @@
             // is no user action.
             mDeviceStateCallback = this::updateDeviceState;
             mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
-            mDeviceStateRotationLockSettingsListener = () ->
+            mDeviceStateAutoRotateSettingListener = () ->
                     readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
             mDeviceStateRotationLockSettingsManager.registerListener(
-                    mDeviceStateRotationLockSettingsListener);
+                    mDeviceStateAutoRotateSettingListener);
         } else {
             if (mDeviceStateCallback != null) {
                 mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
             }
-            if (mDeviceStateRotationLockSettingsListener != null) {
+            if (mDeviceStateAutoRotateSettingListener != null) {
                 mDeviceStateRotationLockSettingsManager.unregisterListener(
-                        mDeviceStateRotationLockSettingsListener);
+                        mDeviceStateAutoRotateSettingListener);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
index 3cb7090..f6e0123 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -32,45 +32,50 @@
 import com.android.systemui.qs.tiles.FlashlightTile
 import com.android.systemui.qs.tiles.LocationTile
 import com.android.systemui.qs.tiles.MicrophoneToggleTile
+import com.android.systemui.qs.tiles.ModesDndTile
 import com.android.systemui.qs.tiles.ModesTile
 import com.android.systemui.qs.tiles.UiModeNightTile
 import com.android.systemui.qs.tiles.WorkModeTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.impl.alarm.domain.AlarmTileMapper
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
 import com.android.systemui.qs.tiles.impl.alarm.domain.interactor.AlarmTileDataInteractor
 import com.android.systemui.qs.tiles.impl.alarm.domain.interactor.AlarmTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
-import com.android.systemui.qs.tiles.impl.flashlight.domain.FlashlightMapper
+import com.android.systemui.qs.tiles.impl.alarm.ui.mapper.AlarmTileMapper
 import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileDataInteractor
 import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
-import com.android.systemui.qs.tiles.impl.location.domain.LocationTileMapper
+import com.android.systemui.qs.tiles.impl.flashlight.ui.mapper.FlashlightMapper
 import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileDataInteractor
 import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
+import com.android.systemui.qs.tiles.impl.location.ui.mapper.LocationTileMapper
+import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileDataInteractor
+import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.impl.modes.ui.ModesTileMapper
-import com.android.systemui.qs.tiles.impl.sensorprivacy.SensorPrivacyToggleTileDataInteractor
-import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.SensorPrivacyToggleTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.modes.ui.ModesDndTileMapper
+import com.android.systemui.qs.tiles.impl.modes.ui.mapper.ModesTileMapper
+import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.interactor.SensorPrivacyToggleTileDataInteractor
+import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.interactor.SensorPrivacyToggleTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
-import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyTileResources
-import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyToggleTileMapper
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.UiModeNightTileMapper
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.mapper.SensorPrivacyToggleTileMapper
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model.SensorPrivacyTileResources
 import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.UiModeNightTileDataInteractor
 import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.UiModeNightTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
+import com.android.systemui.qs.tiles.impl.uimodenight.ui.mapper.UiModeNightTileMapper
 import com.android.systemui.qs.tiles.impl.work.domain.interactor.WorkModeTileDataInteractor
 import com.android.systemui.qs.tiles.impl.work.domain.interactor.WorkModeTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
-import com.android.systemui.qs.tiles.impl.work.ui.WorkModeTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.impl.work.ui.mapper.WorkModeTileMapper
 import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
@@ -132,6 +137,7 @@
         const val CAMERA_TOGGLE_TILE_SPEC = "cameratoggle"
         const val MIC_TOGGLE_TILE_SPEC = "mictoggle"
         const val DND_TILE_SPEC = "dnd"
+        const val MODES_DND_TILE_SPEC = "modes_dnd"
 
         /** Inject DndTile or ModesTile into tileMap in QSModule based on feature flag */
         @Provides
@@ -146,6 +152,12 @@
             return if (ModesUi.isEnabled) modesTile.get() else dndTile.get()
         }
 
+        /** Inject ModesDndTile into tileViewModelMap in QSModule */
+        @Provides
+        @IntoMap
+        @StringKey(MODES_DND_TILE_SPEC)
+        fun bindDndModeTile(tile: ModesDndTile): QSTileImpl<*> = tile
+
         /** Inject flashlight config */
         @Provides
         @IntoMap
@@ -449,6 +461,37 @@
                     mapper,
                 )
             else StubQSTileViewModel
+
+        @Provides
+        @IntoMap
+        @StringKey(MODES_DND_TILE_SPEC)
+        fun provideDndModeTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+            QSTileConfig(
+                tileSpec = TileSpec.create(MODES_DND_TILE_SPEC),
+                uiConfig =
+                    QSTileUIConfig.Resource(
+                        iconRes = R.drawable.qs_dnd_icon_off,
+                        labelRes = R.string.quick_settings_dnd_label,
+                    ),
+                instanceId = uiEventLogger.getNewInstanceId(),
+                category = TileCategory.CONNECTIVITY,
+            )
+
+        @Provides
+        @IntoMap
+        @StringKey(MODES_DND_TILE_SPEC)
+        fun provideDndModeTileViewModel(
+            factory: QSTileViewModelFactory.Static<ModesDndTileModel>,
+            mapper: ModesDndTileMapper,
+            stateInteractor: ModesDndTileDataInteractor,
+            userActionInteractor: ModesDndTileUserActionInteractor,
+        ): QSTileViewModel =
+            factory.create(
+                TileSpec.create(MODES_DND_TILE_SPEC),
+                userActionInteractor,
+                stateInteractor,
+                mapper,
+            )
     }
 
     /** Inject FlashlightTile into tileMap in QSModule */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 2b0bf1a..6509a69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -79,6 +79,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
+import com.android.systemui.Flags;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -245,7 +246,9 @@
         mProgressBar.setProgressTintList(accentColor);
         mProgressBar.setIndeterminateTintList(accentColor);
         mProgressBar.setSecondaryProgressTintList(accentColor);
-        setBackgroundColor(backgroundColor);
+        if (!Flags.notificationRowTransparency()) {
+            setBackgroundColor(backgroundColor);
+        }
     }
 
     @Override
@@ -419,10 +422,10 @@
         // case to prevent flicker.
         if (!mRemoved) {
             ViewGroup parent = (ViewGroup) getParent();
+            View actionsContainer = getActionsContainerLayout();
             if (animate && parent != null) {
 
                 ViewGroup grandParent = (ViewGroup) parent.getParent();
-                View actionsContainer = getActionsContainerLayout();
                 int actionsContainerHeight =
                         actionsContainer != null ? actionsContainer.getHeight() : 0;
 
@@ -459,8 +462,12 @@
                 if (mWrapper != null) {
                     mWrapper.setRemoteInputVisible(false);
                 }
+                if (Flags.notificationRowTransparency()) {
+                    if (actionsContainer != null) actionsContainer.setAlpha(1);
+                }
             }
         }
+        unregisterBackCallback();
 
         if (logClose) {
             mUiEventLogger.logWithInstanceId(
@@ -558,11 +565,6 @@
 
     @Override
     public void onVisibilityAggregated(boolean isVisible) {
-        if (isVisible) {
-            registerBackCallback();
-        } else {
-            unregisterBackCallback();
-        }
         super.onVisibilityAggregated(isVisible);
         mEditText.setEnabled(isVisible && !mSending);
     }
@@ -623,6 +625,7 @@
         setAttachment(mEntry.remoteInputAttachment);
 
         updateSendButton();
+        registerBackCallback();
     }
 
     public void onNotificationUpdateOrReset() {
@@ -826,12 +829,14 @@
                     ObjectAnimator.ofFloat(fadeOutView, View.ALPHA, 1f, 0f);
             fadeOutViewAlphaAnimator.setDuration(FOCUS_ANIMATION_CROSSFADE_DURATION);
             fadeOutViewAlphaAnimator.setInterpolator(InterpolatorsAndroidX.LINEAR);
-            animatorSet.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation, boolean isReverse) {
-                    fadeOutView.setAlpha(1f);
-                }
-            });
+            if (!Flags.notificationRowTransparency()) {
+                animatorSet.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation, boolean isReverse) {
+                        fadeOutView.setAlpha(1f);
+                    }
+                });
+            }
             animatorSet.playTogether(alphaAnimator, scaleAnimator, fadeOutViewAlphaAnimator);
         }
         return animatorSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
index d3af1e5..6d959be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.policy
 
 import android.app.ActivityOptions
+import android.app.Flags.notificationsRedesignTemplates
 import android.app.Notification
 import android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY
 import android.app.PendingIntent
@@ -53,7 +54,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
 import com.android.systemui.statusbar.notification.logging.NotificationLogger
-import com.android.systemui.statusbar.notification.row.MagicActionBackgroundDrawable
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState.SuppressedActions
 import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions
@@ -397,16 +397,21 @@
         delayOnClickListener: Boolean,
         packageContext: Context,
     ): Button {
-        val isMagicAction = Flags.notificationMagicActionsTreatment() &&
+        val isMagicAction =
+            Flags.notificationMagicActionsTreatment() &&
                 smartActions.fromAssistant &&
                 action.extras.getBoolean(Notification.Action.EXTRA_IS_MAGIC, false)
-        val layoutRes = if (isMagicAction) {
-            R.layout.magic_action_button
-        } else {
-            R.layout.smart_action_button
-        }
-        return (LayoutInflater.from(parent.context).inflate(layoutRes, parent, false)
-                as Button)
+        val layoutRes =
+            if (isMagicAction) {
+                R.layout.magic_action_button
+            } else {
+                if (notificationsRedesignTemplates()) {
+                    R.layout.notification_2025_smart_action_button
+                } else {
+                    R.layout.smart_action_button
+                }
+            }
+        return (LayoutInflater.from(parent.context).inflate(layoutRes, parent, false) as Button)
             .apply {
                 text = action.title
 
@@ -435,7 +440,6 @@
                 // Mark this as an Action button
                 (layoutParams as SmartReplyView.LayoutParams).mButtonType = SmartButtonType.ACTION
             }
-
     }
 
     private fun onSmartActionClick(
@@ -499,9 +503,11 @@
         replyIndex: Int,
         choice: CharSequence,
         delayOnClickListener: Boolean,
-    ): Button =
-        (LayoutInflater.from(parent.context).inflate(R.layout.smart_reply_button, parent, false)
-                as Button)
+    ): Button {
+        val layoutRes =
+            if (notificationsRedesignTemplates()) R.layout.notification_2025_smart_reply_button
+            else R.layout.smart_reply_button
+        return (LayoutInflater.from(parent.context).inflate(layoutRes, parent, false) as Button)
             .apply {
                 text = choice
                 val onClickListener =
@@ -531,6 +537,7 @@
                 // Mark this as a Reply button
                 (layoutParams as SmartReplyView.LayoutParams).mButtonType = SmartButtonType.REPLY
             }
+    }
 
     private fun onSmartReplyClick(
         entry: NotificationEntry,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt
index 07bbca7..2b9d39a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt
@@ -15,7 +15,7 @@
  */
 package com.android.systemui.statusbar.policy.data.repository
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import dagger.Binds
 import dagger.Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
index e8347df..ed814c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -58,7 +58,7 @@
  * An interactor that performs business logic related to the status and configuration of Zen Mode
  * (or Do Not Disturb/DND Mode).
  */
- @SysUISingleton
+@SysUISingleton
 class ZenModeInteractor
 @Inject
 constructor(
@@ -141,6 +141,18 @@
             return field
         }
 
+    /**
+     * Returns the current state of the special "manual DND" mode.
+     *
+     * This should only be used when there is a strong reason to handle DND specifically (such as
+     * legacy UI pieces that haven't been updated to use modes more generally, or if the user
+     * explicitly wants a shortcut to DND). Please prefer using [modes] or [activeModes] in all
+     * other scenarios.
+     */
+    fun getDndMode(): ZenMode {
+        return zenModeRepository.getModes().single { it.isManualDnd }
+    }
+
     /** Flow returning the currently active mode(s), if any. */
     val activeModes: Flow<ActiveZenModes> =
         modes
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 2dc17f4..c86e00d 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
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.ui.viewmodel
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/data/repository/TelephonyRepository.kt b/packages/SystemUI/src/com/android/systemui/telephony/data/repository/TelephonyRepository.kt
index b1b6014..9b88d43 100644
--- a/packages/SystemUI/src/com/android/systemui/telephony/data/repository/TelephonyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/telephony/data/repository/TelephonyRepository.kt
@@ -23,7 +23,7 @@
 import android.telecom.TelecomManager
 import android.telephony.Annotation
 import android.telephony.TelephonyCallback
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 9f60fe21..bd3fead 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -28,6 +28,7 @@
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_DYNAMIC_COLOR;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_BOTH;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_INDEX;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE;
@@ -106,6 +107,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.stream.Collectors;
@@ -507,18 +509,52 @@
         mUserTracker.addCallback(mUserTrackerCallback, mMainExecutor);
         mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
 
+
+        // Condition only applies when booting to Setup Wizard.
+        // We should check if the device has specific hardware color styles, load the relative
+        // color palette and more also save the setting in our shared setting with ThemePicker.
         WallpaperColors systemColor;
         if (hardwareColorStyles() && !mDeviceProvisionedController.isCurrentUserSetup()) {
-            Pair<Integer, Color> defaultSettings = getThemeSettingsDefaults();
-            mThemeStyle = defaultSettings.first;
-            Color seedColor = defaultSettings.second;
+            HardwareDefaultSetting defaultSettings = getThemeSettingsDefaults();
+            mThemeStyle = defaultSettings.style;
 
             // we only use the first color anyway, so we can pass only the single color we have
             systemColor = new WallpaperColors(
-                    /*primaryColor*/ seedColor,
-                    /*secondaryColor*/ seedColor,
-                    /*tertiaryColor*/ seedColor
+                    /*primaryColor*/ defaultSettings.seedColor,
+                    /*secondaryColor*/ defaultSettings.seedColor,
+                    /*tertiaryColor*/ defaultSettings.seedColor
             );
+
+            /* We update the json in THEME_CUSTOMIZATION_OVERLAY_PACKAGES to reflect the preset. */
+            final int currentUser = mUserTracker.getUserId();
+            final String overlayPackageJson = Objects.requireNonNullElse(
+                    mSecureSettings.getStringForUser(
+                            Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+                            currentUser),
+                    "{}"
+            );
+
+            try {
+                JSONObject object = new JSONObject(overlayPackageJson);
+                String seedColorStr = Integer.toHexString(defaultSettings.seedColor.toArgb());
+                object.put(OVERLAY_CATEGORY_SYSTEM_PALETTE, seedColorStr);
+                object.put(OVERLAY_CATEGORY_ACCENT_COLOR, seedColorStr);
+                object.put(OVERLAY_COLOR_SOURCE, defaultSettings.colorSource);
+                object.put(OVERLAY_CATEGORY_THEME_STYLE, Style.toString(mThemeStyle));
+
+                mSecureSettings.putStringForUser(
+                        Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, object.toString(),
+                        UserHandle.USER_CURRENT);
+
+                Log.d(TAG, "Hardware Color Defaults loaded: " + object.toString());
+
+            } catch (JSONException e) {
+                Log.d(TAG, "Failed to store hardware color defaults in "
+                    + "THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
+            }
+
+            // now we have to update
+
         } else {
             systemColor = mWallpaperManager.getWallpaperColors(
                     getDefaultWallpaperColorsSource(mUserTracker.getUserId()));
@@ -863,7 +899,7 @@
             try {
                 JSONObject object = new JSONObject(overlayPackageJson);
                 style = Style.valueOf(
-                        object.getString(ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE));
+                        object.getString(OVERLAY_CATEGORY_THEME_STYLE));
                 if (!validStyles.contains(style)) {
                     style = Style.TONAL_SPOT;
                 }
@@ -899,26 +935,29 @@
         }
 
         String deviceColorPropertyValue = mSystemPropertiesHelper.get(deviceColorProperty);
-        Pair<Integer, String> selectedTheme = themeMap.get(deviceColorPropertyValue);
-        if (selectedTheme == null) {
+        Pair<Integer, String> styleAndSource = themeMap.get(deviceColorPropertyValue);
+        if (styleAndSource == null) {
             Log.d(TAG, "Sysprop `" + deviceColorProperty + "` of value '" + deviceColorPropertyValue
                     + "' not found in theming_defaults: " + Arrays.toString(themeData));
-            selectedTheme = fallbackTheme;
+            styleAndSource = fallbackTheme;
         }
 
-        return selectedTheme;
+        return styleAndSource;
+    }
+
+    record HardwareDefaultSetting(Color seedColor, @Style.Type int style, String colorSource) {
     }
 
     @VisibleForTesting
-    protected Pair<Integer, Color> getThemeSettingsDefaults() {
+    protected HardwareDefaultSetting getThemeSettingsDefaults() {
 
-        Pair<Integer, String> selectedTheme = getHardwareColorSetting();
+        Pair<Integer, String> styleAndSource = getHardwareColorSetting();
 
         // Last fallback color
         Color defaultSeedColor = Color.valueOf(GOOGLE_BLUE);
 
         // defaultColor will come from wallpaper or be parsed from a string
-        boolean isWallpaper = selectedTheme.second.equals(COLOR_SOURCE_HOME);
+        boolean isWallpaper = styleAndSource.second.equals(COLOR_SOURCE_HOME);
 
         if (isWallpaper) {
             WallpaperColors wallpaperColors = mWallpaperManager.getWallpaperColors(
@@ -932,22 +971,24 @@
                     defaultSeedColor.toArgb()));
         } else {
             try {
-                defaultSeedColor = Color.valueOf(Color.parseColor(selectedTheme.second));
+                defaultSeedColor = Color.valueOf(Color.parseColor(styleAndSource.second));
                 Log.d(TAG, "Default seed color read from resource: " + Integer.toHexString(
                         defaultSeedColor.toArgb()));
             } catch (IllegalArgumentException e) {
-                Log.e(TAG, "Error parsing color: " + selectedTheme.second, e);
+                Log.e(TAG, "Error parsing color: " + styleAndSource.second, e);
                 // defaultSeedColor remains unchanged in this case
             }
         }
 
-        return new Pair<>(selectedTheme.first, defaultSeedColor);
+        return new HardwareDefaultSetting(defaultSeedColor, styleAndSource.first,
+                isWallpaper ? COLOR_SOURCE_HOME : COLOR_SOURCE_PRESET);
     }
 
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("mSystemColors=" + mCurrentColors);
         pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor));
+        pw.println("mContrast=" + mContrast);
         pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
         pw.println("mNeutralOverlay=" + mNeutralOverlay);
         pw.println("mDynamicOverlay=" + mDynamicOverlay);
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
new file mode 100644
index 0000000..c11e4c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects;
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.view.Gravity
+import android.view.WindowInsets
+import android.view.WindowManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.topwindoweffects.domain.interactor.SqueezeEffectInteractor
+import com.android.systemui.topwindoweffects.ui.compose.EffectsWindowRoot
+import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@SysUISingleton
+class TopLevelWindowEffects @Inject constructor(
+    @Application private val context: Context,
+    @Application private val applicationScope: CoroutineScope,
+    private val windowManager: ViewCaptureAwareWindowManager,
+    private val squeezeEffectInteractor: SqueezeEffectInteractor,
+    private val keyEventInteractor: KeyEventInteractor,
+    private val viewModelFactory: SqueezeEffectViewModel.Factory
+) : CoreStartable {
+
+    override fun start() {
+        applicationScope.launch {
+            var root: EffectsWindowRoot? = null
+            squeezeEffectInteractor.isSqueezeEffectEnabled.collectLatest { enabled ->
+                // TODO: move window ops to a separate UI thread
+                if (enabled) {
+                    keyEventInteractor.isPowerButtonDown.collectLatest { down ->
+                        // TODO: ignore new window creation when ignoring short power press duration
+                        if (down && root == null) {
+                            root = EffectsWindowRoot(
+                                context = context,
+                                viewModelFactory = viewModelFactory,
+                                onEffectFinished = {
+                                    if (root?.isAttachedToWindow == true) {
+                                        windowManager.removeView(root)
+                                        root = null
+                                    }
+                                }
+                            )
+                            root?.let {
+                                windowManager.addView(it, getWindowManagerLayoutParams())
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private fun getWindowManagerLayoutParams(): WindowManager.LayoutParams {
+        val lp = WindowManager.LayoutParams(
+            WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
+            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                    or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+            PixelFormat.TRANSPARENT
+        )
+
+        lp.privateFlags = lp.privateFlags or
+                (WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+                        or WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+                        or WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED
+                        or WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
+                        or WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+
+        lp.layoutInDisplayCutoutMode =
+            WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+
+        lp.title = "TopLevelWindowEffects"
+        lp.fitInsetsTypes = WindowInsets.Type.systemOverlays()
+        lp.gravity = Gravity.TOP
+
+        return lp
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/dagger/SqueezeEffectRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/dagger/SqueezeEffectRepositoryModule.kt
new file mode 100644
index 0000000..5a2af92
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/dagger/SqueezeEffectRepositoryModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects.dagger
+
+import com.android.systemui.topwindoweffects.data.repository.SqueezeEffectRepository
+import com.android.systemui.topwindoweffects.data.repository.SqueezeEffectRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface SqueezeEffectRepositoryModule {
+
+    @Binds
+    fun squeezeEffectRepository(
+        squeezeEffectRepositoryImpl: SqueezeEffectRepositoryImpl
+    ) : SqueezeEffectRepository
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/dagger/TopLevelWindowEffectsModule.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/dagger/TopLevelWindowEffectsModule.kt
new file mode 100644
index 0000000..6fbfedc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/dagger/TopLevelWindowEffectsModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects.dagger
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.topwindoweffects.TopLevelWindowEffects
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+interface TopLevelWindowEffectsModule {
+
+    @Binds
+    @IntoMap
+    @ClassKey(TopLevelWindowEffects::class)
+    fun bindTopLevelWindowEffectsCoreStartable(impl: TopLevelWindowEffects): CoreStartable
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepository.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt
copy to packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepository.kt
index 260729b..9e0b256 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepository.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,6 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.topwindoweffects.data.repository
 
-@JvmInline value class IssueRecordingModel(val isRecording: Boolean)
+import kotlinx.coroutines.flow.Flow
+
+interface SqueezeEffectRepository {
+    val isSqueezeEffectEnabled: Flow<Boolean>
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryImpl.kt
new file mode 100644
index 0000000..9e0feed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryImpl.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects.data.repository
+
+import android.database.ContentObserver
+import android.os.Handler
+import android.provider.Settings.Global.POWER_BUTTON_LONG_PRESS
+import com.android.internal.R
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.Flags
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+
+@SysUISingleton
+class SqueezeEffectRepositoryImpl @Inject constructor(
+    @Background private val bgHandler: Handler?,
+    @Background private val bgCoroutineContext: CoroutineContext,
+    private val globalSettings: GlobalSettings
+) : SqueezeEffectRepository {
+
+    override val isSqueezeEffectEnabled: Flow<Boolean> = conflatedCallbackFlow {
+        val observer = object : ContentObserver(bgHandler) {
+            override fun onChange(selfChange: Boolean) {
+                trySendWithFailureLogging(squeezeEffectEnabled, TAG,
+                    "updated isSqueezeEffectEnabled")
+            }
+        }
+        trySendWithFailureLogging(squeezeEffectEnabled, TAG, "init isSqueezeEffectEnabled")
+        globalSettings.registerContentObserverAsync(POWER_BUTTON_LONG_PRESS, observer)
+        awaitClose { globalSettings.unregisterContentObserverAsync(observer) }
+    }.flowOn(bgCoroutineContext)
+
+    private val squeezeEffectEnabled
+        get() = Flags.enableLppSqueezeEffect() && globalSettings.getInt(
+            POWER_BUTTON_LONG_PRESS, R.integer.config_longPressOnPowerBehavior
+        ) == 5 // 5 corresponds to launch assistant in config_longPressOnPowerBehavior
+
+    companion object {
+        private const val TAG = "SqueezeEffectRepository"
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/domain/interactor/SqueezeEffectInteractor.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/domain/interactor/SqueezeEffectInteractor.kt
new file mode 100644
index 0000000..879fde7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/domain/interactor/SqueezeEffectInteractor.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.topwindoweffects.data.repository.SqueezeEffectRepository
+import javax.inject.Inject
+
+@SysUISingleton
+class SqueezeEffectInteractor @Inject constructor(
+    squeezeEffectRepository: SqueezeEffectRepository
+) {
+    val isSqueezeEffectEnabled = squeezeEffectRepository.isSqueezeEffectEnabled
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/EffectsWindowRoot.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/EffectsWindowRoot.kt
new file mode 100644
index 0000000..61448f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/EffectsWindowRoot.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects.ui.compose
+
+import android.annotation.SuppressLint
+import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.AbstractComposeView
+import com.android.systemui.compose.ComposeInitializer
+import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
+
+@SuppressLint("ViewConstructor")
+class EffectsWindowRoot(
+    context: Context,
+    private val onEffectFinished: () -> Unit,
+    private val viewModelFactory: SqueezeEffectViewModel.Factory
+) : AbstractComposeView(context) {
+
+    override fun onAttachedToWindow() {
+        ComposeInitializer.onAttachedToWindow(this)
+        super.onAttachedToWindow()
+    }
+
+    override fun onDetachedFromWindow() {
+        super.onDetachedFromWindow()
+        ComposeInitializer.onDetachedFromWindow(this)
+    }
+
+    @Composable
+    override fun Content() {
+        SqueezeEffect(
+            viewModelFactory = viewModelFactory,
+            onEffectFinished = onEffectFinished
+        )
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/SqueezeEffect.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/SqueezeEffect.kt
new file mode 100644
index 0000000..9809b05
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/compose/SqueezeEffect.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects.ui.compose
+
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Matrix
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.withTransform
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.VectorPainter
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.unit.dp
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.res.R
+import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
+
+private val SqueezeEffectMaxThickness = 12.dp
+private val SqueezeColor = Color.Black
+
+@Composable
+fun SqueezeEffect(
+    viewModelFactory: SqueezeEffectViewModel.Factory,
+    onEffectFinished: () -> Unit,
+    modifier: Modifier = Modifier
+) {
+    val viewModel = rememberViewModel(traceName = "SqueezeEffect") { viewModelFactory.create() }
+    val down = viewModel.isPowerButtonPressed
+    val longPressed = viewModel.isPowerButtonLongPressed
+    // TODO: Choose the correct resource based on primary / secondary display
+    val top = rememberVectorPainter(ImageVector.vectorResource(R.drawable.rounded_corner_top))
+    val bottom = rememberVectorPainter(ImageVector.vectorResource(R.drawable.rounded_corner_bottom))
+
+    val squeezeProgress by animateFloatAsState(
+        targetValue =
+            if (down && !longPressed) {
+                1f
+            } else {
+                0f
+            },
+        animationSpec = tween(durationMillis = 400),
+        finishedListener = { onEffectFinished() }
+    )
+
+    Canvas(modifier = modifier.fillMaxSize()) {
+        if (squeezeProgress <= 0) {
+            return@Canvas
+        }
+
+        val squeezeThickness = SqueezeEffectMaxThickness.toPx() * squeezeProgress
+
+        drawRect(color = SqueezeColor, size = Size(size.width, squeezeThickness))
+
+        drawRect(
+            color = SqueezeColor,
+            topLeft = Offset(0f, size.height - squeezeThickness),
+            size = Size(size.width, squeezeThickness)
+        )
+
+        drawRect(color = SqueezeColor, size = Size(squeezeThickness, size.height))
+
+        drawRect(
+            color = SqueezeColor,
+            topLeft = Offset(size.width - squeezeThickness, 0f),
+            size = Size(squeezeThickness, size.height)
+        )
+
+        drawTransform(
+            dx = squeezeThickness,
+            dy = squeezeThickness,
+            rotation = 0f,
+            corner = top
+        )
+
+        drawTransform(
+            dx = size.width - squeezeThickness,
+            dy = squeezeThickness,
+            rotation = 90f,
+            corner = top
+        )
+
+        drawTransform(
+            dx = squeezeThickness,
+            dy = size.height - squeezeThickness,
+            rotation = 270f,
+            corner = bottom
+        )
+
+        drawTransform(
+            dx = size.width - squeezeThickness,
+            dy = size.height - squeezeThickness,
+            rotation = 180f,
+            corner = bottom
+        )
+    }
+}
+
+private fun DrawScope.drawTransform(
+    dx: Float,
+    dy: Float,
+    rotation: Float = 0f,
+    corner: VectorPainter,
+) {
+    withTransform(transformBlock = {
+        transform(matrix = Matrix().apply {
+            translate(dx, dy)
+            if (rotation != 0f) {
+                rotateZ(rotation)
+            }
+        })
+    }) {
+        with(corner) {
+            draw(size = intrinsicSize)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/viewmodel/SqueezeEffectViewModel.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/viewmodel/SqueezeEffectViewModel.kt
new file mode 100644
index 0000000..1cab327
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/ui/viewmodel/SqueezeEffectViewModel.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.topwindoweffects.ui.viewmodel
+
+import androidx.compose.runtime.getValue
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+class SqueezeEffectViewModel
+@AssistedInject
+constructor(
+    keyEventInteractor: KeyEventInteractor
+) : ExclusiveActivatable() {
+    private val hydrator = Hydrator("SqueezeEffectViewModel.hydrator")
+
+    val isPowerButtonPressed: Boolean by hydrator.hydratedStateOf(
+        traceName = "isPowerButtonPressed",
+        initialValue = false,
+        source = keyEventInteractor.isPowerButtonDown
+    )
+
+    val isPowerButtonLongPressed: Boolean by hydrator.hydratedStateOf(
+        traceName = "isPowerButtonLongPressed",
+        initialValue = false,
+        source = keyEventInteractor.isPowerButtonLongPressed
+    )
+
+    override suspend fun onActivated(): Nothing {
+        hydrator.activate()
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): SqueezeEffectViewModel
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index eecea92..2f8da2e 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -84,7 +84,7 @@
                 ),
     ) {
         val isCompactWindow = hasCompactWindowSize()
-        val padding = if (isCompactWindow) 24.dp else 60.dp
+        val padding = if (isCompactWindow) 24.dp else 48.dp
         val configuration = LocalConfiguration.current
         when (configuration.orientation) {
             Configuration.ORIENTATION_LANDSCAPE -> {
@@ -121,7 +121,7 @@
         // because other composables have weight 1, Done button will be positioned first
         DoneButton(
             onDoneButtonClicked = onDoneButtonClicked,
-            modifier = Modifier.padding(horizontal = padding),
+            modifier = Modifier.padding(start = padding, top = 0.dp, end = padding, bottom = 32.dp),
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
index 47e27bc..1cc7a31 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
@@ -50,6 +50,7 @@
                 onScreenTurningOnToOnDrawnMs,
                 onDrawnToOnScreenTurnedOnMs,
                 trackingResult,
+                screenWakelockstatus
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index 66de522..5800d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -344,6 +344,8 @@
         val onDrawnToOnScreenTurnedOnMs: Int = VALUE_UNKNOWN,
         val trackingResult: Int =
             SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TRACKING_RESULT__UNKNOWN_RESULT,
+        val screenWakelockstatus: Int =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_UNKNOWN,
     )
 
     enum class TrackingResult {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
index fbbd2b9..e47d74e 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
@@ -16,7 +16,7 @@
 package com.android.systemui.unfold.data.repository
 
 import androidx.annotation.FloatRange
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
 import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionInProgress
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index c960b55..b33eafc 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -33,7 +33,7 @@
 import com.android.internal.statusbar.IStatusBarService
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -414,16 +414,15 @@
         }
     }
 
+    private suspend fun SelectedUserModel.isEligibleForLogout(): Boolean {
+        return withContext(backgroundDispatcher) {
+            selectionStatus == SelectionStatus.SELECTION_COMPLETE &&
+                devicePolicyManager.logoutUser != null
+        }
+    }
+
     companion object {
         private const val TAG = "UserRepository"
         @VisibleForTesting const val SETTING_SIMPLE_USER_SWITCHER = "lockscreenSimpleUserSwitcher"
     }
 }
-
-fun SelectedUserModel.isEligibleForLogout(): Boolean {
-    // TODO(b/206032495): should call mDevicePolicyManager.getLogoutUserId() instead of
-    // hardcode it to USER_SYSTEM so it properly supports headless system user mode
-    // (and then call mDevicePolicyManager.clearLogoutUser() after switched)
-    return selectionStatus == SelectionStatus.SELECTION_COMPLETE &&
-        userInfo.id != android.os.UserHandle.USER_SYSTEM
-}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
index bcbd679..412161c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
@@ -23,7 +23,7 @@
 import android.provider.Settings.Global.USER_SWITCHER_ENABLED
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt
index 3bd8af6..6657c42 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
@@ -31,7 +32,14 @@
 constructor(
     @Background val backgroundDispatcher: CoroutineDispatcher,
     val userRepository: UserRepository,
+    val selectedUserInteractor: SelectedUserInteractor,
 ) {
+    /** Whether the current user is unlocked */
+    val currentUserUnlocked: Flow<Boolean> =
+        selectedUserInteractor.selectedUserInfo.flatMapLatestConflated { user ->
+            isUserUnlocked(user.userHandle)
+        }
+
     fun isUserUnlocked(userHandle: UserHandle?): Flow<Boolean> =
         userRepository.isUserUnlocked(userHandle).flowOn(backgroundDispatcher)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt b/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt
index 31a8d86..9937eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt
@@ -19,7 +19,7 @@
 import android.database.ContentObserver
 import android.os.Handler
 import android.provider.Settings
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt
index 80ccd64..d4eabb9 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/BatteryControllerExt.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.util.kotlin
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.statusbar.policy.BatteryController
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index 735da46..cc4307a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -19,8 +19,11 @@
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.util.time.SystemClock
 import com.android.systemui.util.time.SystemClockImpl
+import java.util.LinkedList
 import java.util.concurrent.atomic.AtomicReference
 import kotlin.math.max
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
@@ -364,3 +367,58 @@
  */
 @Suppress("NOTHING_TO_INLINE")
 inline fun Flow<Unit>.emitOnStart(): Flow<Unit> = onStart { emit(Unit) }
+
+/**
+ * Transforms a Flow<T> into a Flow<List<T>> by implementing a sliding window algorithm.
+ *
+ * This function creates a sliding window over the input Flow<T>. The window has a specified
+ * [windowDuration] and slides continuously as time progresses. The emitted List<T> contains all
+ * items from the input flow that fall within the current window.
+ *
+ * The window slides forward by the smallest possible increment to include or exclude *one* event
+ * based on the time the event was emitted (determined by the System.currentTimeMillis()). This
+ * means that consecutive emitted lists will have overlapping elements if the elements fall within
+ * the [windowDuration]
+ *
+ * @param windowDuration The duration of the sliding window.
+ * @return A Flow that emits Lists of elements within the current sliding window.
+ */
+fun <T> Flow<T>.slidingWindow(
+    windowDuration: Duration,
+    clock: SystemClock = SystemClockImpl(),
+): Flow<List<T>> = channelFlow {
+    require(windowDuration.isPositive()) { "Window duration must be positive" }
+    val buffer = LinkedList<Pair<Duration, T>>()
+
+    coroutineScope {
+        var windowAdvancementJob: Job? = null
+
+        collect { value ->
+            windowAdvancementJob?.cancel()
+            val now = clock.currentTimeMillis().milliseconds
+            buffer.addLast(now to value)
+
+            while (buffer.isNotEmpty() && buffer.first.first + windowDuration <= now) {
+                buffer.removeFirst()
+            }
+            send(buffer.map { it.second })
+
+            // Keep the window advancing through time even if the source flow isn't emitting
+            // anymore. We stop advancing the window as soon as there are no items left in the
+            // buffer.
+            windowAdvancementJob = launch {
+                while (buffer.isNotEmpty()) {
+                    val startOfWindow = clock.currentTimeMillis().milliseconds - windowDuration
+                    // Invariant: At this point, everything in the buffer is guaranteed to be in
+                    // the window, as we removed expired items above.
+                    val timeUntilNextOldest =
+                        (buffer.first.first - startOfWindow).coerceAtLeast(0.milliseconds)
+                    delay(timeUntilNextOldest)
+                    // Remove the oldest item, as it has now fallen out of the window.
+                    buffer.removeFirst()
+                    send(buffer.map { it.second })
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/ManagedProfileControllerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/ManagedProfileControllerExt.kt
index 7a2f9b2..837bbea 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/ManagedProfileControllerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/ManagedProfileControllerExt.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.util.kotlin
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.statusbar.phone.ManagedProfileController
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt
index ee00e8b..02012ed 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/ReduceBrightColorsControllerExt.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.util.kotlin
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.qs.ReduceBrightColorsController
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt
index 22cc8dd..a914c86 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/RotationLockControllerExt.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.util.kotlin
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.statusbar.policy.RotationLockController
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/repository/SettingsForUserRepository.kt b/packages/SystemUI/src/com/android/systemui/util/settings/repository/SettingsForUserRepository.kt
index 94b3fd2..6cdc942 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/repository/SettingsForUserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/repository/SettingsForUserRepository.kt
@@ -47,6 +47,11 @@
             .distinctUntilChanged()
             .flowOn(backgroundDispatcher)
 
+    fun intSettingForUser(userId: Int, name: String, defaultValue: Int = 0): Flow<Int> =
+        settingObserver(name, userId) { userSettings.getIntForUser(name, defaultValue, userId) }
+            .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
+
     fun <T> settingObserver(name: String, userId: Int, settingsReader: () -> T): Flow<T> {
         return userSettings
             .observerFlow(userId, name)
@@ -63,4 +68,14 @@
             userSettings.getBoolForUser(name, defaultValue, userId)
         }
     }
+
+    suspend fun setIntForUser(userId: Int, name: String, value: Int) {
+        withContext(backgroundContext) { userSettings.putIntForUser(name, value, userId) }
+    }
+
+    suspend fun getIntForUser(userId: Int, name: String, defaultValue: Int = 0): Int {
+        return withContext(backgroundContext) {
+            userSettings.getIntForUser(name, defaultValue, userId)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index 1f11f2d..f6aa189 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -40,7 +40,6 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
@@ -78,11 +77,16 @@
 
     private val userVolumeUpdates = MutableStateFlow<VolumeUpdate?>(null)
     private val model: Flow<VolumeDialogStreamModel> =
-        interactor.slider
-            .filter {
-                val currentVolumeUpdate = userVolumeUpdates.value ?: return@filter true
+        combine(interactor.slider, userVolumeUpdates) { model, currentVolumeUpdate ->
+                currentVolumeUpdate ?: return@combine model
                 val lastVolumeUpdateTime = currentVolumeUpdate.timestampMillis
-                getTimestampMillis() - lastVolumeUpdateTime > VOLUME_UPDATE_GRACE_PERIOD
+                val shouldIgnoreUpdates =
+                    getTimestampMillis() - lastVolumeUpdateTime < VOLUME_UPDATE_GRACE_PERIOD
+                if (shouldIgnoreUpdates) {
+                    model.copy(level = currentVolumeUpdate.newVolumeLevel)
+                } else {
+                    model
+                }
             }
             .stateIn(coroutineScope, SharingStarted.Eagerly, null)
             .filterNotNull()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt
index 720d550..f6582a0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt
@@ -72,10 +72,10 @@
     valueRange: ClosedFloatingPointRange<Float>,
     onValueChanged: (Float) -> Unit,
     onValueChangeFinished: ((Float) -> Unit)?,
-    stepDistance: Float,
     isEnabled: Boolean,
     accessibilityParams: AccessibilityParams,
     modifier: Modifier = Modifier,
+    stepDistance: Float = 0f,
     colors: SliderColors = SliderDefaults.colors(),
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
     haptics: Haptics = Haptics.Disabled,
@@ -83,7 +83,7 @@
     isReverseDirection: Boolean = false,
     track: (@Composable (SliderState) -> Unit)? = null,
 ) {
-    require(stepDistance > 0) { "stepDistance must be positive" }
+    require(stepDistance >= 0) { "stepDistance must not be negative" }
     val coroutineScope = rememberCoroutineScope()
     val snappedValue = snapValue(value, valueRange, stepDistance)
     val hapticsViewModel = haptics.createViewModel(snappedValue, valueRange, interactionSource)
@@ -192,16 +192,20 @@
         setProgress { targetValue ->
             val targetDirection =
                 when {
-                    targetValue > value -> 1
-                    targetValue < value -> -1
-                    else -> 0
+                    targetValue > value -> 1f
+                    targetValue < value -> -1f
+                    else -> 0f
+                }
+            val offset =
+                if (stepDistance > 0) {
+                    // advance to the next step when stepDistance is > 0
+                    targetDirection * stepDistance
+                } else {
+                    // advance to the desired value otherwise
+                    targetValue - value
                 }
 
-            val newValue =
-                (value + targetDirection * stepDistance).coerceIn(
-                    valueRange.start,
-                    valueRange.endInclusive,
-                )
+            val newValue = (value + offset).coerceIn(valueRange.start, valueRange.endInclusive)
             onValueChanged(newValue)
             true
         }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
index 594c552..7e9ebd2 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
@@ -24,7 +24,7 @@
 import android.service.quickaccesswallet.WalletCard
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.flags.FeatureFlags
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
index dd1c11d..e9180df 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
@@ -28,9 +28,9 @@
 import com.android.systemui.qs.shared.model.TileCategory;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.qs.tiles.QuickAccessWalletTile;
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig;
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy;
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig;
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig;
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy;
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig;
 import com.android.systemui.res.R;
 import com.android.systemui.wallet.controller.WalletContextualLocationsService;
 import com.android.systemui.wallet.ui.WalletActivity;
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
index 300a7e0..72bc9f6 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -36,6 +36,8 @@
 @SysUISingleton
 class NoopWallpaperRepository @Inject constructor() : WallpaperRepository {
     override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow()
+    override val lockscreenWallpaperInfo: StateFlow<WallpaperInfo?> =
+        MutableStateFlow(null).asStateFlow()
     override val wallpaperSupportsAmbientMode = flowOf(false)
     override var rootView: View? = null
     override val shouldSendFocalArea: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index b07342c..d314f76 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -18,6 +18,8 @@
 
 import android.app.WallpaperInfo
 import android.app.WallpaperManager
+import android.app.WallpaperManager.FLAG_LOCK
+import android.app.WallpaperManager.FLAG_SYSTEM
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
@@ -30,9 +32,11 @@
 import android.view.View
 import com.android.internal.R
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.res.R as SysUIR
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.shared.Flags.ambientAod
 import com.android.systemui.shared.Flags.extendedWallpaperEffects
 import com.android.systemui.user.data.model.SelectedUserModel
@@ -63,6 +67,12 @@
     /** Emits the current user's current wallpaper. */
     val wallpaperInfo: StateFlow<WallpaperInfo?>
 
+    /**
+     * Emits the current user's lockscreen wallpaper. This will emit the same value as
+     * [wallpaperInfo] if the wallpaper is shared between home and lock screen.
+     */
+    val lockscreenWallpaperInfo: StateFlow<WallpaperInfo?>
+
     /** Emits true if the current user's current wallpaper supports ambient mode. */
     val wallpaperSupportsAmbientMode: Flow<Boolean>
 
@@ -81,13 +91,14 @@
 class WallpaperRepositoryImpl
 @Inject
 constructor(
-    @Background scope: CoroutineScope,
+    @Background private val scope: CoroutineScope,
     @Background private val bgDispatcher: CoroutineDispatcher,
     broadcastDispatcher: BroadcastDispatcher,
     userRepository: UserRepository,
     private val wallpaperManager: WallpaperManager,
     private val context: Context,
     private val secureSettings: SecureSettings,
+    @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
 ) : WallpaperRepository {
     private val wallpaperChanged: Flow<Unit> =
         broadcastDispatcher
@@ -106,26 +117,19 @@
             // Only update the wallpaper status once the user selection has finished.
             .filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
 
-    override val wallpaperInfo: StateFlow<WallpaperInfo?> =
-        if (!wallpaperManager.isWallpaperSupported) {
-            MutableStateFlow(null).asStateFlow()
-        } else {
-            combine(wallpaperChanged, selectedUser, ::Pair)
-                .mapLatestConflated { (_, selectedUser) -> getWallpaper(selectedUser) }
-                .stateIn(
-                    scope,
-                    // Always be listening for wallpaper changes.
-                    SharingStarted.Eagerly,
-                    // The initial value is null, but it should get updated pretty quickly because
-                    // the `combine` should immediately kick off a fetch.
-                    initialValue = null,
-                )
-        }
-
+    override val wallpaperInfo: StateFlow<WallpaperInfo?> = getWallpaperInfo(FLAG_SYSTEM)
+    override val lockscreenWallpaperInfo: StateFlow<WallpaperInfo?> = getWallpaperInfo(FLAG_LOCK)
     override val wallpaperSupportsAmbientMode: Flow<Boolean> =
-        secureSettings
-            .observerFlow(UserHandle.USER_ALL, Settings.Secure.DOZE_ALWAYS_ON_WALLPAPER_ENABLED)
-            .onStart { emit(Unit) }
+        combine(
+                secureSettings
+                    .observerFlow(
+                        UserHandle.USER_ALL,
+                        Settings.Secure.DOZE_ALWAYS_ON_WALLPAPER_ENABLED,
+                    )
+                    .onStart { emit(Unit) },
+                configurationInteractor.onAnyConfigurationChange,
+                ::Pair,
+            )
             .map {
                 val userEnabled =
                     secureSettings.getInt(Settings.Secure.DOZE_ALWAYS_ON_WALLPAPER_ENABLED, 1) == 1
@@ -172,7 +176,7 @@
     }
 
     override val shouldSendFocalArea =
-        wallpaperInfo
+        lockscreenWallpaperInfo
             .map {
                 val focalAreaTarget = context.resources.getString(SysUIR.string.focal_area_target)
                 val shouldSendNotificationLayout = it?.component?.className == focalAreaTarget
@@ -184,12 +188,35 @@
                 initialValue = extendedWallpaperEffects(),
             )
 
-    private suspend fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? {
+    private suspend fun getWallpaper(
+        selectedUser: SelectedUserModel,
+        which: Int = FLAG_SYSTEM,
+    ): WallpaperInfo? {
         return withContext(bgDispatcher) {
-            wallpaperManager.getWallpaperInfoForUser(selectedUser.userInfo.id)
+            if (which == FLAG_LOCK && wallpaperManager.lockScreenWallpaperExists()) {
+                wallpaperManager.getWallpaperInfo(FLAG_LOCK, selectedUser.userInfo.id)
+            } else {
+                wallpaperManager.getWallpaperInfoForUser(selectedUser.userInfo.id)
+            }
         }
     }
 
+    private fun getWallpaperInfo(which: Int): StateFlow<WallpaperInfo?> =
+        if (!wallpaperManager.isWallpaperSupported) {
+            MutableStateFlow(null).asStateFlow()
+        } else {
+            combine(wallpaperChanged, selectedUser, ::Pair)
+                .mapLatestConflated { (_, selectedUser) -> getWallpaper(selectedUser, which) }
+                .stateIn(
+                    scope,
+                    // Always be listening for wallpaper changes.
+                    SharingStarted.Eagerly,
+                    // The initial value is null, but it should get updated pretty quickly because
+                    // the `combine` should immediately kick off a fetch.
+                    initialValue = null,
+                )
+        }
+
     companion object {
         private val TAG = WallpaperRepositoryImpl::class.simpleName
         private val DEBUG = true
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index a42f5d3..9475bdb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -50,7 +50,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
@@ -1878,7 +1877,7 @@
         verify(callback, never()).onTrustGrantedForCurrentUser(
                 anyBoolean() /* dismissKeyguard */,
                 eq(true) /* newlyUnlocked */,
-                anyObject() /* flags */,
+                any() /* flags */,
                 anyString() /* message */
         );
     }
@@ -2747,7 +2746,7 @@
                     () -> mJavaAdapter,
                     () -> mSceneInteractor,
                     () -> mCommunalSceneInteractor,
-                    mKeyguardServiceShowLockscreenInteractor);
+                    () -> mKeyguardServiceShowLockscreenInteractor);
             setAlternateBouncerVisibility(false);
             setPrimaryBouncerVisibility(false);
             setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
index 4f7610a..f98c130 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/SystemUIApplicationTest.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.dagger.SysUIComponent
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.flags.systemPropertiesHelper
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.process.processWrapper
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -48,7 +47,7 @@
 
     @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
 
-    val kosmos = Kosmos()
+    val kosmos = testKosmos()
     @Mock private lateinit var initializer: SystemUIInitializer
     @Mock private lateinit var rootComponent: GlobalRootComponent
     @Mock private lateinit var sysuiComponent: SysUIComponent
@@ -56,9 +55,13 @@
     @Mock private lateinit var initController: InitController
 
     class StartableA : TestableStartable()
+
     class StartableB : TestableStartable()
+
     class StartableC : TestableStartable()
+
     class StartableD : TestableStartable()
+
     class StartableE : TestableStartable()
 
     val dependencyMap: Map<Class<*>, Set<Class<out CoreStartable>>> =
@@ -114,7 +117,7 @@
             .thenReturn(
                 mutableMapOf(
                     StartableA::class.java to Provider { startableA },
-                    StartableB::class.java to Provider { startableB }
+                    StartableB::class.java to Provider { startableB },
                 )
             )
         app.onCreate()
@@ -130,7 +133,7 @@
                 mutableMapOf(
                     StartableC::class.java to Provider { startableC },
                     StartableA::class.java to Provider { startableA },
-                    StartableB::class.java to Provider { startableB }
+                    StartableB::class.java to Provider { startableB },
                 )
             )
         app.onCreate()
@@ -150,7 +153,7 @@
                     StartableC::class.java to Provider { startableC },
                     StartableD::class.java to Provider { startableD },
                     StartableA::class.java to Provider { startableA },
-                    StartableB::class.java to Provider { startableB }
+                    StartableB::class.java to Provider { startableB },
                 )
             )
         app.onCreate()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 845be02..60345a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -24,12 +24,15 @@
 import android.window.RemoteTransition
 import android.window.TransitionFilter
 import android.window.WindowAnimationState
+import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.EmptyTestActivity
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.shared.Flags
+import com.android.systemui.shared.Flags as SharedFlags
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.wm.shell.shared.ShellTransitions
@@ -43,7 +46,6 @@
 import kotlin.test.assertEquals
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Assert.assertThrows
 import org.junit.Before
@@ -57,8 +59,8 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
-import org.mockito.Spy
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.spy
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -66,21 +68,23 @@
 @RunWithLooper
 class ActivityTransitionAnimatorTest : SysuiTestCase() {
     private val kosmos = testKosmos()
-    private val transitionContainer = LinearLayout(mContext)
+
     private val mainExecutor = context.mainExecutor
     private val testTransitionAnimator = fakeTransitionAnimator(mainExecutor)
     private val testShellTransitions = FakeShellTransitions()
+
+    private val Kosmos.underTest by Kosmos.Fixture { activityTransitionAnimator }
+
     @Mock lateinit var callback: ActivityTransitionAnimator.Callback
     @Mock lateinit var listener: ActivityTransitionAnimator.Listener
-    @Spy private val controller = TestTransitionAnimatorController(transitionContainer)
     @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
 
-    private lateinit var underTest: ActivityTransitionAnimator
-    @get:Rule val rule = MockitoJUnit.rule()
+    @get:Rule(order = 0) val mockitoRule = MockitoJUnit.rule()
+    @get:Rule(order = 1) val activityRule = ActivityScenarioRule(EmptyTestActivity::class.java)
 
     @Before
     fun setup() {
-        underTest =
+        kosmos.activityTransitionAnimator =
             ActivityTransitionAnimator(
                 mainExecutor,
                 ActivityTransitionAnimator.TransitionRegister.fromShellTransitions(
@@ -89,19 +93,20 @@
                 testTransitionAnimator,
                 testTransitionAnimator,
                 disableWmTimeout = true,
+                skipReparentTransaction = true,
             )
-        underTest.callback = callback
-        underTest.addListener(listener)
+        kosmos.activityTransitionAnimator.callback = callback
+        kosmos.activityTransitionAnimator.addListener(listener)
     }
 
     @After
     fun tearDown() {
-        underTest.removeListener(listener)
+        kosmos.activityTransitionAnimator.removeListener(listener)
     }
 
     private fun startIntentWithAnimation(
-        animator: ActivityTransitionAnimator = underTest,
-        controller: ActivityTransitionAnimator.Controller? = this.controller,
+        controller: ActivityTransitionAnimator.Controller?,
+        animator: ActivityTransitionAnimator = kosmos.activityTransitionAnimator,
         animate: Boolean = true,
         intentStarter: (RemoteAnimationAdapter?) -> Int,
     ) {
@@ -119,129 +124,152 @@
 
     @Test
     fun animationAdapterIsNullIfControllerIsNull() {
-        var startedIntent = false
-        var animationAdapter: RemoteAnimationAdapter? = null
+        kosmos.runTest {
+            var startedIntent = false
+            var animationAdapter: RemoteAnimationAdapter? = null
 
-        startIntentWithAnimation(controller = null) { adapter ->
-            startedIntent = true
-            animationAdapter = adapter
+            startIntentWithAnimation(controller = null) { adapter ->
+                startedIntent = true
+                animationAdapter = adapter
 
-            ActivityManager.START_SUCCESS
+                ActivityManager.START_SUCCESS
+            }
+
+            assertTrue(startedIntent)
+            assertNull(animationAdapter)
         }
-
-        assertTrue(startedIntent)
-        assertNull(animationAdapter)
     }
 
     @Test
     fun animatesIfActivityOpens() {
-        val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
-        var animationAdapter: RemoteAnimationAdapter? = null
-        startIntentWithAnimation { adapter ->
-            animationAdapter = adapter
-            ActivityManager.START_SUCCESS
-        }
+        kosmos.runTest {
+            val controller = createController()
+            val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+            var animationAdapter: RemoteAnimationAdapter? = null
+            startIntentWithAnimation(controller) { adapter ->
+                animationAdapter = adapter
+                ActivityManager.START_SUCCESS
+            }
 
-        assertNotNull(animationAdapter)
-        waitForIdleSync()
-        verify(controller).onIntentStarted(willAnimateCaptor.capture())
-        assertTrue(willAnimateCaptor.value)
+            assertNotNull(animationAdapter)
+            waitForIdleSync()
+            verify(controller).onIntentStarted(willAnimateCaptor.capture())
+            assertTrue(willAnimateCaptor.value)
+        }
     }
 
     @Test
     fun doesNotAnimateIfActivityIsAlreadyOpen() {
-        val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
-        startIntentWithAnimation { ActivityManager.START_DELIVERED_TO_TOP }
+        kosmos.runTest {
+            val controller = createController()
+            val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+            startIntentWithAnimation(controller) { ActivityManager.START_DELIVERED_TO_TOP }
 
-        waitForIdleSync()
-        verify(controller).onIntentStarted(willAnimateCaptor.capture())
-        assertFalse(willAnimateCaptor.value)
+            waitForIdleSync()
+            verify(controller).onIntentStarted(willAnimateCaptor.capture())
+            assertFalse(willAnimateCaptor.value)
+        }
     }
 
     @Test
     fun animatesIfActivityIsAlreadyOpenAndIsOnKeyguard() {
-        `when`(callback.isOnKeyguard()).thenReturn(true)
+        kosmos.runTest {
+            `when`(callback.isOnKeyguard()).thenReturn(true)
 
-        val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
-        var animationAdapter: RemoteAnimationAdapter? = null
+            val controller = createController()
+            val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+            var animationAdapter: RemoteAnimationAdapter? = null
 
-        startIntentWithAnimation(underTest) { adapter ->
-            animationAdapter = adapter
-            ActivityManager.START_DELIVERED_TO_TOP
+            startIntentWithAnimation(controller, underTest) { adapter ->
+                animationAdapter = adapter
+                ActivityManager.START_DELIVERED_TO_TOP
+            }
+
+            waitForIdleSync()
+            verify(controller).onIntentStarted(willAnimateCaptor.capture())
+            verify(callback).hideKeyguardWithAnimation(any())
+
+            assertTrue(willAnimateCaptor.value)
+            assertNull(animationAdapter)
         }
-
-        waitForIdleSync()
-        verify(controller).onIntentStarted(willAnimateCaptor.capture())
-        verify(callback).hideKeyguardWithAnimation(any())
-
-        assertTrue(willAnimateCaptor.value)
-        assertNull(animationAdapter)
     }
 
     @Test
     fun doesNotAnimateIfAnimateIsFalse() {
-        val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
-        startIntentWithAnimation(animate = false) { ActivityManager.START_SUCCESS }
+        kosmos.runTest {
+            val controller = createController()
+            val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+            startIntentWithAnimation(controller, animate = false) { ActivityManager.START_SUCCESS }
 
-        waitForIdleSync()
-        verify(controller).onIntentStarted(willAnimateCaptor.capture())
-        assertFalse(willAnimateCaptor.value)
+            waitForIdleSync()
+            verify(controller).onIntentStarted(willAnimateCaptor.capture())
+            assertFalse(willAnimateCaptor.value)
+        }
     }
 
-    @EnableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY)
+    @EnableFlags(SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY)
     @Test
     fun registersReturnIffCookieIsPresent() {
-        `when`(callback.isOnKeyguard()).thenReturn(false)
+        kosmos.runTest {
+            `when`(callback.isOnKeyguard()).thenReturn(false)
 
-        startIntentWithAnimation(underTest, controller) { ActivityManager.START_DELIVERED_TO_TOP }
-
-        waitForIdleSync()
-        assertTrue(testShellTransitions.remotes.isEmpty())
-        assertTrue(testShellTransitions.remotesForTakeover.isEmpty())
-
-        val controller =
-            object : DelegateTransitionAnimatorController(controller) {
-                override val transitionCookie
-                    get() = ActivityTransitionAnimator.TransitionCookie("testCookie")
+            val controller = createController()
+            startIntentWithAnimation(controller, underTest) {
+                ActivityManager.START_DELIVERED_TO_TOP
             }
 
-        startIntentWithAnimation(underTest, controller) { ActivityManager.START_DELIVERED_TO_TOP }
+            waitForIdleSync()
+            assertTrue(testShellTransitions.remotes.isEmpty())
+            assertTrue(testShellTransitions.remotesForTakeover.isEmpty())
 
-        waitForIdleSync()
-        assertEquals(1, testShellTransitions.remotes.size)
-        assertTrue(testShellTransitions.remotesForTakeover.isEmpty())
+            val controllerWithCookie =
+                object : DelegateTransitionAnimatorController(controller) {
+                    override val transitionCookie
+                        get() = ActivityTransitionAnimator.TransitionCookie("testCookie")
+                }
+
+            startIntentWithAnimation(controllerWithCookie, underTest) {
+                ActivityManager.START_DELIVERED_TO_TOP
+            }
+
+            waitForIdleSync()
+            assertEquals(1, testShellTransitions.remotes.size)
+            assertTrue(testShellTransitions.remotesForTakeover.isEmpty())
+        }
     }
 
     @EnableFlags(
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
     )
     @Test
     fun registersLongLivedTransition() {
         kosmos.runTest {
-            var factory = controllerFactory()
+            val controller = createController()
+            var factory = controllerFactory(controller)
             underTest.register(factory.cookie, factory, testScope)
             assertEquals(2, testShellTransitions.remotes.size)
 
-            factory = controllerFactory()
+            factory = controllerFactory(controller)
             underTest.register(factory.cookie, factory, testScope)
             assertEquals(4, testShellTransitions.remotes.size)
         }
     }
 
     @EnableFlags(
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
     )
     @Test
     fun registersLongLivedTransitionOverridingPreviousRegistration() {
         kosmos.runTest {
+            val controller = createController()
             val cookie = ActivityTransitionAnimator.TransitionCookie("test_cookie")
-            var factory = controllerFactory(cookie)
+            var factory = controllerFactory(controller, cookie)
             underTest.register(cookie, factory, testScope)
             val transitions = testShellTransitions.remotes.values.toList()
 
-            factory = controllerFactory(cookie)
+            factory = controllerFactory(controller, cookie)
             underTest.register(cookie, factory, testScope)
             assertEquals(2, testShellTransitions.remotes.size)
             for (transition in transitions) {
@@ -250,23 +278,25 @@
         }
     }
 
-    @DisableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
+    @DisableFlags(SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
     @Test
     fun doesNotRegisterLongLivedTransitionIfFlagIsDisabled() {
         kosmos.runTest {
-            val factory = controllerFactory(component = null)
+            val factory = controllerFactory(createController(), component = null)
             assertThrows(IllegalStateException::class.java) {
                 underTest.register(factory.cookie, factory, testScope)
             }
         }
     }
 
-    @EnableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
+    @EnableFlags(SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
     @Test
     fun doesNotRegisterLongLivedTransitionIfMissingRequiredProperties() {
         kosmos.runTest {
+            val controller = createController()
+
             // No ComponentName
-            var factory = controllerFactory(component = null)
+            var factory = controllerFactory(controller, component = null)
             assertThrows(IllegalStateException::class.java) {
                 underTest.register(factory.cookie, factory, testScope)
             }
@@ -280,7 +310,7 @@
                     testTransitionAnimator,
                     disableWmTimeout = true,
                 )
-            factory = controllerFactory()
+            factory = controllerFactory(controller)
             assertThrows(IllegalStateException::class.java) {
                 activityTransitionAnimator.register(factory.cookie, factory, testScope)
             }
@@ -288,17 +318,18 @@
     }
 
     @EnableFlags(
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
     )
     @Test
     fun unregistersLongLivedTransition() {
         kosmos.runTest {
+            val controller = createController()
             val cookies = arrayOfNulls<ActivityTransitionAnimator.TransitionCookie>(3)
 
             for (index in 0 until 3) {
                 cookies[index] = mock(ActivityTransitionAnimator.TransitionCookie::class.java)
-                val factory = controllerFactory(cookies[index]!!)
+                val factory = controllerFactory(controller, cookies[index]!!)
                 underTest.register(factory.cookie, factory, testScope)
             }
 
@@ -315,75 +346,98 @@
 
     @Test
     fun doesNotStartIfAnimationIsCancelled() {
-        val runner = underTest.createEphemeralRunner(controller)
-        runner.onAnimationCancelled()
-        runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback)
+        kosmos.runTest {
+            val controller = createController()
+            val runner = underTest.createEphemeralRunner(controller)
+            runner.onAnimationCancelled()
+            runner.onAnimationStart(
+                TRANSIT_NONE,
+                emptyArray(),
+                emptyArray(),
+                emptyArray(),
+                iCallback,
+            )
 
-        waitForIdleSync()
-        verify(controller).onTransitionAnimationCancelled()
-        verify(controller, never()).onTransitionAnimationStart(anyBoolean())
-        verify(listener).onTransitionAnimationCancelled()
-        verify(listener, never()).onTransitionAnimationStart()
-        assertNull(runner.delegate)
+            waitForIdleSync()
+            verify(controller).onTransitionAnimationCancelled()
+            verify(controller, never()).onTransitionAnimationStart(anyBoolean())
+            verify(listener).onTransitionAnimationCancelled()
+            verify(listener, never()).onTransitionAnimationStart()
+            assertNull(runner.delegate)
+        }
     }
 
     @Test
     fun cancelsIfNoOpeningWindowIsFound() {
-        val runner = underTest.createEphemeralRunner(controller)
-        runner.onAnimationStart(TRANSIT_NONE, emptyArray(), emptyArray(), emptyArray(), iCallback)
+        kosmos.runTest {
+            val controller = createController()
+            val runner = underTest.createEphemeralRunner(controller)
+            runner.onAnimationStart(
+                TRANSIT_NONE,
+                emptyArray(),
+                emptyArray(),
+                emptyArray(),
+                iCallback,
+            )
 
-        waitForIdleSync()
-        verify(controller).onTransitionAnimationCancelled()
-        verify(controller, never()).onTransitionAnimationStart(anyBoolean())
-        verify(listener).onTransitionAnimationCancelled()
-        verify(listener, never()).onTransitionAnimationStart()
-        assertNull(runner.delegate)
+            waitForIdleSync()
+            verify(controller).onTransitionAnimationCancelled()
+            verify(controller, never()).onTransitionAnimationStart(anyBoolean())
+            verify(listener).onTransitionAnimationCancelled()
+            verify(listener, never()).onTransitionAnimationStart()
+            assertNull(runner.delegate)
+        }
     }
 
     @Test
     fun startsAnimationIfWindowIsOpening() {
-        val runner = underTest.createEphemeralRunner(controller)
-        runner.onAnimationStart(
-            TRANSIT_NONE,
-            arrayOf(fakeWindow()),
-            emptyArray(),
-            emptyArray(),
-            iCallback,
-        )
-        waitForIdleSync()
-        verify(listener).onTransitionAnimationStart()
-        verify(controller).onTransitionAnimationStart(anyBoolean())
+        kosmos.runTest {
+            val controller = createController()
+            val runner = underTest.createEphemeralRunner(controller)
+            runner.onAnimationStart(
+                TRANSIT_NONE,
+                arrayOf(fakeWindow()),
+                emptyArray(),
+                emptyArray(),
+                iCallback,
+            )
+            waitForIdleSync()
+            verify(listener).onTransitionAnimationStart()
+            verify(controller).onTransitionAnimationStart(anyBoolean())
+        }
     }
 
     @Test
     fun creatingControllerFromNormalViewThrows() {
-        assertThrows(IllegalArgumentException::class.java) {
-            ActivityTransitionAnimator.Controller.fromView(FrameLayout(mContext))
+        kosmos.runTest {
+            assertThrows(IllegalArgumentException::class.java) {
+                ActivityTransitionAnimator.Controller.fromView(FrameLayout(mContext))
+            }
         }
     }
 
     @DisableFlags(
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
     )
     @Test
     fun creatingRunnerWithLazyInitializationThrows_whenTheFlagsAreDisabled() {
         kosmos.runTest {
             assertThrows(IllegalStateException::class.java) {
-                val factory = controllerFactory()
+                val factory = controllerFactory(createController())
                 underTest.createLongLivedRunner(factory, testScope, forLaunch = true)
             }
         }
     }
 
     @EnableFlags(
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
     )
     @Test
     fun runnerCreatesDelegateLazily_onAnimationStart() {
         kosmos.runTest {
-            val factory = controllerFactory()
+            val factory = controllerFactory(createController())
             val runner = underTest.createLongLivedRunner(factory, testScope, forLaunch = true)
             assertNull(runner.delegate)
 
@@ -412,13 +466,13 @@
     }
 
     @EnableFlags(
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
     )
     @Test
     fun runnerCreatesDelegateLazily_onAnimationTakeover() {
         kosmos.runTest {
-            val factory = controllerFactory()
+            val factory = controllerFactory(createController())
             val runner = underTest.createLongLivedRunner(factory, testScope, forLaunch = false)
             assertNull(runner.delegate)
 
@@ -446,58 +500,78 @@
     }
 
     @DisableFlags(
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
     )
     @Test
     fun animationTakeoverThrows_whenTheFlagsAreDisabled() {
-        val runner = underTest.createEphemeralRunner(controller)
-        assertThrows(IllegalStateException::class.java) {
-            runner.takeOverAnimation(
-                arrayOf(fakeWindow()),
-                emptyArray(),
-                SurfaceControl.Transaction(),
-                iCallback,
-            )
+        kosmos.runTest {
+            val controller = createController()
+            val runner = underTest.createEphemeralRunner(controller)
+            assertThrows(IllegalStateException::class.java) {
+                runner.takeOverAnimation(
+                    arrayOf(fakeWindow()),
+                    emptyArray(),
+                    SurfaceControl.Transaction(),
+                    iCallback,
+                )
+            }
         }
     }
 
     @DisableFlags(
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
-        Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY,
+        SharedFlags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED,
     )
     @Test
     fun disposeRunner_delegateDereferenced() {
-        val runner = underTest.createEphemeralRunner(controller)
-        assertNotNull(runner.delegate)
-        runner.dispose()
-        waitForIdleSync()
-        assertNull(runner.delegate)
+        kosmos.runTest {
+            val controller = createController()
+            val runner = underTest.createEphemeralRunner(controller)
+            assertNotNull(runner.delegate)
+            runner.dispose()
+            waitForIdleSync()
+            assertNull(runner.delegate)
+        }
     }
 
     @Test
     fun concurrentListenerModification_doesNotThrow() {
-        // Need a second listener to trigger the concurrent modification.
-        underTest.addListener(object : ActivityTransitionAnimator.Listener {})
-        `when`(listener.onTransitionAnimationStart()).thenAnswer {
-            underTest.removeListener(listener)
-            listener
+        kosmos.runTest {
+            // Need a second listener to trigger the concurrent modification.
+            underTest.addListener(object : ActivityTransitionAnimator.Listener {})
+            `when`(listener.onTransitionAnimationStart()).thenAnswer {
+                underTest.removeListener(listener)
+                listener
+            }
+
+            val controller = createController()
+            val runner = underTest.createEphemeralRunner(controller)
+            runner.onAnimationStart(
+                TRANSIT_NONE,
+                arrayOf(fakeWindow()),
+                emptyArray(),
+                emptyArray(),
+                iCallback,
+            )
+
+            waitForIdleSync()
+            verify(listener).onTransitionAnimationStart()
         }
+    }
 
-        val runner = underTest.createEphemeralRunner(controller)
-        runner.onAnimationStart(
-            TRANSIT_NONE,
-            arrayOf(fakeWindow()),
-            emptyArray(),
-            emptyArray(),
-            iCallback,
-        )
-
+    private fun createController(): TestTransitionAnimatorController {
+        lateinit var transitionContainer: ViewGroup
+        activityRule.scenario.onActivity { activity ->
+            transitionContainer = LinearLayout(activity)
+            activity.setContentView(transitionContainer)
+        }
         waitForIdleSync()
-        verify(listener).onTransitionAnimationStart()
+        return spy(TestTransitionAnimatorController(transitionContainer))
     }
 
     private fun controllerFactory(
+        controller: ActivityTransitionAnimator.Controller,
         cookie: ActivityTransitionAnimator.TransitionCookie =
             mock(ActivityTransitionAnimator.TransitionCookie::class.java),
         component: ComponentName? = mock(ComponentName::class.java),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
index 1268de0..4cfe106 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
@@ -34,9 +34,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.activity.EmptyTestActivity
 import com.android.systemui.concurrency.fakeExecutor
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.runOnMainThreadAndWaitForIdleSync
+import com.android.systemui.testKosmos
 import kotlin.test.assertTrue
 import org.junit.Rule
 import org.junit.Test
@@ -77,7 +77,7 @@
         }
     }
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val pathManager =
         GoldenPathManager(
             context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 0e68fce..2c70249 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -73,9 +73,9 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.display.data.repository.displayStateRepository
 import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.withArgCaptor
 import com.google.android.msdl.data.model.MSDLToken
 import com.google.common.truth.Truth.assertThat
@@ -189,7 +189,7 @@
     private lateinit var promptContentViewWithMoreOptionsButton:
         PromptContentViewWithMoreOptionsButton
 
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     @Before
     fun setup() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
index 7c8822b..96ef8b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
@@ -18,8 +18,6 @@
 
 import android.bluetooth.BluetoothDevice
 import android.graphics.drawable.Drawable
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import android.util.Pair
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -29,7 +27,6 @@
 import com.android.settingslib.bluetooth.BluetoothUtils
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.LocalBluetoothManager
-import com.android.settingslib.flags.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
 import com.google.common.truth.Truth.assertThat
@@ -208,42 +205,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testSavedFactory_isFilterMatched_bondedAndNotConnected_returnsTrue() {
-        `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(cachedDevice.isConnected).thenReturn(false)
-
-        assertThat(
-                savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false)
-            )
-            .isTrue()
-    }
-
-    @Test
-    @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testSavedFactory_isFilterMatched_connected_returnsFalse() {
-        `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(cachedDevice.isConnected).thenReturn(true)
-
-        assertThat(
-                savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false)
-            )
-            .isFalse()
-    }
-
-    @Test
-    @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testSavedFactory_isFilterMatched_notBonded_returnsFalse() {
-        `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
-
-        assertThat(
-                savedDeviceItemFactory.isFilterMatched(context, cachedDevice, isOngoingCall = false)
-            )
-            .isFalse()
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testSavedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() {
         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true)
@@ -255,7 +216,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testSavedFactory_isFilterMatched_notExclusiveManaged_returnsTrue() {
         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
@@ -269,7 +229,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testSavedFactory_isFilterMatched_notExclusivelyManaged_connected_returnsFalse() {
         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
@@ -283,35 +242,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testConnectedFactory_isFilterMatched_bondedAndConnected_returnsTrue() {
-        `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(true)
-
-        assertThat(
-                connectedDeviceItemFactory.isFilterMatched(
-                    context,
-                    cachedDevice,
-                    isOngoingCall = false,
-                )
-            )
-            .isTrue()
-    }
-
-    @Test
-    @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testConnectedFactory_isFilterMatched_notConnected_returnsFalse() {
-        assertThat(
-                connectedDeviceItemFactory.isFilterMatched(
-                    context,
-                    cachedDevice,
-                    isOngoingCall = false,
-                )
-            )
-            .isFalse()
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testConnectedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() {
         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true)
@@ -327,7 +257,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testConnectedFactory_isFilterMatched_noExclusiveManager_returnsTrue() {
         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
@@ -344,7 +273,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notConnected_returnsFalse() {
         `when`(cachedDevice.device).thenReturn(bluetoothDevice)
         `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/brightness/ui/compose/BrightnessSliderMotionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/brightness/ui/compose/BrightnessSliderMotionTest.kt
index 9dab9d7..b134dff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/brightness/ui/compose/BrightnessSliderMotionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/brightness/ui/compose/BrightnessSliderMotionTest.kt
@@ -31,8 +31,8 @@
 import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
 import com.android.systemui.common.shared.model.asIcon
 import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.motion.createSysUiComposeMotionTestRule
+import com.android.systemui.testKosmos
 import com.android.systemui.utils.PolicyRestriction
 import kotlin.test.Test
 import kotlin.time.Duration.Companion.seconds
@@ -61,7 +61,7 @@
 class BrightnessSliderMotionTest : SysuiTestCase() {
 
     private val deviceSpec = DeviceEmulationSpec(Phone)
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
 
     @get:Rule val motionTestRule = createSysUiComposeMotionTestRule(kosmos, deviceSpec)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 5c893da..6a4f3da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -44,6 +44,7 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -77,6 +78,7 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.Optional;
+import java.util.function.Consumer;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -158,6 +160,31 @@
      * is false are removed.[
      */
     private void initController() {
+        IntentCreator fakeIntentCreator = new IntentCreator() {
+            @Override
+            public Intent getTextEditorIntent(Context context) {
+                return new Intent();
+            }
+
+            @Override
+            public Intent getShareIntent(ClipData clipData, Context context) {
+                Intent intent = Intent.createChooser(new Intent(Intent.ACTION_SEND), null);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                return intent;
+            }
+
+            @Override
+            public void getImageEditIntentAsync(Uri uri, Context context,
+                    Consumer<Intent> outputConsumer) {
+                outputConsumer.accept(new Intent(Intent.ACTION_EDIT));
+            }
+
+            @Override
+            public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
+                return new Intent();
+            }
+        };
+
         mOverlayController = new ClipboardOverlayController(
                 mContext,
                 mClipboardOverlayView,
@@ -171,7 +198,7 @@
                 mClipboardTransitionExecutor,
                 mClipboardIndicationProvider,
                 mUiEventLogger,
-                new ActionIntentCreator());
+                fakeIntentCreator);
         verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
         mCallbacks = mOverlayCallbacksCaptor.getValue();
     }
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 fb70846..061f798 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -134,7 +134,6 @@
     @Mock private RingerModeTracker mRingerModeTracker;
     @Mock private RingerModeLiveData mRingerModeLiveData;
     @Mock private PackageManager mPackageManager;
-    @Mock private Handler mHandler;
     @Mock private UserContextProvider mUserContextProvider;
     @Mock private VibratorHelper mVibratorHelper;
     @Mock private ShadeController mShadeController;
@@ -148,6 +147,7 @@
     private TestableLooper mTestableLooper;
     private KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
     private GlobalActionsInteractor mInteractor;
+    private Handler mHandler;
 
     @Before
     public void setUp() throws Exception {
@@ -166,6 +166,7 @@
         mGlobalSettings = new FakeGlobalSettings();
         mSecureSettings = new FakeSettings();
         mInteractor = mKosmos.getGlobalActionsInteractor();
+        mHandler = new Handler(mTestableLooper.getLooper());
 
         mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
                 mWindowManagerFuncs,
@@ -771,6 +772,31 @@
         mGlobalActionsDialogLite.showOrHideDialog(false, false, null, Display.DEFAULT_DISPLAY);
     }
 
+    @Test
+    public void userSwitching_dismissDialog() {
+        String[] actions = {
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+        };
+        doReturn(actions).when(mResources)
+                .getStringArray(com.android.internal.R.array.config_globalActionsList);
+
+        mGlobalActionsDialogLite.showOrHideDialog(false, true, null, Display.DEFAULT_DISPLAY);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mGlobalActionsDialogLite.mDialog.isShowing()).isTrue();
+
+        ArgumentCaptor<UserTracker.Callback> captor =
+                ArgumentCaptor.forClass(UserTracker.Callback.class);
+
+        verify(mUserTracker).addCallback(captor.capture(), any());
+
+        captor.getValue().onBeforeUserSwitching(100);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mGlobalActionsDialogLite.mDialog).isNull();
+    }
+
     private UserInfo mockCurrentUser(int flags) {
         return new UserInfo(10, "A User", flags);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index e8054c0..8105ae0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -283,7 +283,6 @@
                 () -> mSelectedUserInteractor,
                 mock(UserTracker.class),
                 mKosmos.getNotificationShadeWindowModel(),
-                mSecureSettings,
                 mKosmos::getCommunalInteractor,
                 mKosmos.getShadeLayoutParams());
         mFeatureFlags = new FakeFeatureFlags();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index b274f1c..d9c59d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -88,11 +88,6 @@
 
     companion object {
         private val INTENT = Intent("some.intent.action")
-        private val DRAWABLE =
-            mock<Icon> {
-                whenever(this.contentDescription)
-                    .thenReturn(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
-            }
         private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
 
         @Parameters(
@@ -337,7 +332,17 @@
 
             homeControls.setState(
                 lockScreenState =
-                    KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = DRAWABLE)
+                    KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                        icon =
+                            mock<Icon> {
+                                whenever(contentDescription)
+                                    .thenReturn(
+                                        ContentDescription.Resource(
+                                            res = CONTENT_DESCRIPTION_RESOURCE_ID
+                                        )
+                                    )
+                            }
+                    )
             )
             homeControls.onTriggeredResult =
                 if (startActivity) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.java
deleted file mode 100644
index 7297e0f..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.java
+++ /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.lowlightclock;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shared.condition.Condition;
-import com.android.systemui.statusbar.commandline.Command;
-import com.android.systemui.statusbar.commandline.CommandRegistry;
-
-import kotlin.jvm.functions.Function0;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.io.PrintWriter;
-import java.util.Arrays;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class ForceLowLightConditionTest extends SysuiTestCase {
-    @Mock
-    private CommandRegistry mCommandRegistry;
-
-    @Mock
-    private Condition.Callback mCallback;
-
-    @Mock
-    private PrintWriter mPrintWriter;
-
-    @Mock
-    CoroutineScope mScope;
-
-    private ForceLowLightCondition mCondition;
-    private Command mCommand;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mCondition = new ForceLowLightCondition(mScope, mCommandRegistry);
-        mCondition.addCallback(mCallback);
-        ArgumentCaptor<Function0<Command>> commandCaptor =
-                ArgumentCaptor.forClass(Function0.class);
-        verify(mCommandRegistry).registerCommand(eq(ForceLowLightCondition.COMMAND_ROOT),
-                commandCaptor.capture());
-        mCommand = commandCaptor.getValue().invoke();
-    }
-
-    @Test
-    public void testEnableLowLight() {
-        mCommand.execute(mPrintWriter,
-                Arrays.asList(ForceLowLightCondition.COMMAND_ENABLE_LOW_LIGHT));
-        verify(mCallback).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionSet()).isTrue();
-        assertThat(mCondition.isConditionMet()).isTrue();
-    }
-
-    @Test
-    public void testDisableLowLight() {
-        mCommand.execute(mPrintWriter,
-                Arrays.asList(ForceLowLightCondition.COMMAND_DISABLE_LOW_LIGHT));
-        verify(mCallback).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionSet()).isTrue();
-        assertThat(mCondition.isConditionMet()).isFalse();
-    }
-
-    @Test
-    public void testClearEnableLowLight() {
-        mCommand.execute(mPrintWriter,
-                Arrays.asList(ForceLowLightCondition.COMMAND_ENABLE_LOW_LIGHT));
-        verify(mCallback).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionSet()).isTrue();
-        assertThat(mCondition.isConditionMet()).isTrue();
-        Mockito.clearInvocations(mCallback);
-        mCommand.execute(mPrintWriter,
-                Arrays.asList(ForceLowLightCondition.COMMAND_CLEAR_LOW_LIGHT));
-        verify(mCallback).onConditionChanged(mCondition);
-        assertThat(mCondition.isConditionSet()).isFalse();
-        assertThat(mCondition.isConditionMet()).isFalse();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.kt
new file mode 100644
index 0000000..f8ce242
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ForceLowLightConditionTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.lowlightclock
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shared.condition.Condition
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.google.common.truth.Truth
+import java.io.PrintWriter
+import java.util.Arrays
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ForceLowLightConditionTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+
+    @Mock private lateinit var commandRegistry: CommandRegistry
+
+    @Mock private lateinit var callback: Condition.Callback
+
+    @Mock private lateinit var printWriter: PrintWriter
+
+    private lateinit var condition: ForceLowLightCondition
+    private lateinit var command: Command
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        condition = ForceLowLightCondition(kosmos.testScope, commandRegistry)
+        condition.addCallback(callback)
+        val commandCaptor = argumentCaptor<() -> Command>()
+        Mockito.verify(commandRegistry)
+            .registerCommand(eq(ForceLowLightCondition.COMMAND_ROOT), commandCaptor.capture())
+        command = commandCaptor.lastValue.invoke()
+    }
+
+    @Test
+    fun testEnableLowLight() =
+        kosmos.runTest {
+            command.execute(
+                printWriter,
+                Arrays.asList(ForceLowLightCondition.COMMAND_ENABLE_LOW_LIGHT),
+            )
+            Mockito.verify(callback).onConditionChanged(condition)
+            Truth.assertThat(condition.isConditionSet).isTrue()
+            Truth.assertThat(condition.isConditionMet).isTrue()
+        }
+
+    @Test
+    fun testDisableLowLight() =
+        kosmos.runTest {
+            command.execute(printWriter, listOf(ForceLowLightCondition.COMMAND_DISABLE_LOW_LIGHT))
+            Mockito.verify(callback).onConditionChanged(condition)
+            Truth.assertThat(condition.isConditionSet).isTrue()
+            Truth.assertThat(condition.isConditionMet).isFalse()
+        }
+
+    @Test
+    fun testClearEnableLowLight() =
+        kosmos.runTest {
+            command.execute(printWriter, listOf(ForceLowLightCondition.COMMAND_ENABLE_LOW_LIGHT))
+            Mockito.verify(callback).onConditionChanged(condition)
+            Truth.assertThat(condition.isConditionSet).isTrue()
+            Truth.assertThat(condition.isConditionMet).isTrue()
+            Mockito.clearInvocations(callback)
+            command.execute(printWriter, listOf(ForceLowLightCondition.COMMAND_CLEAR_LOW_LIGHT))
+            Mockito.verify(callback).onConditionChanged(condition)
+            Truth.assertThat(condition.isConditionSet).isFalse()
+            Truth.assertThat(condition.isConditionMet).isFalse()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.java
deleted file mode 100644
index 2c21624..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.java
+++ /dev/null
@@ -1,143 +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.lowlightclock;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.SysuiTestCase;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class LowLightConditionTest extends SysuiTestCase {
-    @Mock
-    private AmbientLightModeMonitor mAmbientLightModeMonitor;
-    @Mock
-    private UiEventLogger mUiEventLogger;
-    @Mock
-    CoroutineScope mScope;
-    private LowLightCondition mCondition;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mCondition = new LowLightCondition(mScope, mAmbientLightModeMonitor, mUiEventLogger);
-        mCondition.start();
-    }
-
-    @Test
-    public void testLowLightFalse() {
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-        assertThat(mCondition.isConditionMet()).isFalse();
-    }
-
-    @Test
-    public void testLowLightTrue() {
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-        assertThat(mCondition.isConditionMet()).isTrue();
-    }
-
-    @Test
-    public void testUndecidedLowLightStateIgnored() {
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-        assertThat(mCondition.isConditionMet()).isTrue();
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED);
-        assertThat(mCondition.isConditionMet()).isTrue();
-    }
-
-    @Test
-    public void testLowLightChange() {
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-        assertThat(mCondition.isConditionMet()).isFalse();
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-        assertThat(mCondition.isConditionMet()).isTrue();
-    }
-
-    @Test
-    public void testResetIsConditionMetUponStop() {
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-        assertThat(mCondition.isConditionMet()).isTrue();
-
-        mCondition.stop();
-        assertThat(mCondition.isConditionMet()).isFalse();
-    }
-
-    @Test
-    public void testLoggingAmbientLightNotLowToLow() {
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-        // Only logged once.
-        verify(mUiEventLogger, times(1)).log(any());
-        // Logged with the correct state.
-        verify(mUiEventLogger).log(LowLightDockEvent.AMBIENT_LIGHT_TO_DARK);
-    }
-
-    @Test
-    public void testLoggingAmbientLightLowToLow() {
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-        reset(mUiEventLogger);
-
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-        // Doesn't log.
-        verify(mUiEventLogger, never()).log(any());
-    }
-
-    @Test
-    public void testLoggingAmbientLightNotLowToNotLow() {
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-        // Doesn't log.
-        verify(mUiEventLogger, never()).log(any());
-    }
-
-    @Test
-    public void testLoggingAmbientLightLowToNotLow() {
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-        reset(mUiEventLogger);
-
-        changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-        // Only logged once.
-        verify(mUiEventLogger).log(any());
-        // Logged with the correct state.
-        verify(mUiEventLogger).log(LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT);
-    }
-
-    private void changeLowLightMode(int mode) {
-        ArgumentCaptor<AmbientLightModeMonitor.Callback> ambientLightCallbackCaptor =
-                ArgumentCaptor.forClass(AmbientLightModeMonitor.Callback.class);
-        verify(mAmbientLightModeMonitor).start(ambientLightCallbackCaptor.capture());
-        ambientLightCallbackCaptor.getValue().onChange(mode);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.kt
new file mode 100644
index 0000000..da68442
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/LowLightConditionTest.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.lowlightclock
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.condition.testStart
+import com.android.systemui.condition.testStop
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.argumentCaptor
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class LowLightConditionTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
+    @Mock private lateinit var ambientLightModeMonitor: AmbientLightModeMonitor
+
+    @Mock private lateinit var uiEventLogger: UiEventLogger
+
+    private lateinit var condition: LowLightCondition
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        condition = LowLightCondition(kosmos.testScope, ambientLightModeMonitor, uiEventLogger)
+    }
+
+    @Test
+    fun testLowLightFalse() =
+        kosmos.runTest {
+            testStart(condition)
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
+            Truth.assertThat(condition.isConditionMet).isFalse()
+        }
+
+    @Test
+    fun testLowLightTrue() =
+        kosmos.runTest {
+            testStart(condition)
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+            Truth.assertThat(condition.isConditionMet).isTrue()
+        }
+
+    @Test
+    fun testUndecidedLowLightStateIgnored() =
+        kosmos.runTest {
+            testStart(condition)
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+            Truth.assertThat(condition.isConditionMet).isTrue()
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED)
+            Truth.assertThat(condition.isConditionMet).isTrue()
+        }
+
+    @Test
+    fun testLowLightChange() =
+        kosmos.runTest {
+            testStart(condition)
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
+            Truth.assertThat(condition.isConditionMet).isFalse()
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+            Truth.assertThat(condition.isConditionMet).isTrue()
+        }
+
+    @Test
+    fun testResetIsConditionMetUponStop() =
+        kosmos.runTest {
+            testStart(condition)
+            runCurrent()
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+            Truth.assertThat(condition.isConditionMet).isTrue()
+
+            testStop(condition)
+            Truth.assertThat(condition.isConditionMet).isFalse()
+        }
+
+    @Test
+    fun testLoggingAmbientLightNotLowToLow() =
+        kosmos.runTest {
+            testStart(condition)
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+            // Only logged once.
+            Mockito.verify(uiEventLogger, Mockito.times(1)).log(ArgumentMatchers.any())
+            // Logged with the correct state.
+            Mockito.verify(uiEventLogger).log(LowLightDockEvent.AMBIENT_LIGHT_TO_DARK)
+        }
+
+    @Test
+    fun testLoggingAmbientLightLowToLow() =
+        kosmos.runTest {
+            testStart(condition)
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+            Mockito.reset(uiEventLogger)
+
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+            // Doesn't log.
+            Mockito.verify(uiEventLogger, Mockito.never()).log(ArgumentMatchers.any())
+        }
+
+    @Test
+    fun testLoggingAmbientLightNotLowToNotLow() =
+        kosmos.runTest {
+            testStart(condition)
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
+            // Doesn't log.
+            Mockito.verify(uiEventLogger, Mockito.never()).log(ArgumentMatchers.any())
+        }
+
+    @Test
+    fun testLoggingAmbientLightLowToNotLow() =
+        kosmos.runTest {
+            testStart(condition)
+            runCurrent()
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK)
+            Mockito.reset(uiEventLogger)
+
+            changeLowLightMode(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT)
+            // Only logged once.
+            Mockito.verify(uiEventLogger).log(ArgumentMatchers.any())
+            // Logged with the correct state.
+            Mockito.verify(uiEventLogger).log(LowLightDockEvent.AMBIENT_LIGHT_TO_LIGHT)
+        }
+
+    private fun changeLowLightMode(mode: Int) {
+        val ambientLightCallbackCaptor = argumentCaptor<AmbientLightModeMonitor.Callback>()
+
+        Mockito.verify(ambientLightModeMonitor).start(ambientLightCallbackCaptor.capture())
+        ambientLightCallbackCaptor.lastValue.onChange(mode)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.java
deleted file mode 100644
index 366c071..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.java
+++ /dev/null
@@ -1,108 +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.lowlightclock;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.settings.SecureSettings;
-
-import kotlinx.coroutines.CoroutineScope;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class ScreenSaverEnabledConditionTest extends SysuiTestCase {
-    @Mock
-    private Resources mResources;
-    @Mock
-    private SecureSettings mSecureSettings;
-    @Mock
-    CoroutineScope mScope;
-    @Captor
-    private ArgumentCaptor<ContentObserver> mSettingsObserverCaptor;
-    private ScreenSaverEnabledCondition mCondition;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        // Default dreams to enabled by default
-        doReturn(true).when(mResources).getBoolean(
-                com.android.internal.R.bool.config_dreamsEnabledByDefault);
-
-        mCondition = new ScreenSaverEnabledCondition(mScope, mResources, mSecureSettings);
-    }
-
-    @Test
-    public void testScreenSaverInitiallyEnabled() {
-        setScreenSaverEnabled(true);
-        mCondition.start();
-        assertThat(mCondition.isConditionMet()).isTrue();
-    }
-
-    @Test
-    public void testScreenSaverInitiallyDisabled() {
-        setScreenSaverEnabled(false);
-        mCondition.start();
-        assertThat(mCondition.isConditionMet()).isFalse();
-    }
-
-    @Test
-    public void testScreenSaverStateChanges() {
-        setScreenSaverEnabled(false);
-        mCondition.start();
-        assertThat(mCondition.isConditionMet()).isFalse();
-
-        setScreenSaverEnabled(true);
-        final ContentObserver observer = captureSettingsObserver();
-        observer.onChange(/* selfChange= */ false);
-        assertThat(mCondition.isConditionMet()).isTrue();
-    }
-
-    private void setScreenSaverEnabled(boolean enabled) {
-        when(mSecureSettings.getIntForUser(eq(Settings.Secure.SCREENSAVER_ENABLED), anyInt(),
-                eq(UserHandle.USER_CURRENT))).thenReturn(enabled ? 1 : 0);
-    }
-
-    private ContentObserver captureSettingsObserver() {
-        verify(mSecureSettings).registerContentObserverForUserSync(
-                eq(Settings.Secure.SCREENSAVER_ENABLED),
-                mSettingsObserverCaptor.capture(), eq(UserHandle.USER_CURRENT));
-        return mSettingsObserverCaptor.getValue();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.kt
new file mode 100644
index 0000000..29237a7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lowlightclock/ScreenSaverEnabledConditionTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.lowlightclock
+
+import android.content.res.Resources
+import android.database.ContentObserver
+import android.os.UserHandle
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.condition.testStart
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.SecureSettings
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ScreenSaverEnabledConditionTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
+    @Mock private lateinit var resources: Resources
+
+    @Mock private lateinit var secureSettings: SecureSettings
+
+    private val settingsObserverCaptor = argumentCaptor<ContentObserver>()
+    private lateinit var condition: ScreenSaverEnabledCondition
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        // Default dreams to enabled by default
+        whenever(resources.getBoolean(R.bool.config_dreamsEnabledByDefault)).thenReturn(true)
+
+        condition = ScreenSaverEnabledCondition(kosmos.testScope, resources, secureSettings)
+    }
+
+    @Test
+    fun testScreenSaverInitiallyEnabled() =
+        kosmos.runTest {
+            setScreenSaverEnabled(true)
+            testStart(condition)
+            Truth.assertThat(condition.isConditionMet).isTrue()
+        }
+
+    @Test
+    fun testScreenSaverInitiallyDisabled() =
+        kosmos.runTest {
+            setScreenSaverEnabled(false)
+            testStart(condition)
+            Truth.assertThat(condition.isConditionMet).isFalse()
+        }
+
+    @Test
+    fun testScreenSaverStateChanges() =
+        kosmos.runTest {
+            setScreenSaverEnabled(false)
+            testStart(condition)
+            Truth.assertThat(condition.isConditionMet).isFalse()
+
+            setScreenSaverEnabled(true)
+            runCurrent()
+            val observer = captureSettingsObserver()
+            observer.onChange(/* selfChange= */ false)
+            Truth.assertThat(condition.isConditionMet).isTrue()
+        }
+
+    private fun setScreenSaverEnabled(enabled: Boolean) {
+        whenever(
+                secureSettings.getIntForUser(
+                    eq(Settings.Secure.SCREENSAVER_ENABLED),
+                    any(),
+                    eq(UserHandle.USER_CURRENT),
+                )
+            )
+            .thenReturn(if (enabled) 1 else 0)
+    }
+
+    private fun captureSettingsObserver(): ContentObserver {
+        Mockito.verify(secureSettings)
+            .registerContentObserverForUserSync(
+                eq(Settings.Secure.SCREENSAVER_ENABLED),
+                settingsObserverCaptor.capture(),
+                eq(UserHandle.USER_CURRENT),
+            )
+        return settingsObserverCaptor.lastValue
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 175e8d4..e048d46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -33,8 +33,8 @@
 import android.platform.test.annotations.RequiresFlagsDisabled
 import android.platform.test.annotations.RequiresFlagsEnabled
 import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.platform.test.flag.junit.FlagsParameterization
 import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
 import com.android.settingslib.bluetooth.LocalBluetoothManager
@@ -77,6 +77,8 @@
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
 import org.mockito.kotlin.eq
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 private const val KEY = "TEST_KEY"
 private const val KEY_OLD = "TEST_KEY_OLD"
@@ -89,12 +91,24 @@
 private const val NORMAL_APP_NAME = "NORMAL_APP_NAME"
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @TestableLooper.RunWithLooper
-public class MediaDeviceManagerTest : SysuiTestCase() {
+public class MediaDeviceManagerTest(flags: FlagsParameterization) : SysuiTestCase() {
 
-    private companion object {
+    companion object {
         val OTHER_DEVICE_ICON_STUB = TestStubDrawable()
+
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.progressionOf(
+                com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DEVICE_MANAGER_BACKGROUND_EXECUTION
+            )
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
@@ -187,6 +201,8 @@
     @Test
     fun loadMediaData() {
         manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         verify(lmmFactory).create(PACKAGE)
     }
 
@@ -195,6 +211,7 @@
         manager.onMediaDataLoaded(KEY, null, mediaData)
         manager.onMediaDataRemoved(KEY, false)
         fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         verify(lmm).unregisterCallback(any())
         verify(muteAwaitManager).stopListening()
     }
@@ -406,6 +423,8 @@
         manager.onMediaDataLoaded(KEY, null, mediaData)
         // WHEN the notification is removed
         manager.onMediaDataRemoved(KEY, true)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
         // THEN the listener receives key removed event
         verify(listener).onKeyRemoved(eq(KEY), eq(true))
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index f394c80..8f1d07b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -855,6 +855,20 @@
 
     @Test
     fun contentDescriptionUpdated() {
+        var elapsedTimeDesc: CharSequence? = null
+        var durationDesc: CharSequence? = null
+        val listener =
+            object : SeekBarViewModel.ContentDescriptionListener {
+                override fun onContentDescriptionChanged(
+                    elapsedTimeDescription: CharSequence,
+                    durationDescription: CharSequence,
+                ) {
+                    elapsedTimeDesc = elapsedTimeDescription
+                    durationDesc = durationDescription
+                }
+            }
+        viewModel.setContentDescriptionListener(listener)
+
         // When there is a duration and position
         val duration = (1.5 * 60 * 60 * 1000).toLong()
         val metadata =
@@ -875,9 +889,7 @@
         viewModel.updateController(mockController)
         fakeExecutor.runNextReady()
 
-        // Then the content description is set
-        val result = viewModel.progress.value!!
-
+        // Then the content description listener gets an update
         val expectedProgress =
             MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
                 .formatMeasures(Measure(3, MeasureUnit.SECOND))
@@ -888,7 +900,7 @@
                     Measure(30, MeasureUnit.MINUTE),
                     Measure(0, MeasureUnit.SECOND),
                 )
-        assertThat(result.durationDescription).isEqualTo(expectedDuration)
-        assertThat(result.elapsedTimeDescription).isEqualTo(expectedProgress)
+        assertThat(elapsedTimeDesc).isEqualTo(expectedProgress)
+        assertThat(durationDesc).isEqualTo(expectedDuration)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 205ccea..9ad2235 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -406,11 +406,6 @@
         }
 
         @Override
-        int getHeaderIconSize() {
-            return 10;
-        }
-
-        @Override
         CharSequence getHeaderText() {
             return mHeaderTitle;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index f0902e3..f1bf7c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -50,6 +50,7 @@
 import com.android.settingslib.media.BluetoothMediaDevice;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestCaseExtKt;
 import com.android.systemui.animation.DialogTransitionAnimator;
@@ -152,9 +153,9 @@
                         volumePanelGlobalStateInteractor,
                         mUserTracker);
         mMediaSwitchingController.mLocalMediaManager = mLocalMediaManager;
-        mMediaOutputBroadcastDialog =
-                new MediaOutputBroadcastDialog(
-                        mContext, false, mBroadcastSender, mMediaSwitchingController);
+        mMediaOutputBroadcastDialog = new MediaOutputBroadcastDialog(mContext, false,
+                mBroadcastSender, mMediaSwitchingController, mContext.getMainExecutor(),
+                ThreadUtils.getBackgroundExecutor());
         mMediaOutputBroadcastDialog.show();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index d3ecb3d..420fd6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -53,6 +53,7 @@
 import com.android.settingslib.flags.Flags;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestCaseExtKt;
 import com.android.systemui.animation.DialogTransitionAnimator;
@@ -455,6 +456,8 @@
                 controller,
                 mDialogTransitionAnimator,
                 mUiEventLogger,
+                mContext.getMainExecutor(),
+                ThreadUtils.getBackgroundExecutor(),
                 true);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
index 88c2697..798aa42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
@@ -60,13 +60,13 @@
 import android.os.UserHandle;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.service.notification.StatusBarNotification;
 import android.testing.TestableLooper;
 import android.text.TextUtils;
 import android.view.View;
 
 import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.media.flags.Flags;
@@ -101,6 +101,9 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -108,7 +111,7 @@
 import java.util.concurrent.TimeUnit;
 
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class MediaSwitchingControllerTest extends SysuiTestCase {
     private static final String TEST_DEVICE_1_ID = "test_device_1_id";
@@ -201,6 +204,17 @@
     private MediaDescription mMediaDescription;
     private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.allCombinationsOf(
+                Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION,
+                Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING);
+    }
+
+    public MediaSwitchingControllerTest(FlagsParameterization flags) {
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Before
     public void setUp() {
         mPackageName = mContext.getPackageName();
@@ -260,7 +274,6 @@
         mMediaDevices.add(mMediaDevice1);
         mMediaDevices.add(mMediaDevice2);
 
-
         when(mNearbyDevice1.getMediaRoute2Id()).thenReturn(TEST_DEVICE_1_ID);
         when(mNearbyDevice1.getRangeZone()).thenReturn(NearbyDevice.RANGE_FAR);
         when(mNearbyDevice2.getMediaRoute2Id()).thenReturn(TEST_DEVICE_2_ID);
@@ -689,7 +702,7 @@
 
         mMediaSwitchingController.start(mCb);
         reset(mCb);
-        mMediaSwitchingController.getMediaItemList().clear();
+        mMediaSwitchingController.clearMediaItemList();
         mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
         final List<MediaDevice> devices = new ArrayList<>();
         int dividerSize = 0;
@@ -1528,7 +1541,7 @@
                 .getSelectedMediaDevice();
         mMediaSwitchingController.start(mCb);
         reset(mCb);
-        mMediaSwitchingController.getMediaItemList().clear();
+        mMediaSwitchingController.clearMediaItemList();
 
         mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
 
@@ -1546,7 +1559,7 @@
                 .getSelectedMediaDevice();
         mMediaSwitchingController.start(mCb);
         reset(mCb);
-        mMediaSwitchingController.getMediaItemList().clear();
+        mMediaSwitchingController.clearMediaItemList();
 
         mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
 
@@ -1564,7 +1577,7 @@
                 .getSelectedMediaDevice();
         mMediaSwitchingController.start(mCb);
         reset(mCb);
-        mMediaSwitchingController.getMediaItemList().clear();
+        mMediaSwitchingController.clearMediaItemList();
 
         mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
 
@@ -1573,6 +1586,25 @@
         assertThat(items.get(1).isFirstDeviceInGroup()).isFalse();
     }
 
+    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
+    @Test
+    public void deviceListUpdateWithDifferentDevices_firstSelectedDeviceIsFirstDeviceInGroup() {
+        when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true);
+        doReturn(mMediaDevices)
+                .when(mLocalMediaManager)
+                .getSelectedMediaDevice();
+        mMediaSwitchingController.start(mCb);
+        reset(mCb);
+        mMediaSwitchingController.clearMediaItemList();
+        mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
+        mMediaDevices.clear();
+        mMediaDevices.add(mMediaDevice2);
+        mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
+
+        List<MediaItem> items = mMediaSwitchingController.getMediaItemList();
+        assertThat(items.get(0).isFirstDeviceInGroup()).isTrue();
+    }
+
     private int getNumberOfConnectDeviceButtons() {
         int numberOfConnectDeviceButtons = 0;
         for (MediaItem item : mMediaSwitchingController.getMediaItemList()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/OutputMediaItemListProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/OutputMediaItemListProxyTest.java
new file mode 100644
index 0000000..f6edd49
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/OutputMediaItemListProxyTest.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.media.flags.Flags;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4.class)
+@TestableLooper.RunWithLooper
+public class OutputMediaItemListProxyTest extends SysuiTestCase {
+    private static final String DEVICE_ID_1 = "device_id_1";
+    private static final String DEVICE_ID_2 = "device_id_2";
+    private static final String DEVICE_ID_3 = "device_id_3";
+    private static final String DEVICE_ID_4 = "device_id_4";
+    @Mock private MediaDevice mMediaDevice1;
+    @Mock private MediaDevice mMediaDevice2;
+    @Mock private MediaDevice mMediaDevice3;
+    @Mock private MediaDevice mMediaDevice4;
+
+    private MediaItem mMediaItem1;
+    private MediaItem mMediaItem2;
+    private MediaItem mConnectNewDeviceMediaItem;
+    private OutputMediaItemListProxy mOutputMediaItemListProxy;
+
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.allCombinationsOf(
+                Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION,
+                Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING);
+    }
+
+    public OutputMediaItemListProxyTest(FlagsParameterization flags) {
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mMediaDevice1.getId()).thenReturn(DEVICE_ID_1);
+        when(mMediaDevice2.getId()).thenReturn(DEVICE_ID_2);
+        when(mMediaDevice2.isSuggestedDevice()).thenReturn(true);
+        when(mMediaDevice3.getId()).thenReturn(DEVICE_ID_3);
+        when(mMediaDevice4.getId()).thenReturn(DEVICE_ID_4);
+        mMediaItem1 = MediaItem.createDeviceMediaItem(mMediaDevice1);
+        mMediaItem2 = MediaItem.createDeviceMediaItem(mMediaDevice2);
+        mConnectNewDeviceMediaItem = MediaItem.createPairNewDeviceMediaItem();
+
+        mOutputMediaItemListProxy = new OutputMediaItemListProxy(mContext);
+    }
+
+    @EnableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+    @Test
+    public void updateMediaDevices_shouldUpdateMediaItemList() {
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+        // Create the initial output media item list with mMediaDevice2 and mMediaDevice3.
+        mOutputMediaItemListProxy.updateMediaDevices(
+                /* devices= */ List.of(mMediaDevice2, mMediaDevice3),
+                /* selectedDevices */ List.of(mMediaDevice3),
+                /* connectedMediaDevice= */ null,
+                /* needToHandleMutingExpectedDevice= */ false,
+                /* connectNewDeviceMediaItem= */ null);
+
+        // Check the output media items to be
+        //     * a media item with the selected mMediaDevice3
+        //     * a group divider for suggested devices
+        //     * a media item with the mMediaDevice2
+        assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                .containsExactly(mMediaDevice3, null, mMediaDevice2);
+        assertThat(mOutputMediaItemListProxy.getOutputMediaItemList().get(0).isFirstDeviceInGroup())
+                .isEqualTo(Flags.enableOutputSwitcherDeviceGrouping());
+
+        // Update the output media item list with more media devices.
+        mOutputMediaItemListProxy.updateMediaDevices(
+                /* devices= */ List.of(mMediaDevice4, mMediaDevice1, mMediaDevice3, mMediaDevice2),
+                /* selectedDevices */ List.of(mMediaDevice3),
+                /* connectedMediaDevice= */ null,
+                /* needToHandleMutingExpectedDevice= */ false,
+                /* connectNewDeviceMediaItem= */ null);
+
+        // Check the output media items to be
+        //     * a media item with the selected route mMediaDevice3
+        //     * a group divider for suggested devices
+        //     * a media item with the route mMediaDevice2
+        //     * a group divider for speakers and displays
+        //     * a media item with the route mMediaDevice4
+        //     * a media item with the route mMediaDevice1
+        assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                .containsExactly(
+                        mMediaDevice3, null, mMediaDevice2, null, mMediaDevice4, mMediaDevice1);
+        assertThat(mOutputMediaItemListProxy.getOutputMediaItemList().get(0).isFirstDeviceInGroup())
+                .isEqualTo(Flags.enableOutputSwitcherDeviceGrouping());
+
+        // Update the output media item list where mMediaDevice4 is offline and new selected device.
+        mOutputMediaItemListProxy.updateMediaDevices(
+                /* devices= */ List.of(mMediaDevice1, mMediaDevice3, mMediaDevice2),
+                /* selectedDevices */ List.of(mMediaDevice1, mMediaDevice3),
+                /* connectedMediaDevice= */ null,
+                /* needToHandleMutingExpectedDevice= */ false,
+                /* connectNewDeviceMediaItem= */ null);
+
+        // Check the output media items to be
+        //     * a media item with the selected route mMediaDevice3
+        //     * a group divider for suggested devices
+        //     * a media item with the route mMediaDevice2
+        //     * a group divider for speakers and displays
+        //     * a media item with the route mMediaDevice1
+        assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                .containsExactly(mMediaDevice3, null, mMediaDevice2, null, mMediaDevice1);
+        assertThat(mOutputMediaItemListProxy.getOutputMediaItemList().get(0).isFirstDeviceInGroup())
+                .isEqualTo(Flags.enableOutputSwitcherDeviceGrouping());
+    }
+
+    @EnableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+    @Test
+    public void updateMediaDevices_multipleSelectedDevices_shouldHaveCorrectDeviceOrdering() {
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+        // Create the initial output media item list with mMediaDevice2 and mMediaDevice3.
+        mOutputMediaItemListProxy.updateMediaDevices(
+                /* devices= */ List.of(mMediaDevice2, mMediaDevice4, mMediaDevice3, mMediaDevice1),
+                /* selectedDevices */ List.of(mMediaDevice1, mMediaDevice2, mMediaDevice3),
+                /* connectedMediaDevice= */ null,
+                /* needToHandleMutingExpectedDevice= */ false,
+                /* connectNewDeviceMediaItem= */ null);
+
+        if (Flags.enableOutputSwitcherDeviceGrouping()) {
+            // When the device grouping is enabled, the order of selected devices are preserved:
+            //     * a media item with the selected mMediaDevice2
+            //     * a media item with the selected mMediaDevice3
+            //     * a media item with the selected mMediaDevice1
+            //     * a group divider for speakers and displays
+            //     * a media item with the mMediaDevice4
+            assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                    .containsExactly(
+                            mMediaDevice2, mMediaDevice3, mMediaDevice1, null, mMediaDevice4);
+            assertThat(
+                            mOutputMediaItemListProxy
+                                    .getOutputMediaItemList()
+                                    .get(0)
+                                    .isFirstDeviceInGroup())
+                    .isTrue();
+        } else {
+            // When the device grouping is disabled, the order of selected devices are reverted:
+            //     * a media item with the selected mMediaDevice1
+            //     * a media item with the selected mMediaDevice3
+            //     * a media item with the selected mMediaDevice2
+            //     * a group divider for speakers and displays
+            //     * a media item with the mMediaDevice4
+            assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                    .containsExactly(
+                            mMediaDevice1, mMediaDevice3, mMediaDevice2, null, mMediaDevice4);
+        }
+
+        // Update the output media item list with a selected device being deselected.
+        mOutputMediaItemListProxy.updateMediaDevices(
+                /* devices= */ List.of(mMediaDevice4, mMediaDevice1, mMediaDevice3, mMediaDevice2),
+                /* selectedDevices */ List.of(mMediaDevice2, mMediaDevice3),
+                /* connectedMediaDevice= */ null,
+                /* needToHandleMutingExpectedDevice= */ false,
+                /* connectNewDeviceMediaItem= */ null);
+
+        if (Flags.enableOutputSwitcherDeviceGrouping()) {
+            // When the device grouping is enabled, the order of selected devices are preserved:
+            //     * a media item with the selected mMediaDevice2
+            //     * a media item with the selected mMediaDevice3
+            //     * a media item with the selected mMediaDevice1
+            //     * a group divider for speakers and displays
+            //     * a media item with the mMediaDevice4
+            assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                    .containsExactly(
+                            mMediaDevice2, mMediaDevice3, mMediaDevice1, null, mMediaDevice4);
+            assertThat(
+                            mOutputMediaItemListProxy
+                                    .getOutputMediaItemList()
+                                    .get(0)
+                                    .isFirstDeviceInGroup())
+                    .isTrue();
+        } else {
+            // When the device grouping is disabled, the order of selected devices are reverted:
+            //     * a media item with the selected mMediaDevice1
+            //     * a media item with the selected mMediaDevice3
+            //     * a media item with the selected mMediaDevice2
+            //     * a group divider for speakers and displays
+            //     * a media item with the mMediaDevice4
+            assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                    .containsExactly(
+                            mMediaDevice1, mMediaDevice3, mMediaDevice2, null, mMediaDevice4);
+        }
+
+        // Update the output media item list with a selected device is missing.
+        mOutputMediaItemListProxy.updateMediaDevices(
+                /* devices= */ List.of(mMediaDevice1, mMediaDevice3, mMediaDevice4),
+                /* selectedDevices */ List.of(mMediaDevice3),
+                /* connectedMediaDevice= */ null,
+                /* needToHandleMutingExpectedDevice= */ false,
+                /* connectNewDeviceMediaItem= */ null);
+
+        if (Flags.enableOutputSwitcherDeviceGrouping()) {
+            // When the device grouping is enabled, the order of selected devices are preserved:
+            //     * a media item with the selected mMediaDevice3
+            //     * a media item with the selected mMediaDevice1
+            //     * a group divider for speakers and displays
+            //     * a media item with the mMediaDevice4
+            assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                    .containsExactly(mMediaDevice3, mMediaDevice1, null, mMediaDevice4);
+            assertThat(
+                            mOutputMediaItemListProxy
+                                    .getOutputMediaItemList()
+                                    .get(0)
+                                    .isFirstDeviceInGroup())
+                    .isTrue();
+        } else {
+            // When the device grouping is disabled, the order of selected devices are reverted:
+            //     * a media item with the selected mMediaDevice1
+            //     * a media item with the selected mMediaDevice3
+            //     * a group divider for speakers and displays
+            //     * a media item with the mMediaDevice4
+            assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                    .containsExactly(mMediaDevice1, mMediaDevice3, null, mMediaDevice4);
+        }
+    }
+
+    @EnableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+    @Test
+    public void updateMediaDevices_withConnectNewDeviceMediaItem_shouldUpdateMediaItemList() {
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+        // Create the initial output media item list with a connect new device media item.
+        mOutputMediaItemListProxy.updateMediaDevices(
+                /* devices= */ List.of(mMediaDevice2, mMediaDevice3),
+                /* selectedDevices */ List.of(mMediaDevice3),
+                /* connectedMediaDevice= */ null,
+                /* needToHandleMutingExpectedDevice= */ false,
+                mConnectNewDeviceMediaItem);
+
+        // Check the output media items to be
+        //     * a media item with the selected mMediaDevice3
+        //     * a group divider for suggested devices
+        //     * a media item with the mMediaDevice2
+        //     * a connect new device media item
+        assertThat(mOutputMediaItemListProxy.getOutputMediaItemList())
+                .contains(mConnectNewDeviceMediaItem);
+        assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                .containsExactly(mMediaDevice3, null, mMediaDevice2, null);
+
+        // Update the output media item list without a connect new device media item.
+        mOutputMediaItemListProxy.updateMediaDevices(
+                /* devices= */ List.of(mMediaDevice2, mMediaDevice3),
+                /* selectedDevices */ List.of(mMediaDevice3),
+                /* connectedMediaDevice= */ null,
+                /* needToHandleMutingExpectedDevice= */ false,
+                /* connectNewDeviceMediaItem= */ null);
+
+        // Check the output media items to be
+        //     * a media item with the selected mMediaDevice3
+        //     * a group divider for suggested devices
+        //     * a media item with the mMediaDevice2
+        assertThat(mOutputMediaItemListProxy.getOutputMediaItemList())
+                .doesNotContain(mConnectNewDeviceMediaItem);
+        assertThat(getMediaDevices(mOutputMediaItemListProxy.getOutputMediaItemList()))
+                .containsExactly(mMediaDevice3, null, mMediaDevice2);
+    }
+
+    @DisableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+    @Test
+    public void clearAndAddAll_shouldUpdateMediaItemList() {
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+        mOutputMediaItemListProxy.clearAndAddAll(List.of(mMediaItem1));
+        assertThat(mOutputMediaItemListProxy.getOutputMediaItemList()).containsExactly(mMediaItem1);
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+
+        mOutputMediaItemListProxy.clearAndAddAll(List.of(mMediaItem2));
+        assertThat(mOutputMediaItemListProxy.getOutputMediaItemList()).containsExactly(mMediaItem2);
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+    }
+
+    @EnableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+    @Test
+    public void clear_flagOn_shouldClearMediaItemList() {
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+        mOutputMediaItemListProxy.updateMediaDevices(
+                /* devices= */ List.of(mMediaDevice1),
+                /* selectedDevices */ List.of(),
+                /* connectedMediaDevice= */ null,
+                /* needToHandleMutingExpectedDevice= */ false,
+                /* connectNewDeviceMediaItem= */ null);
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+
+        mOutputMediaItemListProxy.clear();
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+    }
+
+    @DisableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+    @Test
+    public void clear_flagOff_shouldClearMediaItemList() {
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+        mOutputMediaItemListProxy.clearAndAddAll(List.of(mMediaItem1));
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+
+        mOutputMediaItemListProxy.clear();
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+    }
+
+    @EnableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+    @Test
+    public void removeMutingExpectedDevices_flagOn_shouldClearMediaItemList() {
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+        mOutputMediaItemListProxy.updateMediaDevices(
+                /* devices= */ List.of(mMediaDevice1),
+                /* selectedDevices */ List.of(),
+                /* connectedMediaDevice= */ null,
+                /* needToHandleMutingExpectedDevice= */ false,
+                /* connectNewDeviceMediaItem= */ null);
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+
+        mOutputMediaItemListProxy.removeMutingExpectedDevices();
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+    }
+
+    @DisableFlags(Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION)
+    @Test
+    public void removeMutingExpectedDevices_flagOff_shouldClearMediaItemList() {
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isTrue();
+
+        mOutputMediaItemListProxy.clearAndAddAll(List.of(mMediaItem1));
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+
+        mOutputMediaItemListProxy.removeMutingExpectedDevices();
+        assertThat(mOutputMediaItemListProxy.getOutputMediaItemList()).containsExactly(mMediaItem1);
+        assertThat(mOutputMediaItemListProxy.isEmpty()).isFalse();
+    }
+
+    private List<MediaDevice> getMediaDevices(List<MediaItem> mediaItems) {
+        return mediaItems.stream()
+                .map(item -> item.getMediaDevice().orElse(null))
+                .collect(Collectors.toList());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index 92b26ea..7e42ec7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -30,6 +30,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -55,19 +56,21 @@
         listState: EditTileListState,
         onSetTiles: (List<TileSpec>) -> Unit,
     ) {
-        DefaultEditTileGrid(
-            listState = listState,
-            otherTiles = listOf(),
-            columns = 4,
-            largeTilesSpan = 4,
-            modifier = Modifier.fillMaxSize(),
-            onAddTile = {},
-            onRemoveTile = {},
-            onSetTiles = onSetTiles,
-            onResize = { _, _ -> },
-            onStopEditing = {},
-            onReset = null,
-        )
+        PlatformTheme {
+            DefaultEditTileGrid(
+                listState = listState,
+                otherTiles = listOf(),
+                columns = 4,
+                largeTilesSpan = 4,
+                modifier = Modifier.fillMaxSize(),
+                onAddTile = {},
+                onRemoveTile = {},
+                onSetTiles = onSetTiles,
+                onResize = { _, _ -> },
+                onStopEditing = {},
+                onReset = null,
+            )
+        }
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
index e76be82c..9d4a425 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
@@ -33,6 +33,7 @@
 import androidx.compose.ui.text.AnnotatedString
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -56,19 +57,22 @@
         var tiles by remember { mutableStateOf(TestEditTiles) }
         val (currentTiles, otherTiles) = tiles.partition { it.tile.isCurrent }
         val listState = EditTileListState(currentTiles, columns = 4, largeTilesSpan = 2)
-        DefaultEditTileGrid(
-            listState = listState,
-            otherTiles = otherTiles,
-            columns = 4,
-            largeTilesSpan = 4,
-            modifier = Modifier.fillMaxSize(),
-            onAddTile = { tiles = tiles.add(it) },
-            onRemoveTile = { tiles = tiles.remove(it) },
-            onSetTiles = {},
-            onResize = { _, _ -> },
-            onStopEditing = {},
-            onReset = null,
-        )
+
+        PlatformTheme {
+            DefaultEditTileGrid(
+                listState = listState,
+                otherTiles = otherTiles,
+                columns = 4,
+                largeTilesSpan = 4,
+                modifier = Modifier.fillMaxSize(),
+                onAddTile = { tiles = tiles.add(it) },
+                onRemoveTile = { tiles = tiles.remove(it) },
+                onSetTiles = {},
+                onResize = { _, _ -> },
+                onStopEditing = {},
+                onReset = null,
+            )
+        }
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
index 021be32..5e76000 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
@@ -35,6 +35,7 @@
 import androidx.compose.ui.text.AnnotatedString
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.theme.PlatformTheme
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -61,19 +62,21 @@
         listState: EditTileListState,
         onResize: (TileSpec, Boolean) -> Unit,
     ) {
-        DefaultEditTileGrid(
-            listState = listState,
-            otherTiles = listOf(),
-            columns = 4,
-            largeTilesSpan = 4,
-            modifier = Modifier.fillMaxSize(),
-            onAddTile = {},
-            onRemoveTile = {},
-            onSetTiles = {},
-            onResize = onResize,
-            onStopEditing = {},
-            onReset = null,
-        )
+        PlatformTheme {
+            DefaultEditTileGrid(
+                listState = listState,
+                otherTiles = listOf(),
+                columns = 4,
+                largeTilesSpan = 4,
+                modifier = Modifier.fillMaxSize(),
+                onAddTile = {},
+                onRemoveTile = {},
+                onSetTiles = {},
+                onResize = onResize,
+                onStopEditing = {},
+                onReset = null,
+            )
+        }
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index 6f71df5..1b5f032 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.qs.tiles
 
+import android.bluetooth.BluetoothAdapter
 import android.bluetooth.BluetoothDevice
 import android.os.Handler
 import android.os.Looper
@@ -8,11 +9,11 @@
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
+import android.service.quicksettings.Tile
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
-import com.android.internal.telephony.flags.Flags
 import com.android.settingslib.Utils
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.systemui.SysuiTestCase
@@ -82,6 +83,7 @@
         testableLooper = TestableLooper.get(this)
 
         whenever(qsHost.context).thenReturn(mContext)
+        whenever(bluetoothController.canConfigBluetooth()).thenReturn(true)
 
         tile =
             FakeBluetoothTile(
@@ -257,6 +259,38 @@
             .removeOnMetadataChangedListener(eq(cachedDevice), any())
     }
 
+    @Test
+    @EnableFlags(QSComposeFragment.FLAG_NAME)
+    fun disableBluetooth_transientTurningOff() {
+        enableBluetooth()
+        tile.refreshState()
+        testableLooper.processAllMessages()
+
+        tile.handleSecondaryClick(null)
+        testableLooper.processAllMessages()
+
+        val state = tile.state
+
+        assertThat(state.state).isEqualTo(Tile.STATE_INACTIVE)
+        assertThat(state.isTransient).isTrue()
+        assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_bluetooth_icon_off))
+    }
+
+    @Test
+    @EnableFlags(QSComposeFragment.FLAG_NAME)
+    fun turningOffState() {
+        setBluetoothTurningOff()
+
+        tile.refreshState()
+        testableLooper.processAllMessages()
+
+        val state = tile.state
+
+        assertThat(state.state).isEqualTo(Tile.STATE_INACTIVE)
+        assertThat(state.isTransient).isTrue()
+        assertThat(state.icon).isEqualTo(createExpectedIcon(R.drawable.qs_bluetooth_icon_off))
+    }
+
     private class FakeBluetoothTile(
         qsHost: QSHost,
         uiEventLogger: QsEventLogger,
@@ -318,6 +352,13 @@
         whenever(bluetoothController.isBluetoothConnecting).thenReturn(true)
     }
 
+    fun setBluetoothTurningOff() {
+        whenever(bluetoothController.isBluetoothConnected).thenReturn(false)
+        whenever(bluetoothController.isBluetoothConnecting).thenReturn(false)
+        whenever(bluetoothController.isBluetoothEnabled).thenReturn(false)
+        whenever(bluetoothController.bluetoothState).thenReturn(BluetoothAdapter.STATE_TURNING_OFF)
+    }
+
     fun addConnectedDevice(device: CachedBluetoothDevice) {
         whenever(bluetoothController.connectedDevices).thenReturn(listOf(device))
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
index c20a801..356d445 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
@@ -40,9 +40,9 @@
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.wifitrackerlib.WifiEntry
@@ -66,7 +66,7 @@
 @EnableFlags(Flags.FLAG_QS_TILE_DETAILED_VIEW)
 @UiThreadTest
 class InternetDetailsContentManagerTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val handler: Handler = kosmos.fakeExecutorHandler
     private val scope: CoroutineScope = mock<CoroutineScope>()
     private val telephonyManager: TelephonyManager = kosmos.telephonyManager
@@ -103,6 +103,8 @@
         whenever(internetWifiEntry.hasInternetAccess()).thenReturn(true)
         whenever(wifiEntries.size).thenReturn(1)
         whenever(internetDetailsContentController.getDialogTitleText()).thenReturn(TITLE)
+        whenever(internetDetailsContentController.getSubtitleText(ArgumentMatchers.anyBoolean()))
+            .thenReturn("")
         whenever(internetDetailsContentController.getMobileNetworkTitle(ArgumentMatchers.anyInt()))
             .thenReturn(MOBILE_NETWORK_TITLE)
         whenever(
@@ -128,15 +130,13 @@
                 internetDetailsContentController,
                 canConfigMobileData = true,
                 canConfigWifi = true,
-                coroutineScope = scope,
-                context = mContext,
                 uiEventLogger = mock<UiEventLogger>(),
                 handler = handler,
                 backgroundExecutor = bgExecutor,
                 keyguard = keyguard,
             )
 
-        internetDetailsContentManager.bind(contentView)
+        internetDetailsContentManager.bind(contentView, scope)
         internetDetailsContentManager.adapter = internetAdapter
         internetDetailsContentManager.connectedWifiEntry = internetWifiEntry
         internetDetailsContentManager.wifiEntriesCount = wifiEntries.size
@@ -777,6 +777,26 @@
         }
     }
 
+    @Test
+    fun updateTitleAndSubtitle() {
+        assertThat(internetDetailsContentManager.title).isEqualTo("Internet")
+        assertThat(internetDetailsContentManager.subTitle).isEqualTo("")
+
+        whenever(internetDetailsContentController.getDialogTitleText()).thenReturn("New title")
+        whenever(internetDetailsContentController.getSubtitleText(ArgumentMatchers.anyBoolean()))
+            .thenReturn("New subtitle")
+
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(internetDetailsContentManager.title).isEqualTo("New title")
+            assertThat(internetDetailsContentManager.subTitle).isEqualTo("New subtitle")
+        }
+    }
+
     companion object {
         private const val TITLE = "Internet"
         private const val MOBILE_NETWORK_TITLE = "Mobile Title"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
index cf54df8..997cf41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
@@ -22,6 +22,7 @@
 import android.platform.test.annotations.EnableFlags
 import android.view.Display
 import androidx.test.filters.SmallTest
+import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.deviceStateManager
 import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor
@@ -37,6 +38,7 @@
 import kotlinx.coroutines.flow.MutableSharedFlow
 import org.junit.Before
 import org.junit.Test
+import org.mockito.Mockito.times
 import org.mockito.kotlin.any
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
@@ -59,6 +61,7 @@
             fakeRearDisplayStateInteractor,
             kosmos.rearDisplayInnerDialogDelegateFactory,
             kosmos.testScope,
+            kosmos.keyguardUpdateMonitor,
         )
 
     @Before
@@ -96,6 +99,26 @@
             }
         }
 
+    @Test
+    @EnableFlags(FLAG_DEVICE_STATE_RDM_V2)
+    fun testDialogResumesAfterKeyguardGone() =
+        kosmos.runTest {
+            impl.use {
+                it.start()
+                fakeRearDisplayStateInteractor.emitRearDisplay()
+
+                verify(mockDialog).show()
+
+                it.keyguardCallback.onKeyguardVisibilityChanged(true)
+                // Do not need to check that the dialog is dismissed, since SystemUIDialog
+                // implementation handles that. We just toggle keyguard here so that the flow
+                // emits.
+
+                it.keyguardCallback.onKeyguardVisibilityChanged(false)
+                verify(mockDialog, times(2)).show()
+            }
+        }
+
     private class FakeRearDisplayStateInteractor(private val kosmos: Kosmos) :
         RearDisplayStateInteractor {
         private val stateFlow = MutableSharedFlow<RearDisplayStateInteractor.State>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
index 2d3f538..9b03833 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/LauncherProxyServiceTest.kt
@@ -22,6 +22,7 @@
 import android.content.pm.ResolveInfo
 import android.os.PowerManager
 import android.os.UserManager
+import android.platform.test.annotations.EnableFlags
 import android.testing.TestableContext
 import android.testing.TestableLooper
 import android.view.Display
@@ -29,16 +30,23 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.app.AssistUtils
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.display.data.repository.displayRepository
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.assertLogsWtf
+import com.android.systemui.model.fakeSysUIStatePerDisplayRepository
 import com.android.systemui.model.sysUiState
+import com.android.systemui.model.sysUiStateFactory
 import com.android.systemui.navigationbar.NavigationBarController
 import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.navigationbar.views.NavigationBar
 import com.android.systemui.process.ProcessWrapper
 import com.android.systemui.recents.LauncherProxyService.ACTION_QUICKSTEP
 import com.android.systemui.settings.FakeDisplayTracker
@@ -55,18 +63,20 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.wm.shell.back.BackAnimation
 import com.android.wm.shell.sysui.ShellInterface
 import com.google.common.util.concurrent.MoreExecutors
 import java.util.Optional
 import java.util.concurrent.Executor
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.any
@@ -80,6 +90,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -95,8 +106,10 @@
     private val displayTracker = FakeDisplayTracker(mContext)
     private val fakeSystemClock = FakeSystemClock()
     private val sysUiState = kosmos.sysUiState
+    private val sysUiStateFactory = kosmos.sysUiStateFactory
     private val wakefulnessLifecycle =
         WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager)
+    private val sysuiStatePerDisplayRepository = kosmos.fakeSysUIStatePerDisplayRepository
 
     @Mock private lateinit var launcherProxy: ILauncherProxy.Stub
     @Mock private lateinit var packageManager: PackageManager
@@ -148,6 +161,8 @@
 
         // return isSystemUser as true by default.
         `when`(processWrapper.isSystemUser).thenReturn(true)
+        sysuiStatePerDisplayRepository.add(Display.DEFAULT_DISPLAY, sysUiState)
+        runBlocking { kosmos.displayRepository.apply { addDisplay(0) } }
         subject = createLauncherProxyService(context)
     }
 
@@ -221,7 +236,7 @@
         `when`(processWrapper.isSystemUser).thenReturn(false)
         `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(false)
         val spyContext = spy(context)
-        val ops = createLauncherProxyService(spyContext)
+        val ops = assertLogsWtf { createLauncherProxyService(spyContext) }.result
         ops.startConnectionToCurrentUser()
         verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
     }
@@ -243,11 +258,53 @@
         `when`(userManager.isVisibleBackgroundUsersSupported()).thenReturn(true)
         `when`(userManager.isUserForeground()).thenReturn(true)
         val spyContext = spy(context)
-        val ops = createLauncherProxyService(spyContext)
+        val ops = assertLogsWtf { createLauncherProxyService(spyContext) }.result
         ops.startConnectionToCurrentUser()
         verify(spyContext, times(0)).bindServiceAsUser(any(), any(), anyInt(), any())
     }
 
+    @Test
+    fun notifySysUiStateFlagsForAllDisplays_triggersUpdateInAllDisplays() =
+        kosmos.testScope.runTest {
+            kosmos.displayRepository.apply {
+                addDisplay(0)
+                addDisplay(1)
+                addDisplay(2)
+            }
+            kosmos.fakeSysUIStatePerDisplayRepository.apply {
+                add(1, sysUiStateFactory.create(1))
+                add(2, sysUiStateFactory.create(2))
+            }
+            clearInvocations(launcherProxy)
+            subject.notifySysUiStateFlagsForAllDisplays()
+
+            verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(0))
+            verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(1))
+            verify(launcherProxy).onSystemUiStateChanged(anyLong(), eq(2))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun updateSystemUiStateFlags_updatesAllNavBars() =
+        kosmos.testScope.runTest {
+            kosmos.displayRepository.apply {
+                addDisplay(0)
+                addDisplay(1)
+            }
+            kosmos.fakeSysUIStatePerDisplayRepository.apply {
+                add(1, sysUiStateFactory.create(1))
+            }
+            val navBar0 = mock<NavigationBar>()
+            val navBar1 = mock<NavigationBar>()
+            whenever(navBarController.getNavigationBar(eq(0))).thenReturn(navBar0)
+            whenever(navBarController.getNavigationBar(eq(1))).thenReturn(navBar1)
+
+            subject.updateSystemUiStateFlags()
+
+            verify(navBar0).updateSystemUiStateFlags()
+            verify(navBar1).updateSystemUiStateFlags()
+        }
+
     private fun createLauncherProxyService(ctx: Context): LauncherProxyService {
         return LauncherProxyService(
             ctx,
@@ -259,7 +316,7 @@
             screenPinningRequest,
             navModeController,
             statusBarWinController,
-            sysUiState,
+            kosmos.fakeSysUIStatePerDisplayRepository,
             mock(),
             mock(),
             userTracker,
@@ -275,6 +332,7 @@
             broadcastDispatcher,
             backAnimation,
             processWrapper,
+            kosmos.displayRepository,
         )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
new file mode 100644
index 0000000..826c547
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ringtone/RingtonePlayerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ringtone;
+
+import static org.junit.Assert.assertThrows;
+
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RingtonePlayerTest extends SysuiTestCase {
+
+    private static final String TAG = "RingtonePlayerTest";
+
+    @Test
+    public void testRingtonePlayerUriUserCheck() {
+        // temporarily skipping this test
+        Log.i(TAG, "skipping testRingtonePlayerUriUserCheck");
+        return;
+
+        // TODO change how IRingtonePlayer is created
+//        android.media.IRingtonePlayer irp = mAudioManager.getRingtonePlayer();
+//        final AudioAttributes aa = new AudioAttributes.Builder()
+//                .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
+//        // get a UserId that doesn't belong to mine
+//        final int otherUserId = UserHandle.myUserId() == 0 ? 10 : 0;
+//        // build a URI that I shouldn't have access to
+//        final Uri uri = new Uri.Builder()
+//                .scheme("content").authority(otherUserId + "@media")
+//                .appendPath("external").appendPath("downloads")
+//                .appendPath("bogusPathThatDoesNotMatter.mp3")
+//                .build();
+//        if (android.media.audio.Flags.ringtoneUserUriCheck()) {
+//            assertThrows(SecurityException.class, () ->
+//                    irp.play(new Binder(), uri, aa, 1.0f /*volume*/, false /*looping*/)
+//            );
+//
+//            assertThrows(SecurityException.class, () ->
+//                    irp.getTitle(uri));
+//        }
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index a64ff321..11aaeef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -134,10 +134,7 @@
         val frame = activityRule.activity.requireViewById<View>(viewId)
 
         val lp = frame.layoutParams as ViewGroup.MarginLayoutParams
-        val horizontalMargin =
-            activityRule.activity.resources.getDimensionPixelSize(
-                R.dimen.notification_side_paddings
-            )
+        val horizontalMargin = 0
         assertThat(lp.leftMargin).isEqualTo(horizontalMargin)
         assertThat(lp.rightMargin).isEqualTo(horizontalMargin)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 51bb38f..f72645e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -51,6 +51,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.assertLogsWtf
 import com.android.systemui.qs.flags.QSComposeFragment
 import com.android.systemui.res.R
 import com.android.systemui.scene.ui.view.WindowRootViewKeyEventHandler
@@ -413,7 +414,9 @@
 
             // THEN move is ignored, down is handled, and window is notified
             assertThat(interactionEventHandler.handleDispatchTouchEvent(MOVE_EVENT)).isFalse()
-            assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue()
+            assertLogsWtf {
+                assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue()
+            }
             verify(notificationShadeWindowController).setLaunchingActivity(false)
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
index 116a2ca..e1e9aa3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
@@ -35,8 +35,7 @@
 @RunWith(AndroidJUnit4::class)
 class CombinedConditionTest : SysuiTestCase() {
 
-    class FakeCondition
-    constructor(
+    class FakeCondition(
         scope: CoroutineScope,
         initialValue: Boolean?,
         overriding: Boolean = false,
@@ -46,7 +45,7 @@
         val started: Boolean
             get() = _started
 
-        override fun start() {
+        override suspend fun start() {
             _started = true
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index b7040ee..020b5dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.After
@@ -61,10 +62,6 @@
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
 
-private fun <T> anyObject(): T {
-    return Mockito.anyObject<T>()
-}
-
 @SmallTest
 @RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidJUnit4::class)
@@ -265,7 +262,7 @@
             verify(statusbarStateController, never()).setState(anyInt())
             verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
             verify(centralSurfaces)
-                .showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+                .showBouncerWithDimissAndCancelIfKeyguard(nullable(), nullable())
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 81213ca..3570cf8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -77,7 +77,6 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -1795,7 +1794,7 @@
         invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
 
         // THEN an exception is NOT thrown directly, but a WTF IS logged.
-        LogAssertKt.assertLogsWtfs(() -> {
+        LogAssertKt.assertRunnableLogsWtfs(() -> {
             dispatchBuild();
             runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
         });
@@ -1818,7 +1817,7 @@
         addNotif(0, PACKAGE_2);
         invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
 
-        LogAssertKt.assertLogsWtfs(() -> {
+        LogAssertKt.assertRunnableLogsWtfs(() -> {
             Assert.assertThrows(IllegalStateException.class, () -> {
                 dispatchBuild();
                 runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
@@ -1844,13 +1843,13 @@
         addNotif(0, PACKAGE_2);
 
         invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
-        LogAssertKt.assertLogsWtfs(() -> {
+        LogAssertKt.assertRunnableLogsWtfs(() -> {
             dispatchBuild();
             runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
         });
 
         invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
-        LogAssertKt.assertLogsWtfs(() -> {
+        LogAssertKt.assertRunnableLogsWtfs(() -> {
             // Note: dispatchBuild itself triggers a non-reentrant pipeline run.
             dispatchBuild();
             runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
@@ -1874,7 +1873,7 @@
         addNotif(0, PACKAGE_1);
         invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
 
-        LogAssertKt.assertLogsWtfs(() -> {
+        LogAssertKt.assertRunnableLogsWtfs(() -> {
             dispatchBuild();
             runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
         });
@@ -1897,7 +1896,7 @@
         addNotif(0, PACKAGE_1);
         invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
 
-        LogAssertKt.assertLogsWtfs(() -> {
+        LogAssertKt.assertRunnableLogsWtfs(() -> {
             Assert.assertThrows(IllegalStateException.class, () -> {
                 dispatchBuild();
                 runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
@@ -1922,7 +1921,7 @@
         addNotif(0, PACKAGE_2);
         invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
 
-        LogAssertKt.assertLogsWtfs(() -> {
+        LogAssertKt.assertRunnableLogsWtfs(() -> {
             dispatchBuild();
             runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
         });
@@ -1945,7 +1944,7 @@
         addNotif(0, PACKAGE_2);
         invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
 
-        LogAssertKt.assertLogsWtfs(() -> {
+        LogAssertKt.assertRunnableLogsWtfs(() -> {
             Assert.assertThrows(IllegalStateException.class, () -> {
                 dispatchBuild();
                 runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
@@ -1970,7 +1969,7 @@
         addNotif(0, PACKAGE_2);
         invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS);
 
-        LogAssertKt.assertLogsWtfs(() -> {
+        LogAssertKt.assertRunnableLogsWtfs(() -> {
             dispatchBuild();
             runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
         });
@@ -1993,7 +1992,7 @@
         addNotif(0, PACKAGE_2);
         invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
 
-        LogAssertKt.assertLogsWtfs(() -> {
+        LogAssertKt.assertRunnableLogsWtfs(() -> {
             Assert.assertThrows(IllegalStateException.class, () -> {
                 dispatchBuild();
                 runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
index 3937d3d46..ff17a36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -97,7 +96,6 @@
     fun themeChangePropagatesToEntry() {
         configurationListener.onThemeChanged()
         verify(entry).onDensityOrFontScaleChanged()
-        checkGutsExposedCalled()
         verifyNoMoreInteractions(entry, row)
     }
 
@@ -105,7 +103,6 @@
     fun densityChangePropagatesToEntry() {
         configurationListener.onDensityOrFontScaleChanged()
         verify(entry).onDensityOrFontScaleChanged()
-        checkGutsExposedCalled()
         verifyNoMoreInteractions(entry, row)
     }
 
@@ -129,7 +126,6 @@
         verify(entry).row
         verify(row).onUiModeChanged()
         verify(entry).onDensityOrFontScaleChanged()
-        checkGutsExposedCalled()
         verifyNoMoreInteractions(entry, row)
         clearInvocations(entry, row)
 
@@ -160,7 +156,6 @@
         verify(entry).row
         verify(row).onUiModeChanged()
         verify(entry).onDensityOrFontScaleChanged()
-        checkGutsExposedCalled()
         verifyNoMoreInteractions(entry, row)
         clearInvocations(entry, row)
 
@@ -196,14 +191,7 @@
         verify(entry).row
         verify(row).onUiModeChanged()
         verify(entry).onDensityOrFontScaleChanged()
-        checkGutsExposedCalled()
         verifyNoMoreInteractions(entry, row)
         clearInvocations(entry, row)
     }
-
-    private fun checkGutsExposedCalled() {
-        if (!Flags.notificationUndoGutsOnConfigChanged()) {
-            verify(entry).areGutsExposed()
-        }
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index cd2ea7d..2ea4e7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -18,6 +18,7 @@
 
 import static android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES;
 
+import static com.android.systemui.log.LogAssertKt.assertRunnableLogsWtf;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
 import static com.android.systemui.statusbar.notification.row.NotificationTestHelper.PKG;
 import static com.android.systemui.statusbar.notification.row.NotificationTestHelper.USER_HANDLE;
@@ -60,6 +61,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.widget.CachingIconView;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestableContext;
@@ -70,11 +72,18 @@
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.notification.FeedbackIcon;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryAdapter;
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
 import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
 import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
 import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
 import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
@@ -171,6 +180,7 @@
     }
 
     @Test
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void testUpdateBackgroundColors_isRecursive() throws Exception {
         ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
         group.setTintColor(Color.RED);
@@ -595,14 +605,14 @@
     public void testGetIsNonblockable() throws Exception {
         ExpandableNotificationRow row =
                 mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
-        row.setEntry(null);
+        row.setEntryLegacy(null);
 
         assertTrue(row.getIsNonblockable());
 
         NotificationEntry entry = mock(NotificationEntry.class);
 
         Mockito.doReturn(false, true).when(entry).isBlockable();
-        row.setEntry(entry);
+        row.setEntryLegacy(entry);
         assertTrue(row.getIsNonblockable());
         assertFalse(row.getIsNonblockable());
     }
@@ -936,11 +946,15 @@
     }
 
     @Test
-    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_sensitivePromotedNotification_notExpanded() throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setPromotedOngoing(true);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.isPromotedOngoing()).thenReturn(true);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
         row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true);
 
@@ -949,11 +963,15 @@
     }
 
     @Test
-    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_promotedNotificationNotOnKeyguard_expanded() throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setPromotedOngoing(true);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.isPromotedOngoing()).thenReturn(true);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setOnKeyguard(false);
 
         // THEN
@@ -961,11 +979,15 @@
     }
 
     @Test
-    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_promotedNotificationAllowOnKeyguard_expanded() throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setPromotedOngoing(true);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.isPromotedOngoing()).thenReturn(true);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setOnKeyguard(true);
 
         // THEN
@@ -973,12 +995,16 @@
     }
 
     @Test
-    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_promotedNotificationIgnoreLockscreenConstraints_expanded()
             throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setPromotedOngoing(true);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.isPromotedOngoing()).thenReturn(true);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setOnKeyguard(true);
         row.setIgnoreLockscreenConstraints(true);
 
@@ -986,13 +1012,35 @@
         assertThat(row.isExpanded()).isTrue();
     }
 
+    private static void setRowPromotedOngoing(ExpandableNotificationRow row) {
+        final NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.isPromotedOngoing()).thenReturn(true);
+        if (NotificationBundleUi.isEnabled()) {
+            final EntryAdapter entryAdapter = new NotificationEntryAdapter(
+                    mock(NotificationActivityStarter.class),
+                    mock(MetricsLogger.class),
+                    mock(PeopleNotificationIdentifier.class),
+                    mock(NotificationIconStyleProvider.class),
+                    mock(VisualStabilityCoordinator.class),
+                    mock(NotificationActionClickManager.class),
+                    entry);
+            row.setEntryAdapter(entryAdapter);
+        } else {
+            row.setEntryLegacy(entry);
+        }
+    }
+
     @Test
-    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_promotedNotificationSaveSpaceOnLockScreen_notExpanded()
             throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setPromotedOngoing(true);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.isPromotedOngoing()).thenReturn(true);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setOnKeyguard(true);
         row.setSaveSpaceOnLockscreen(true);
 
@@ -1001,12 +1049,16 @@
     }
 
     @Test
-    @EnableFlags(PromotedNotificationUiForceExpanded.FLAG_NAME)
+    @EnableFlags({PromotedNotificationUi.FLAG_NAME, PromotedNotificationUiForceExpanded.FLAG_NAME})
+    @DisableFlags(NotificationBundleUi.FLAG_NAME)
     public void isExpanded_promotedNotificationNotSaveSpaceOnLockScreen_expanded()
             throws Exception {
         // GIVEN
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-        row.setPromotedOngoing(true);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.isPromotedOngoing()).thenReturn(true);
+        row.setEntryLegacy(entry);
+        setRowPromotedOngoing(row);
         row.setOnKeyguard(true);
         row.setSaveSpaceOnLockscreen(false);
 
@@ -1134,7 +1186,7 @@
     public void hasStatusBarChipDuringHeadsUpAnimation_flagOff_false() throws Exception {
         final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
 
-        row.setHasStatusBarChipDuringHeadsUpAnimation(true);
+        assertRunnableLogsWtf(() -> row.setHasStatusBarChipDuringHeadsUpAnimation(true));
 
         assertThat(row.hasStatusBarChipDuringHeadsUpAnimation()).isFalse();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index c874bc6..82082cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
 import android.annotation.DimenRes
 import android.content.res.Resources
 import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
 import android.service.notification.StatusBarNotification
 import android.testing.TestableLooper
 import android.testing.ViewUtils
@@ -37,9 +38,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.notification.FeedbackIcon
 import com.android.systemui.statusbar.notification.collection.EntryAdapter
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactory
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.msgStyleBubbleableFullPerson
+import com.android.systemui.statusbar.notification.people.peopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -50,7 +55,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.anyInt
@@ -66,10 +70,12 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class NotificationContentViewTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
+    private val factory: EntryAdapterFactory = kosmos.entryAdapterFactory
 
     private lateinit var row: ExpandableNotificationRow
     private lateinit var fakeParent: ViewGroup
-    @Mock private lateinit var mPeopleNotificationIdentifier: PeopleNotificationIdentifier
 
     private val testableResources = mContext.getOrCreateTestableResources()
     private val contractedHeight =
@@ -81,25 +87,19 @@
     fun setup() {
         initMocks(this)
         fakeParent =
-            spy(FrameLayout(mContext, /* attrs= */ null).also { it.visibility = View.GONE })
-        val mockEntry = createMockNotificationEntry()
-        val mockEntryAdapter = createMockNotificationEntryAdapter()
+            spy(FrameLayout(mContext, /* attrs= */ null)).also { it.visibility = View.GONE }
+        val entry = kosmos.msgStyleBubbleableFullPerson
+        val mockEntryAdapter = factory.create(entry)
         row =
             spy(
                 when (NotificationBundleUi.isEnabled) {
                     true -> {
-                        ExpandableNotificationRow(
-                            mContext,
-                            /* attrs= */ null,
-                            UserHandle.CURRENT
-                        ).apply {
-                            entry = mockEntry
-                            entryAdapter = mockEntryAdapter
-                        }
+                        ExpandableNotificationRow(mContext, /* attrs= */ null, UserHandle.CURRENT)
+                            .apply { entryAdapter = mockEntryAdapter }
                     }
                     false -> {
-                        ExpandableNotificationRow(mContext, /* attrs= */ null, mockEntry).apply {
-                            entry = mockEntry
+                        ExpandableNotificationRow(mContext, /* attrs= */ null, entry).apply {
+                            entryLegacy = entry
                         }
                     }
                 }
@@ -402,14 +402,15 @@
     }
 
     @Test
+    @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
     fun setExpandedChild_notShowBubbleButton_marginTargetBottomMarginShouldNotChange() {
         // Given: bottom margin of actionListMarginTarget is notificationContentMargin
         // Bubble button should not be shown for the given NotificationEntry
-        val mockNotificationEntry = createMockNotificationEntry()
+        val mockNotificationEntry = kosmos.msgStyleBubbleableFullPerson
         val mockContainingNotification = createMockContainingNotification(mockNotificationEntry)
         val actionListMarginTarget =
             spy(createLinearLayoutWithBottomMargin(notificationContentMargin))
-        val mockExpandedChild = createMockExpandedChild(mockNotificationEntry)
+        val mockExpandedChild = createMockExpandedChild()
         whenever(
                 mockExpandedChild.findViewById<LinearLayout>(
                     R.id.notification_action_list_margin_target
@@ -429,14 +430,15 @@
     }
 
     @Test
+    @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
     fun setExpandedChild_showBubbleButton_marginTargetBottomMarginShouldChangeToZero() {
         // Given: bottom margin of actionListMarginTarget is notificationContentMargin
         // Bubble button should be shown for the given NotificationEntry
-        val mockNotificationEntry = createMockNotificationEntry()
+        val mockNotificationEntry = kosmos.msgStyleBubbleableFullPerson
         val mockContainingNotification = createMockContainingNotification(mockNotificationEntry)
         val actionListMarginTarget =
             spy(createLinearLayoutWithBottomMargin(notificationContentMargin))
-        val mockExpandedChild = createMockExpandedChild(mockNotificationEntry)
+        val mockExpandedChild = createMockExpandedChild()
         whenever(
                 mockExpandedChild.findViewById<LinearLayout>(
                     R.id.notification_action_list_margin_target
@@ -458,13 +460,14 @@
     }
 
     @Test
+    @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
     fun onNotificationUpdated_notShowBubbleButton_marginTargetBottomMarginShouldNotChange() {
         // Given: bottom margin of actionListMarginTarget is notificationContentMargin
-        val mockNotificationEntry = createMockNotificationEntry()
+        val mockNotificationEntry = kosmos.msgStyleBubbleableFullPerson
         val mockContainingNotification = createMockContainingNotification(mockNotificationEntry)
         val actionListMarginTarget =
             spy(createLinearLayoutWithBottomMargin(notificationContentMargin))
-        val mockExpandedChild = createMockExpandedChild(mockNotificationEntry)
+        val mockExpandedChild = createMockExpandedChild()
         whenever(
                 mockExpandedChild.findViewById<LinearLayout>(
                     R.id.notification_action_list_margin_target
@@ -479,20 +482,21 @@
 
         // When: call NotificationContentView.onNotificationUpdated() to update the
         // NotificationEntry, which should not show bubble button
-        view.onNotificationUpdated(createMockNotificationEntry())
+        view.onNotificationUpdated(kosmos.msgStyleBubbleableFullPerson)
 
         // Then: bottom margin of actionListMarginTarget should not change, still be 20
         assertEquals(notificationContentMargin, getMarginBottom(actionListMarginTarget))
     }
 
     @Test
+    @DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
     fun onNotificationUpdated_showBubbleButton_marginTargetBottomMarginShouldChangeToZero() {
         // Given: bottom margin of actionListMarginTarget is notificationContentMargin
-        val mockNotificationEntry = createMockNotificationEntry()
+        val mockNotificationEntry = kosmos.msgStyleBubbleableFullPerson
         val mockContainingNotification = createMockContainingNotification(mockNotificationEntry)
         val actionListMarginTarget =
             spy(createLinearLayoutWithBottomMargin(notificationContentMargin))
-        val mockExpandedChild = createMockExpandedChild(mockNotificationEntry)
+        val mockExpandedChild = createMockExpandedChild()
         whenever(
                 mockExpandedChild.findViewById<LinearLayout>(
                     R.id.notification_action_list_margin_target
@@ -506,7 +510,7 @@
 
         // When: call NotificationContentView.onNotificationUpdated() to update the
         // NotificationEntry, which should show bubble button
-        view.onNotificationUpdated(createMockNotificationEntry(/*true*/ ))
+        view.onNotificationUpdated(kosmos.msgStyleBubbleableFullPerson)
 
         // Then: no bubble yet
         assertEquals(notificationContentMargin, getMarginBottom(actionListMarginTarget))
@@ -514,7 +518,7 @@
         // Given: controller says bubbles are enabled for the user
         view.setBubblesEnabledForUser(true)
 
-        // Then: bottom margin of actionListMarginTarget should not change, still be 20
+        // Then: bottom margin of actionListMarginTarget should be changed to 0
         assertEquals(0, getMarginBottom(actionListMarginTarget))
     }
 
@@ -611,15 +615,17 @@
 
     private fun createMockContainingNotification(notificationEntry: NotificationEntry) =
         mock<ExpandableNotificationRow>().apply {
-            whenever(this.entry).thenReturn(notificationEntry)
+            if (!NotificationBundleUi.isEnabled) {
+                whenever(this.entryLegacy).thenReturn(notificationEntry)
+            }
             whenever(this.context).thenReturn(mContext)
             whenever(this.bubbleClickListener).thenReturn(View.OnClickListener {})
-            whenever(this.entryAdapter).thenReturn(createMockNotificationEntryAdapter())
+            whenever(this.entryAdapter).thenReturn(factory.create(notificationEntry))
         }
 
     private fun createMockNotificationEntry() =
         mock<NotificationEntry>().apply {
-            whenever(mPeopleNotificationIdentifier.getPeopleNotificationType(this))
+            whenever(kosmos.peopleNotificationIdentifier.getPeopleNotificationType(this))
                 .thenReturn(PeopleNotificationIdentifier.TYPE_FULL_PERSON)
             whenever(this.bubbleMetadata).thenReturn(mock())
             val sbnMock: StatusBarNotification = mock()
@@ -640,7 +646,7 @@
         return innerLayout
     }
 
-    private fun createMockExpandedChild(notificationEntry: NotificationEntry) =
+    private fun createMockExpandedChild() =
         spy(createViewWithHeight(expandedHeight)).apply {
             whenever(this.findViewById<ImageView>(R.id.bubble_button)).thenReturn(mock())
             whenever(this.findViewById<View>(R.id.actions_container)).thenReturn(mock())
@@ -661,9 +667,16 @@
         val height = if (isSystemExpanded) expandedHeight else contractedHeight
         doReturn(height).whenever(row).intrinsicHeight
 
-        return spy(NotificationContentView(mContext, /* attrs= */ null))
+        return NotificationContentView(mContext, /* attrs= */ null)
             .apply {
-                initialize(mPeopleNotificationIdentifier, mock(), mock(), mock(), mock(), mock())
+                initialize(
+                    kosmos.peopleNotificationIdentifier,
+                    mock(),
+                    mock(),
+                    mock(),
+                    mock(),
+                    mock(),
+                )
                 setContainingNotification(row)
                 setHeights(
                     /* smallHeight= */ contractedHeight,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationCustomContentMemoryVerifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationCustomContentMemoryVerifierTest.java
index 1cadb3c..e1bab8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationCustomContentMemoryVerifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationCustomContentMemoryVerifierTest.java
@@ -46,6 +46,7 @@
 
 import com.android.server.notification.Flags;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.log.LogAssertKt;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 
@@ -173,8 +174,10 @@
     public void satisfiesMemoryLimits_viewWithoutCustomNotificationRoot_returnsTrue() {
         NotificationEntry entry = new NotificationEntryBuilder().build();
         View view = new FrameLayout(mContext);
-        assertThat(NotificationCustomContentMemoryVerifier.satisfiesMemoryLimits(view, entry))
-                .isTrue();
+        LogAssertKt.assertRunnableLogsWtf(() -> {
+            assertThat(NotificationCustomContentMemoryVerifier.satisfiesMemoryLimits(view, entry))
+                    .isTrue();
+        });
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 0c0ef9d..10de866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -67,6 +67,7 @@
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
 import com.android.systemui.statusbar.notification.row.icon.appIconProvider
 import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -146,6 +147,7 @@
     @Mock private lateinit var notificationManager: INotificationManager
     @Mock private lateinit var shortcutManager: ShortcutManager
     @Mock private lateinit var channelEditorDialogController: ChannelEditorDialogController
+    @Mock private lateinit var packageDemotionInteractor: PackageDemotionInteractor
     @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
     @Mock private lateinit var contextTracker: UserContextProvider
     @Mock private lateinit var bubblesManager: BubblesManager
@@ -185,6 +187,7 @@
                 launcherApps,
                 shortcutManager,
                 channelEditorDialogController,
+                packageDemotionInteractor,
                 contextTracker,
                 assistantFeedbackController,
                 Optional.of(bubblesManager),
@@ -297,45 +300,6 @@
     }
 
     @Test
-    fun testChangeDensityOrFontScale() {
-        val guts = spy(NotificationGuts(mContext))
-        whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
-            handler.post((invocation.arguments[0] as Runnable))
-            null
-        }
-
-        // Test doesn't support animation since the guts view is not attached.
-        doNothing()
-            .whenever(guts)
-            .openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
-        val realRow = createTestNotificationRow()
-        val menuItem = createTestMenuItem(realRow)
-        val row = spy(realRow)
-        whenever(row!!.windowToken).thenReturn(Binder())
-        whenever(row.guts).thenReturn(guts)
-        doNothing().whenever(row).ensureGutsInflated()
-        val realEntry = realRow!!.entry
-        val entry = spy(realEntry)
-        whenever(entry.row).thenReturn(row)
-        whenever(entry.getGuts()).thenReturn(guts)
-        Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
-        executor.runAllReady()
-        verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
-
-        // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
-        verify(row).setGutsView(any())
-        row.onDensityOrFontScaleChanged()
-        gutsManager.onDensityOrFontScaleChanged(entry)
-        executor.runAllReady()
-        gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
-        verify(guts)
-            .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>())
-
-        // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
-        verify(row, times(2)).setGutsView(any())
-    }
-
-    @Test
     fun testAppOpsSettingsIntent_camera() {
         val ops = ArraySet<Int>()
         ops.add(AppOpsManager.OP_CAMERA)
@@ -427,6 +391,7 @@
             .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
             .setImportance(NotificationManager.IMPORTANCE_HIGH)
             .build()
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
         val statusBarNotification = entry.sbn
         gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -438,6 +403,7 @@
                 eq(iconStyleProvider),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                eq(packageDemotionInteractor),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -447,7 +413,8 @@
                 any<UiEventLogger>(),
                 eq(true),
                 eq(false),
-                eq(true), /* wasShownHighPriority */
+                eq(true),
+                eq(true),
                 eq(assistantFeedbackController),
                 any<MetricsLogger>(),
                 any<View.OnClickListener>(),
@@ -462,6 +429,7 @@
         NotificationEntryHelper.modifyRanking(row.entry)
             .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
             .build()
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         val statusBarNotification = row.entry.sbn
         val entry = row.entry
         gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -473,6 +441,7 @@
                 eq(iconStyleProvider),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                eq(packageDemotionInteractor),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -482,7 +451,8 @@
                 any<UiEventLogger>(),
                 eq(true),
                 eq(false),
-                eq(false), /* wasShownHighPriority */
+                eq(true), /* wasShownHighPriority */
+                eq(false),
                 eq(assistantFeedbackController),
                 any<MetricsLogger>(),
                 any<View.OnClickListener>(),
@@ -497,6 +467,7 @@
         NotificationEntryHelper.modifyRanking(row.entry)
             .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
             .build()
+        whenever(row.canViewBeDismissed()).thenReturn(true)
         val statusBarNotification = row.entry.sbn
         val entry = row.entry
         gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -508,6 +479,7 @@
                 eq(iconStyleProvider),
                 eq(onUserInteractionCallback),
                 eq(channelEditorDialogController),
+                eq(packageDemotionInteractor),
                 eq(statusBarNotification.packageName),
                 any<NotificationChannel>(),
                 eq(entry),
@@ -517,7 +489,8 @@
                 any<UiEventLogger>(),
                 eq(true),
                 eq(false),
-                eq(false), /* wasShownHighPriority */
+                eq(true), /* wasShownHighPriority */
+                eq(false),
                 eq(assistantFeedbackController),
                 any<MetricsLogger>(),
                 any<View.OnClickListener>(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 89b7bee..a3616d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -128,6 +128,7 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
+import com.android.systemui.media.NotificationMediaManager;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.notetask.NoteTaskController;
 import com.android.systemui.plugins.ActivityStarter;
@@ -164,7 +165,6 @@
 import com.android.systemui.statusbar.LightRevealScrim;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
index 2e65478..12f7af1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
@@ -108,7 +108,7 @@
     private fun setGoToSleepStates(vararg states: Int) {
         mContext.orCreateTestableResources.addOverride(
             R.array.config_deviceStatesOnWhichToSleep,
-            states
+            states,
         )
     }
 
@@ -117,9 +117,10 @@
     }
 
     companion object {
-        private val DEVICE_STATE_FOLDED = Kosmos().foldedDeviceStateList.first()
-        private val DEVICE_STATE_HALF_FOLDED = Kosmos().halfFoldedDeviceState
-        private val DEVICE_STATE_UNFOLDED = Kosmos().unfoldedDeviceState
+        private val kosmos = Kosmos()
+        private val DEVICE_STATE_FOLDED = kosmos.foldedDeviceStateList.first()
+        private val DEVICE_STATE_HALF_FOLDED = kosmos.halfFoldedDeviceState
+        private val DEVICE_STATE_UNFOLDED = kosmos.unfoldedDeviceState
 
         private const val FOLDED = true
         private const val NOT_FOLDED = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index ffb861d..063b546 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -296,6 +296,7 @@
                 mKosmos.getTestDispatcher(),
                 mLinearLargeScreenShadeInterpolator,
                 new BlurConfig(0.0f, 0.0f),
+                mContext,
                 mKosmos::getWindowRootViewBlurInteractor);
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
@@ -1247,6 +1248,7 @@
                 mKosmos.getTestDispatcher(),
                 mLinearLargeScreenShadeInterpolator,
                 new BlurConfig(0.0f, 0.0f),
+                mContext,
                 mKosmos::getWindowRootViewBlurInteractor);
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
index c22c628..293e1fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
@@ -57,7 +57,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyObject
+import org.mockito.ArgumentMatchers.any
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -138,7 +138,7 @@
         `when`(secureSettings.getInt(Settings.Secure.CONTROLS_ENABLED, 1)).thenReturn(0)
         controller.setCallback(callback)
 
-        verify(controlsListingController, never()).addCallback(anyObject())
+        verify(controlsListingController, never()).addCallback(any())
         verify(callback).onControlsUpdate(null)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index ffe7750..4a0445d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -269,14 +269,14 @@
         when(viewRoot.getOnBackInvokedDispatcher()).thenReturn(backInvokedDispatcher);
         view.setViewRootImpl(viewRoot);
 
-        /* verify that predictive back callback registered when RemoteInputView becomes visible */
-        view.onVisibilityAggregated(true);
+        /* verify that predictive back callback registered when RemoteInputView gains focus */
+        view.focus();
         verify(backInvokedDispatcher).registerOnBackInvokedCallback(
                 eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
                 onBackInvokedCallbackCaptor.capture());
 
-        /* verify that same callback unregistered when RemoteInputView becomes invisible */
-        view.onVisibilityAggregated(false);
+        /* verify that same callback unregistered when RemoteInputView loses focus */
+        view.onDefocus(false, false, null);
         verify(backInvokedDispatcher).unregisterOnBackInvokedCallback(
                 eq(onBackInvokedCallbackCaptor.getValue()));
     }
@@ -299,13 +299,12 @@
         view.onVisibilityAggregated(true);
         view.setEditTextReferenceToSelf();
 
+        view.focus();
         /* capture the callback during registration */
         verify(backInvokedDispatcher).registerOnBackInvokedCallback(
                 eq(OnBackInvokedDispatcher.PRIORITY_OVERLAY),
                 onBackInvokedCallbackCaptor.capture());
 
-        view.focus();
-
         /* invoke the captured callback */
         onBackInvokedCallbackCaptor.getValue().onBackInvoked();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index a553b17..6618843 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -26,9 +26,8 @@
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.foldedDeviceStateList
-import com.android.systemui.halfFoldedDeviceState
 import com.android.systemui.keyguard.ScreenLifecycle
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.testKosmos
 import com.android.systemui.unfold.util.FoldableDeviceStates
 import com.android.systemui.unfold.util.FoldableTestUtils
 import com.android.systemui.unfoldedDeviceState
@@ -70,12 +69,10 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        whenever(deviceStateManager.supportedDeviceStates).thenReturn(
-            listOf(
-                Kosmos().foldedDeviceStateList[0],
-                Kosmos().unfoldedDeviceState
+        whenever(deviceStateManager.supportedDeviceStates)
+            .thenReturn(
+                listOf(testKosmos().foldedDeviceStateList[0], testKosmos().unfoldedDeviceState)
             )
-        )
 
         unfoldLatencyTracker =
             UnfoldLatencyTracker(
@@ -85,7 +82,7 @@
                     context.mainExecutor,
                     context,
                     context.contentResolver,
-                    screenLifecycle
+                    screenLifecycle,
                 )
                 .apply { init() }
 
@@ -206,7 +203,7 @@
         Settings.Global.putString(
             context.contentResolver,
             Settings.Global.ANIMATOR_DURATION_SCALE,
-            durationScale.toString()
+            durationScale.toString(),
         )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 9440280..54ac3bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
@@ -33,6 +34,7 @@
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.takeWhile
 import kotlinx.coroutines.flow.toList
 import kotlinx.coroutines.launch
@@ -50,11 +52,7 @@
 class PairwiseFlowTest : SysuiTestCase() {
     @Test
     fun simple() = runBlocking {
-        assertThatFlow((1..3).asFlow().pairwise())
-            .emitsExactly(
-                WithPrev(1, 2),
-                WithPrev(2, 3),
-            )
+        assertThatFlow((1..3).asFlow().pairwise()).emitsExactly(WithPrev(1, 2), WithPrev(2, 3))
     }
 
     @Test fun notEnough() = runBlocking { assertThatFlow(flowOf(1).pairwise()).emitsNothing() }
@@ -157,48 +155,27 @@
     fun simple() = runBlocking {
         assertThatFlow(flowOf(setOf(1, 2, 3), setOf(2, 3, 4)).setChanges())
             .emitsExactly(
-                SetChanges(
-                    added = setOf(1, 2, 3),
-                    removed = emptySet(),
-                ),
-                SetChanges(
-                    added = setOf(4),
-                    removed = setOf(1),
-                ),
+                SetChanges(added = setOf(1, 2, 3), removed = emptySet()),
+                SetChanges(added = setOf(4), removed = setOf(1)),
             )
     }
 
     @Test
     fun onlyOneEmission() = runBlocking {
         assertThatFlow(flowOf(setOf(1)).setChanges())
-            .emitsExactly(
-                SetChanges(
-                    added = setOf(1),
-                    removed = emptySet(),
-                )
-            )
+            .emitsExactly(SetChanges(added = setOf(1), removed = emptySet()))
     }
 
     @Test
     fun fromEmptySet() = runBlocking {
         assertThatFlow(flowOf(emptySet(), setOf(1, 2)).setChanges())
-            .emitsExactly(
-                SetChanges(
-                    removed = emptySet(),
-                    added = setOf(1, 2),
-                )
-            )
+            .emitsExactly(SetChanges(removed = emptySet(), added = setOf(1, 2)))
     }
 
     @Test
     fun dontEmitFirstEvent() = runBlocking {
         assertThatFlow(flowOf(setOf(1, 2), setOf(2, 3)).setChanges(emitFirstEvent = false))
-            .emitsExactly(
-                SetChanges(
-                    removed = setOf(1),
-                    added = setOf(3),
-                )
-            )
+            .emitsExactly(SetChanges(removed = setOf(1), added = setOf(3)))
     }
 }
 
@@ -235,11 +212,7 @@
             emit(4)
         }
         assertThatFlow(sampler.sample(samplee) { a, b -> a to b })
-            .emitsExactly(
-                2 to 1,
-                3 to 3,
-                4 to 3,
-            )
+            .emitsExactly(2 to 1, 3 to 3, 4 to 3)
     }
 }
 
@@ -419,10 +392,262 @@
         }
 }
 
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SlidingWindowFlowTest : SysuiTestCase() {
+
+    @Test
+    fun basicWindowing() = runTest {
+        val choreographer = createChoreographer(this)
+        val output = mutableListOf<List<Int>>()
+        val collectJob =
+            backgroundScope.launch {
+                (1..5)
+                    .asFlow()
+                    .onEach { delay(100) }
+                    .slidingWindow(300.milliseconds, choreographer.fakeClock)
+                    .toList(output)
+            }
+
+        choreographer.advanceAndRun(0)
+        assertThat(output).isEmpty()
+
+        choreographer.advanceAndRun(100)
+        assertThat(output).containsExactly(listOf(1))
+
+        choreographer.advanceAndRun(1)
+        assertThat(output).containsExactly(listOf(1))
+
+        choreographer.advanceAndRun(99)
+        assertThat(output).containsExactly(listOf(1), listOf(1, 2))
+
+        choreographer.advanceAndRun(100)
+        assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3))
+
+        choreographer.advanceAndRun(100)
+        assertThat(output)
+            .containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3), listOf(2, 3, 4))
+
+        choreographer.advanceAndRun(100)
+        assertThat(output)
+            .containsExactly(
+                listOf(1),
+                listOf(1, 2),
+                listOf(1, 2, 3),
+                listOf(2, 3, 4),
+                listOf(3, 4, 5),
+            )
+
+        choreographer.advanceAndRun(100)
+        assertThat(output)
+            .containsExactly(
+                listOf(1),
+                listOf(1, 2),
+                listOf(1, 2, 3),
+                listOf(2, 3, 4),
+                listOf(3, 4, 5),
+                listOf(4, 5),
+            )
+
+        choreographer.advanceAndRun(100)
+        assertThat(output)
+            .containsExactly(
+                listOf(1),
+                listOf(1, 2),
+                listOf(1, 2, 3),
+                listOf(2, 3, 4),
+                listOf(3, 4, 5),
+                listOf(4, 5),
+                listOf(5),
+            )
+
+        choreographer.advanceAndRun(100)
+        assertThat(output)
+            .containsExactly(
+                listOf(1),
+                listOf(1, 2),
+                listOf(1, 2, 3),
+                listOf(2, 3, 4),
+                listOf(3, 4, 5),
+                listOf(4, 5),
+                listOf(5),
+                emptyList<Int>(),
+            )
+
+        // Verify no more emissions
+        choreographer.advanceAndRun(9999999999)
+        assertThat(output)
+            .containsExactly(
+                listOf(1),
+                listOf(1, 2),
+                listOf(1, 2, 3),
+                listOf(2, 3, 4),
+                listOf(3, 4, 5),
+                listOf(4, 5),
+                listOf(5),
+                emptyList<Int>(),
+            )
+
+        assertThat(collectJob.isCompleted).isTrue()
+    }
+
+    @Test
+    fun initialEmptyFlow() = runTest {
+        val choreographer = createChoreographer(this)
+        val output = mutableListOf<List<Int>>()
+        val collectJob =
+            backgroundScope.launch {
+                flow {
+                        delay(200)
+                        emit(1)
+                    }
+                    .slidingWindow(100.milliseconds, choreographer.fakeClock)
+                    .toList(output)
+            }
+
+        choreographer.advanceAndRun(0)
+        assertThat(output).isEmpty()
+
+        choreographer.advanceAndRun(200)
+        assertThat(output).containsExactly(listOf(1))
+
+        choreographer.advanceAndRun(100)
+        assertThat(output).containsExactly(listOf(1), emptyList<Int>())
+
+        assertThat(collectJob.isCompleted).isTrue()
+    }
+
+    @Test
+    fun windowLargerThanData() = runTest {
+        val choreographer = createChoreographer(this)
+        val output = mutableListOf<List<Int>>()
+        val collectJob =
+            backgroundScope.launch {
+                (1..3)
+                    .asFlow()
+                    .onEach { delay(50) }
+                    .slidingWindow(500.milliseconds, choreographer.fakeClock)
+                    .toList(output)
+            }
+
+        choreographer.advanceAndRun(0)
+        assertThat(output).isEmpty()
+
+        choreographer.advanceAndRun(50)
+        assertThat(output).containsExactly(listOf(1))
+
+        choreographer.advanceAndRun(50)
+        assertThat(output).containsExactly(listOf(1), listOf(1, 2))
+
+        choreographer.advanceAndRun(50)
+        assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3))
+
+        // It has been 100ms since the first emission, which means we have 400ms left until the
+        // first item is evicted from the window. Ensure that we have no evictions until that time.
+        choreographer.advanceAndRun(399)
+        assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3))
+
+        choreographer.advanceAndRun(1)
+        assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3), listOf(2, 3))
+
+        choreographer.advanceAndRun(50)
+        assertThat(output)
+            .containsExactly(listOf(1), listOf(1, 2), listOf(1, 2, 3), listOf(2, 3), listOf(3))
+
+        choreographer.advanceAndRun(50)
+        assertThat(output)
+            .containsExactly(
+                listOf(1),
+                listOf(1, 2),
+                listOf(1, 2, 3),
+                listOf(2, 3),
+                listOf(3),
+                emptyList<Int>(),
+            )
+
+        assertThat(collectJob.isCompleted).isTrue()
+    }
+
+    @Test
+    fun dataGapLargerThanWindow() = runTest {
+        val choreographer = createChoreographer(this)
+        val output = mutableListOf<List<Int>>()
+        val collectJob =
+            backgroundScope.launch {
+                flow {
+                        emit(1)
+                        delay(200)
+                        emit(2)
+                        delay(500) // Gap larger than window
+                        emit(3)
+                    }
+                    .slidingWindow(300.milliseconds, choreographer.fakeClock)
+                    .toList(output)
+            }
+
+        choreographer.advanceAndRun(0)
+        assertThat(output).containsExactly(listOf(1))
+
+        choreographer.advanceAndRun(200)
+        assertThat(output).containsExactly(listOf(1), listOf(1, 2))
+
+        choreographer.advanceAndRun(100)
+        assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(2))
+
+        choreographer.advanceAndRun(200)
+        assertThat(output).containsExactly(listOf(1), listOf(1, 2), listOf(2), emptyList<Int>())
+
+        choreographer.advanceAndRun(200)
+        assertThat(output)
+            .containsExactly(listOf(1), listOf(1, 2), listOf(2), emptyList<Int>(), listOf(3))
+
+        choreographer.advanceAndRun(300)
+        assertThat(output)
+            .containsExactly(
+                listOf(1),
+                listOf(1, 2),
+                listOf(2),
+                emptyList<Int>(),
+                listOf(3),
+                emptyList<Int>(),
+            )
+
+        assertThat(collectJob.isCompleted).isTrue()
+    }
+
+    @Test
+    fun emptyFlow() = runTest {
+        val choreographer = createChoreographer(this)
+        val output = mutableListOf<List<Int>>()
+
+        val collectJob =
+            backgroundScope.launch {
+                emptyFlow<Int>().slidingWindow(100.milliseconds).toList(output)
+            }
+
+        choreographer.advanceAndRun(0)
+        assertThat(output).isEmpty()
+
+        assertThat(collectJob.isCompleted).isTrue()
+    }
+
+    private fun createChoreographer(testScope: TestScope) =
+        object {
+            val fakeClock = FakeSystemClock()
+
+            fun advanceAndRun(millis: Long) {
+                fakeClock.advanceTime(millis)
+                testScope.advanceTimeBy(millis)
+                testScope.runCurrent()
+            }
+        }
+}
+
 private fun <T> assertThatFlow(flow: Flow<T>) =
     object {
         suspend fun emitsExactly(vararg emissions: T) =
             assertThat(flow.toList()).containsExactly(*emissions).inOrder()
+
         suspend fun emitsNothing() = assertThat(flow.toList()).isEmpty()
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
index 5f34420..422b20e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/GradientColorWallpaperTest.kt
@@ -45,7 +45,7 @@
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 @SmallTest
@@ -92,7 +92,7 @@
 
         engine.onSurfaceRedrawNeeded(surfaceHolder)
 
-        verifyZeroInteractions(canvas)
+        verifyNoMoreInteractions(canvas)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 341bd3a..0d7ce53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -33,6 +33,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -55,8 +57,6 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
@@ -136,7 +136,6 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -166,7 +165,6 @@
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.util.FakeEventLog;
 import com.android.systemui.util.settings.FakeGlobalSettings;
-import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.settings.SystemSettings;
 import com.android.systemui.util.time.SystemClock;
 import com.android.wm.shell.Flags;
@@ -206,8 +204,6 @@
 import com.android.wm.shell.taskview.TaskViewTransitions;
 import com.android.wm.shell.transition.Transitions;
 
-import kotlin.Lazy;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -218,9 +214,6 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.stubbing.Answer;
 
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -229,6 +222,10 @@
 import java.util.Optional;
 import java.util.concurrent.Executor;
 
+import kotlin.Lazy;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -451,7 +448,6 @@
                 () -> mSelectedUserInteractor,
                 mUserTracker,
                 mNotificationShadeWindowModel,
-                new FakeSettings(),
                 mKosmos::getCommunalInteractor,
                 mKosmos.getShadeLayoutParams()
         );
@@ -605,14 +601,19 @@
         // Get a reference to KeyguardStateController.Callback
         verify(mKeyguardStateController, atLeastOnce())
                 .addCallback(mKeyguardStateControllerCallbackCaptor.capture());
+
+        // Make sure mocks are set up for current user
+        switchUser(ActivityManager.getCurrentUser());
     }
 
     @After
     public void tearDown() throws Exception {
-        ArrayList<Bubble> bubbles = new ArrayList<>(mBubbleData.getBubbles());
-        for (int i = 0; i < bubbles.size(); i++) {
-            mBubbleController.removeBubble(bubbles.get(i).getKey(),
-                    Bubbles.DISMISS_NO_LONGER_BUBBLE);
+        if (mBubbleData != null) {
+            ArrayList<Bubble> bubbles = new ArrayList<>(mBubbleData.getBubbles());
+            for (int i = 0; i < bubbles.size(); i++) {
+                mBubbleController.removeBubble(bubbles.get(i).getKey(),
+                        Bubbles.DISMISS_NO_LONGER_BUBBLE);
+            }
         }
         mTestableLooper.processAllMessages();
 
@@ -2054,6 +2055,9 @@
 
     @Test
     public void testShowStackEdu_isConversationBubble() {
+        // TODO(b/401025577): Prevent this test from raising a WTF, and remove this exemption
+        mLogWtfRule.addFailureLogExemption(log-> log.getTag().equals("FloatingCoordinator"));
+
         // Setup
         setPrefBoolean(StackEducationView.PREF_STACK_EDUCATION, false);
         BubbleEntry bubbleEntry = createBubbleEntry();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 252c70a..846db63 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui;
 
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -28,7 +29,6 @@
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.platform.test.ravenwood.RavenwoodClassRule;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.test.mock.MockContext;
 import android.testing.DexmakerShareClassLoaderRule;
@@ -46,14 +46,17 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.systemui.broadcast.FakeBroadcastDispatcher;
 import com.android.systemui.flags.SceneContainerRule;
+import com.android.systemui.log.LogWtfHandlerRule;
 
 import org.junit.After;
 import org.junit.AfterClass;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.mockito.Mockito;
 
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.lang.annotation.Retention;
@@ -92,23 +95,6 @@
     public AndroidXAnimatorIsolationRule mAndroidXAnimatorIsolationRule =
             new AndroidXAnimatorIsolationRule();
 
-    /**
-     * Rule that respects class-level annotations such as {@code @DisabledOnRavenwood} when tests
-     * are running on Ravenwood; on all other test environments this rule is a no-op passthrough.
-     */
-    @ClassRule(order = Integer.MIN_VALUE + 1)
-    public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule();
-
-    /**
-     * Rule that defines and prepares the Ravenwood environment when tests are running on
-     * Ravenwood; on all other test environments this rule is a no-op passthrough.
-     */
-    @Rule(order = Integer.MIN_VALUE + 1)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProcessApp()
-            .setProvideMainThread(true)
-            .build();
-
     @ClassRule
     public static final SetFlagsRule.ClassRule mSetFlagsClassRule =
             new SetFlagsRule.ClassRule(
@@ -127,6 +113,8 @@
     @Rule public final SetFlagsRule mSetFlagsRule =
             isRobolectricTest() ? new SetFlagsRule() : mSetFlagsClassRule.createSetFlagsRule();
 
+    @Rule public final LogWtfHandlerRule mLogWtfRule = new LogWtfHandlerRule();
+
     @Rule(order = 10)
     public final SceneContainerRule mSceneContainerRule = new SceneContainerRule();
 
@@ -205,6 +193,7 @@
 
     @Before
     public void SysuiSetup() throws Exception {
+        assertTempFilesAreCreatable();
         ProtoLog.REQUIRE_PROTOLOGTOOL = false;
         mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
         mDependency = mSysuiDependency.install();
@@ -226,6 +215,28 @@
         }
     }
 
+    private static Boolean sCanCreateTempFiles = null;
+
+    private static void assertTempFilesAreCreatable() {
+        // TODO(b/391948934): hopefully remove this hack
+        if (sCanCreateTempFiles == null) {
+            try {
+                File tempFile = File.createTempFile("confirm_temp_file_createable", "txt");
+                sCanCreateTempFiles = true;
+                assertTrue(tempFile.delete());
+            } catch (IOException e) {
+                sCanCreateTempFiles = false;
+                throw new RuntimeException(e);
+            }
+        }
+        if (!sCanCreateTempFiles) {
+            Assert.fail(
+                    "Cannot create temp files, so mockito will probably fail (b/391948934).  Temp"
+                            + " folder should be: "
+                            + System.getProperty("java.io.tmpdir"));
+        }
+    }
+
     protected boolean shouldFailOnLeakedReceiver() {
         return false;
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index 0ba7c85..a3d0135 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -47,6 +47,7 @@
 import com.android.systemui.log.dagger.BroadcastDispatcherLog
 import com.android.systemui.log.dagger.FaceAuthLog
 import com.android.systemui.log.dagger.SceneFrameworkLog
+import com.android.systemui.media.NotificationMediaManager
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
 import com.android.systemui.model.SysUiState
 import com.android.systemui.plugins.ActivityStarter
@@ -57,7 +58,6 @@
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.NotificationListener
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.statusbar.NotificationShadeDepthController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CarProjectionRepositoryKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CarProjectionRepositoryKosmos.kt
index ccfb609..130c298 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CarProjectionRepositoryKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.communal.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
+val Kosmos.carProjectionRepository by
+    Kosmos.Fixture<CarProjectionRepository> { FakeCarProjectionRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCarProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCarProjectionRepository.kt
new file mode 100644
index 0000000..4042342
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCarProjectionRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeCarProjectionRepository : CarProjectionRepository {
+    private val _projectionActive = MutableStateFlow(false)
+    override val projectionActive: Flow<Boolean> = _projectionActive.asStateFlow()
+
+    override suspend fun isProjectionActive(): Boolean {
+        return _projectionActive.value
+    }
+
+    fun setProjectionActive(active: Boolean) {
+        _projectionActive.value = active
+    }
+}
+
+val CarProjectionRepository.fake
+    get() = this as FakeCarProjectionRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CarProjectionInteractorKosmos.kt
similarity index 64%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CarProjectionInteractorKosmos.kt
index ccfb609..23bbe36 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CarProjectionInteractorKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.communal.domain.interactor
 
+import com.android.systemui.communal.data.repository.carProjectionRepository
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
 
-val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
+val Kosmos.carProjectionInteractor by Fixture { CarProjectionInteractor(carProjectionRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorKosmos.kt
new file mode 100644
index 0000000..5735cf8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import com.android.systemui.common.domain.interactor.batteryInteractor
+import com.android.systemui.communal.posturing.domain.interactor.posturingInteractor
+import com.android.systemui.dock.dockManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.backgroundCoroutineContext
+
+val Kosmos.communalAutoOpenInteractor by Fixture {
+    CommunalAutoOpenInteractor(
+        communalSettingsInteractor = communalSettingsInteractor,
+        backgroundContext = backgroundCoroutineContext,
+        batteryInteractor = batteryInteractor,
+        posturingInteractor = posturingInteractor,
+        dockManager = dockManager,
+        allowSwipeAlways = false,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index b8b2ec5..316fcbb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -16,17 +16,14 @@
 
 package com.android.systemui.communal.domain.interactor
 
-import android.content.pm.UserInfo
 import android.content.testableContext
 import android.os.userManager
 import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.common.domain.interactor.batteryInteractor
+import com.android.systemui.communal.data.model.SuppressionReason
 import com.android.systemui.communal.data.repository.communalMediaRepository
 import com.android.systemui.communal.data.repository.communalSmartspaceRepository
 import com.android.systemui.communal.data.repository.communalWidgetRepository
-import com.android.systemui.communal.posturing.domain.interactor.posturingInteractor
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
-import com.android.systemui.dock.dockManager
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -39,13 +36,9 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
 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.userTracker
 import com.android.systemui.statusbar.phone.fakeManagedProfileController
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.user.domain.interactor.userLockedInteractor
 import com.android.systemui.util.mockito.mock
 
 val Kosmos.communalInteractor by Fixture {
@@ -70,10 +63,6 @@
         logBuffer = logcatLogBuffer("CommunalInteractor"),
         tableLogBuffer = mock(),
         managedProfileController = fakeManagedProfileController,
-        batteryInteractor = batteryInteractor,
-        dockManager = dockManager,
-        posturingInteractor = posturingInteractor,
-        userLockedInteractor = userLockedInteractor,
     )
 }
 
@@ -86,28 +75,28 @@
     )
 }
 
-suspend fun Kosmos.setCommunalEnabled(enabled: Boolean): UserInfo {
+fun Kosmos.setCommunalEnabled(enabled: Boolean) {
     fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, enabled)
-    return if (enabled) {
-        fakeUserRepository.asMainUser()
-    } else {
-        fakeUserRepository.asDefaultUser()
-    }
+    val suppressionReasons =
+        if (enabled) {
+            emptyList()
+        } else {
+            listOf(SuppressionReason.ReasonUnknown())
+        }
+    communalSettingsInteractor.setSuppressionReasons(suppressionReasons)
 }
 
-suspend fun Kosmos.setCommunalV2Enabled(enabled: Boolean): UserInfo {
+fun Kosmos.setCommunalV2Enabled(enabled: Boolean) {
     setCommunalV2ConfigEnabled(enabled)
     return setCommunalEnabled(enabled)
 }
 
-suspend fun Kosmos.setCommunalAvailable(available: Boolean): UserInfo {
-    val user = setCommunalEnabled(available)
+fun Kosmos.setCommunalAvailable(available: Boolean) {
+    setCommunalEnabled(available)
     fakeKeyguardRepository.setKeyguardShowing(available)
-    fakeUserRepository.setUserUnlocked(FakeUserRepository.MAIN_USER_ID, available)
-    return user
 }
 
-suspend fun Kosmos.setCommunalV2Available(available: Boolean): UserInfo {
+fun Kosmos.setCommunalV2Available(available: Boolean) {
     setCommunalV2ConfigEnabled(available)
-    return setCommunalAvailable(available)
+    setCommunalAvailable(available)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
index fb983f7..d2fbb51 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.settings.userTracker
 import com.android.systemui.user.domain.interactor.selectedUserInteractor
-import com.android.systemui.util.mockito.mock
 
 val Kosmos.communalSettingsInteractor by Fixture {
     CommunalSettingsInteractor(
@@ -34,6 +33,5 @@
         repository = communalSettingsRepository,
         userInteractor = selectedUserInteractor,
         userTracker = userTracker,
-        tableLogBuffer = mock(),
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/condition/KosmosConditionTestExtensions.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/condition/KosmosConditionTestExtensions.kt
new file mode 100644
index 0000000..c976b57
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/condition/KosmosConditionTestExtensions.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.condition
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.shared.condition.Condition
+
+private val testCallback =
+    Condition.Callback {
+        // This is a no-op
+    }
+
+fun Kosmos.testStart(condition: Condition) {
+    condition.addCallback(testCallback)
+    runCurrent()
+}
+
+fun Kosmos.testStop(condition: Condition) {
+    condition.removeCallback(testCallback)
+    runCurrent()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
index 6f570a8..cd4b09c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.keyevent.domain.interactor.keyEventInteractor
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardBypassInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.util.time.systemClock
@@ -31,8 +30,6 @@
         DeviceEntryHapticsInteractor(
             biometricSettingsRepository = biometricSettingsRepository,
             deviceEntryBiometricAuthInteractor = deviceEntryBiometricAuthInteractor,
-            deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
-            keyguardBypassInteractor = keyguardBypassInteractor,
             deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
             deviceEntrySourceInteractor = deviceEntrySourceInteractor,
             fingerprintPropertyRepository = fingerprintPropertyRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index 663a853..122e6a5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.display.data.repository
 
 import android.view.Display
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.util.mockito.mock
 import dagger.Binds
@@ -41,16 +42,15 @@
 }
 
 /** Creates a mock [DisplayRepository.PendingDisplay]. */
-fun createPendingDisplay(id: Int = 0): DisplayRepository.PendingDisplay =
-    mock<DisplayRepository.PendingDisplay> { whenever(this.id).thenReturn(id) }
+fun createPendingDisplay(id: Int = 0): PendingDisplay =
+    mock<PendingDisplay> { whenever(this.id).thenReturn(id) }
 
 @SysUISingleton
 /** Fake [DisplayRepository] implementation for testing. */
 class FakeDisplayRepository @Inject constructor() : DisplayRepository {
     private val flow = MutableStateFlow<Set<Display>>(emptySet())
     private val displayIdFlow = MutableStateFlow<Set<Int>>(emptySet())
-    private val pendingDisplayFlow =
-        MutableSharedFlow<DisplayRepository.PendingDisplay?>(replay = 1)
+    private val pendingDisplayFlow = MutableSharedFlow<PendingDisplay?>(replay = 1)
     private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0)
     private val displayRemovalEventFlow = MutableSharedFlow<Int>(replay = 0)
     private val displayIdsWithSystemDecorationsFlow = MutableStateFlow<Set<Int>>(emptySet())
@@ -101,7 +101,7 @@
     suspend fun emit(value: Set<Display>) = flow.emit(value)
 
     /** Emits [value] as [pendingDisplay] flow value. */
-    suspend fun emit(value: DisplayRepository.PendingDisplay?) = pendingDisplayFlow.emit(value)
+    suspend fun emit(value: PendingDisplay?) = pendingDisplayFlow.emit(value)
 
     override val displays: StateFlow<Set<Display>>
         get() = flow
@@ -109,7 +109,7 @@
     override val displayIds: StateFlow<Set<Int>>
         get() = displayIdFlow
 
-    override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?>
+    override val pendingDisplay: Flow<PendingDisplay?>
         get() = pendingDisplayFlow
 
     private val _defaultDisplayOff: MutableStateFlow<Boolean> = MutableStateFlow(false)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
index aa23aa3..4b516e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.display.data.repository
 
+import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -66,6 +68,7 @@
 val Kosmos.fakePerDisplayInstanceProviderWithTeardown by
     Kosmos.Fixture { FakePerDisplayInstanceProviderWithTeardown() }
 
+val Kosmos.perDisplayDumpHelper by Kosmos.Fixture { PerDisplayRepoDumpHelper(dumpManager) }
 val Kosmos.fakePerDisplayInstanceRepository by
     Kosmos.Fixture {
         PerDisplayInstanceRepositoryImpl(
@@ -73,6 +76,6 @@
             instanceProvider = fakePerDisplayInstanceProviderWithTeardown,
             testScope.backgroundScope,
             displayRepository,
-            dumpManager,
+            perDisplayDumpHelper,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFake.java
deleted file mode 100644
index b99310b..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFake.java
+++ /dev/null
@@ -1,66 +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.dock;
-
-/**
- * A rudimentary fake for DockManager.
- */
-public class DockManagerFake implements DockManager {
-    DockEventListener mCallback;
-    AlignmentStateListener mAlignmentListener;
-    private boolean mDocked;
-
-    @Override
-    public void addListener(DockEventListener callback) {
-        this.mCallback = callback;
-    }
-
-    @Override
-    public void removeListener(DockEventListener callback) {
-        this.mCallback = null;
-    }
-
-    @Override
-    public void addAlignmentStateListener(AlignmentStateListener listener) {
-        mAlignmentListener = listener;
-    }
-
-    @Override
-    public void removeAlignmentStateListener(AlignmentStateListener listener) {
-        mAlignmentListener = listener;
-    }
-
-    @Override
-    public boolean isDocked() {
-        return mDocked;
-    }
-
-    /** Sets the docked state */
-    public void setIsDocked(boolean docked) {
-        mDocked = docked;
-    }
-
-    @Override
-    public boolean isHidden() {
-        return false;
-    }
-
-    /** Notifies callbacks of dock state change */
-    public void setDockEvent(int event) {
-        mCallback.onEvent(event);
-    }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFake.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFake.kt
new file mode 100644
index 0000000..6a43c40
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dock/DockManagerFake.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dock
+
+import com.android.systemui.dock.DockManager.AlignmentStateListener
+
+/** A rudimentary fake for DockManager. */
+class DockManagerFake : DockManager {
+    private val callbacks = mutableSetOf<DockManager.DockEventListener>()
+    private val alignmentListeners = mutableSetOf<AlignmentStateListener>()
+    private var docked = false
+
+    override fun addListener(callback: DockManager.DockEventListener) {
+        callbacks.add(callback)
+    }
+
+    override fun removeListener(callback: DockManager.DockEventListener) {
+        callbacks.remove(callback)
+    }
+
+    override fun addAlignmentStateListener(listener: AlignmentStateListener) {
+        alignmentListeners.add(listener)
+    }
+
+    override fun removeAlignmentStateListener(listener: AlignmentStateListener) {
+        alignmentListeners.remove(listener)
+    }
+
+    override fun isDocked(): Boolean {
+        return docked
+    }
+
+    /** Sets the docked state */
+    fun setIsDocked(docked: Boolean) {
+        this.docked = docked
+    }
+
+    override fun isHidden(): Boolean {
+        return false
+    }
+
+    /** Notifies callbacks of dock state change */
+    fun setDockEvent(event: Int) {
+        for (callback in callbacks) {
+            callback.onEvent(event)
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt
index 97dab49..c9e25c3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt
@@ -27,9 +27,16 @@
     private val _isPowerButtonDown = MutableStateFlow(false)
     override val isPowerButtonDown: Flow<Boolean> = _isPowerButtonDown.asStateFlow()
 
+    private val _isPowerButtonLongPressed = MutableStateFlow(false)
+    override val isPowerButtonLongPressed = _isPowerButtonLongPressed.asStateFlow()
+
     fun setPowerButtonDown(isDown: Boolean) {
         _isPowerButtonDown.value = isDown
     }
+
+    fun setPowerButtonLongPressed(isLongPressed: Boolean) {
+        _isPowerButtonLongPressed.value = isLongPressed
+    }
 }
 
 @Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
index ccfb609..361d21d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardServiceShowLockscreenRepositoryKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
+val Kosmos.keyguardServiceShowLockscreenRepository by
+    Kosmos.Fixture { KeyguardServiceShowLockscreenRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
index bdfa875..9b0a983 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
@@ -43,6 +42,5 @@
             wakeToGoneInteractor = keyguardWakeDirectlyToGoneInteractor,
             communalSettingsInteractor = communalSettingsInteractor,
             communalSceneInteractor = communalSceneInteractor,
-            communalInteractor = communalInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index 985044c..511bede 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
@@ -42,7 +41,6 @@
             communalSettingsInteractor = communalSettingsInteractor,
             swipeToDismissInteractor = swipeToDismissInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
-            communalInteractor = communalInteractor,
             communalSceneInteractor = communalSceneInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
index 255a780..1130592 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.content.applicationContext
+import android.os.powerManager
 import android.view.accessibility.accessibilityManagerWrapper
 import com.android.internal.logging.uiEventLogger
 import com.android.systemui.broadcast.broadcastDispatcher
@@ -26,6 +27,8 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.shade.pulsingGestureListener
+import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
+import com.android.systemui.util.time.fakeSystemClock
 
 val Kosmos.keyguardTouchHandlingInteractor by
     Kosmos.Fixture {
@@ -40,5 +43,8 @@
             accessibilityManager = accessibilityManagerWrapper,
             pulsingGestureListener = pulsingGestureListener,
             faceAuthInteractor = deviceEntryFaceAuthInteractor,
+            secureSettingsRepository = userAwareSecureSettingsRepository,
+            powerManager = powerManager,
+            systemClock = fakeSystemClock,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
deleted file mode 100644
index 3cec5a9..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractor.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
-
-val Kosmos.keyguardServiceShowLockscreenInteractor by
-    Kosmos.Fixture { KeyguardServiceShowLockscreenInteractor(backgroundScope = testScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt
new file mode 100644
index 0000000..447aa12
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceShowLockscreenInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.keyguard.data.repository.keyguardServiceShowLockscreenRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.userTracker
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+
+val Kosmos.keyguardServiceShowLockscreenInteractor by
+    Kosmos.Fixture {
+        KeyguardServiceShowLockscreenInteractor(
+            backgroundScope = testScope,
+            selectedUserInteractor = selectedUserInteractor,
+            repository = keyguardServiceShowLockscreenRepository,
+            userTracker = userTracker,
+            wmLockscreenVisibilityInteractor = { windowManagerLockscreenVisibilityInteractor },
+            keyguardEnabledInteractor = keyguardEnabledInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
index f7caeb6..dd868e7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardShowWhileAwakeInteractorKosmos.kt
@@ -18,10 +18,12 @@
 
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
 
 val Kosmos.keyguardShowWhileAwakeInteractor by
     Kosmos.Fixture {
         KeyguardShowWhileAwakeInteractor(
+            backgroundScope = testScope,
             biometricSettingsRepository = biometricSettingsRepository,
             keyguardEnabledInteractor = keyguardEnabledInteractor,
             keyguardServiceShowLockscreenInteractor = keyguardServiceShowLockscreenInteractor,
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 43fa718..c89fb70 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
@@ -27,7 +27,7 @@
 import com.android.systemui.util.settings.fakeSettings
 import com.android.systemui.util.time.systemClock
 
-val Kosmos.keyguardWakeDirectlyToGoneInteractor by
+val Kosmos.keyguardWakeDirectlyToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor by
     Kosmos.Fixture {
         KeyguardWakeDirectlyToGoneInteractor(
             applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt
index 2797b44..bf45697 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt
@@ -48,5 +48,6 @@
             primaryBouncerToLockscreenTransitionViewModel,
         lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel,
         glanceableHubToAodTransitionViewModel = glanceableHubToAodTransitionViewModel,
+        glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel,
     )
 }
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 27ca0f86..a9aa8cd 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
@@ -84,6 +84,7 @@
         occludedToAodTransitionViewModel = occludedToAodTransitionViewModel,
         occludedToDozingTransitionViewModel = occludedToDozingTransitionViewModel,
         occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
+        occludedToPrimaryBouncerTransitionViewModel = occludedToPrimaryBouncerTransitionViewModel,
         offToLockscreenTransitionViewModel = offToLockscreenTransitionViewModel,
         primaryBouncerToAodTransitionViewModel = primaryBouncerToAodTransitionViewModel,
         primaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt
index 11f0c19..7093a94 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/util/KeyguardTransitionRepositorySpySubject.kt
@@ -115,14 +115,14 @@
         fun assertThat(
             repository: KeyguardTransitionRepository
         ): KeyguardTransitionRepositorySpySubject =
-            assertAbout { failureMetadata, repository: KeyguardTransitionRepository ->
+                assertAbout { failureMetadata, repository: KeyguardTransitionRepository? ->
                     if (!Mockito.mockingDetails(repository).isSpy) {
                         fail(
                             "Cannot assert on a non-spy KeyguardTransitionRepository. " +
                                 "Use Mockito.spy(keyguardTransitionRepository)."
                         )
                     }
-                    KeyguardTransitionRepositorySpySubject(failureMetadata, repository)
+                    KeyguardTransitionRepositorySpySubject(failureMetadata, repository!!)
                 }
                 .that(repository)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 623989e..c80d738 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -81,6 +81,7 @@
 import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
+import com.android.systemui.statusbar.notification.row.entryAdapterFactory
 import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.statusbar.phone.fakeAutoHideControllerStore
@@ -206,4 +207,5 @@
     val displayTracker by lazy { kosmos.displayTracker }
     val fakeShadeDisplaysRepository by lazy { kosmos.fakeShadeDisplaysRepository }
     val sysUIStateDispatcher by lazy { kosmos.sysUIStateDispatcher }
+    val entryAdapterFactory by lazy { kosmos.entryAdapterFactory }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
index 5e67182..a42f202 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt
@@ -13,89 +13,86 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.systemui.log
 
 import android.util.Log
 import android.util.Log.TerribleFailureHandler
-import junit.framework.Assert
+import com.google.common.truth.Truth.assertWithMessage
 
-/** Asserts that the given block does not make a call to Log.wtf */
-fun assertDoesNotLogWtf(
+/** Asserts that [notLoggingBlock] does not make a call to [Log.wtf] */
+fun <T> assertDoesNotLogWtf(
     message: String = "Expected Log.wtf not to be called",
-    notLoggingBlock: () -> Unit,
-) {
+    notLoggingBlock: () -> T,
+): T {
     var caught: TerribleFailureLog? = null
     val newHandler = TerribleFailureHandler { tag, failure, system ->
         caught = TerribleFailureLog(tag, failure, system)
     }
     val oldHandler = Log.setWtfHandler(newHandler)
-    try {
-        notLoggingBlock()
-    } finally {
-        Log.setWtfHandler(oldHandler)
-    }
+    val result =
+        try {
+            notLoggingBlock()
+        } finally {
+            Log.setWtfHandler(oldHandler)
+        }
     caught?.let { throw AssertionError("$message: $it", it.failure) }
+    return result
 }
 
-fun assertDoesNotLogWtf(
-    message: String = "Expected Log.wtf not to be called",
-    notLoggingRunnable: Runnable,
-) = assertDoesNotLogWtf(message = message) { notLoggingRunnable.run() }
-
-/**
- * Assert that the given block makes a call to Log.wtf
- *
- * @return the details of the log
- */
-fun assertLogsWtf(
+/** Assert that [loggingBlock] makes a call to [Log.wtf] */
+@JvmOverloads
+fun <T> assertLogsWtf(
     message: String = "Expected Log.wtf to be called",
     allowMultiple: Boolean = false,
-    loggingBlock: () -> Unit,
-): TerribleFailureLog {
-    var caught: TerribleFailureLog? = null
-    var count = 0
+    loggingBlock: () -> T,
+): WtfBlockResult<T> {
+    val caught = mutableListOf<TerribleFailureLog>()
     val newHandler = TerribleFailureHandler { tag, failure, system ->
-        if (caught == null) {
-            caught = TerribleFailureLog(tag, failure, system)
-        }
-        count++
+        caught.add(TerribleFailureLog(tag, failure, system))
     }
     val oldHandler = Log.setWtfHandler(newHandler)
-    try {
-        loggingBlock()
-    } finally {
-        Log.setWtfHandler(oldHandler)
+    val result =
+        try {
+            loggingBlock()
+        } finally {
+            Log.setWtfHandler(oldHandler)
+        }
+    assertWithMessage(message).that(caught).isNotEmpty()
+    if (!allowMultiple) {
+        assertWithMessage("Unexpectedly caught Log.Wtf multiple times").that(caught).hasSize(1)
     }
-    Assert.assertNotNull(message, caught)
-    if (!allowMultiple && count != 1) {
-        Assert.fail("Unexpectedly caught Log.Wtf $count times; expected only 1.  First: $caught")
-    }
-    return caught!!
+    return WtfBlockResult(caught, result)
 }
 
+/** Assert that [loggingBlock] makes at least one call to [Log.wtf] */
 @JvmOverloads
-fun assertLogsWtf(
-    message: String = "Expected Log.wtf to be called",
-    allowMultiple: Boolean = false,
-    loggingRunnable: Runnable,
-): TerribleFailureLog =
-    assertLogsWtf(message = message, allowMultiple = allowMultiple) { loggingRunnable.run() }
-
-fun assertLogsWtfs(
+fun <T> assertLogsWtfs(
     message: String = "Expected Log.wtf to be called once or more",
-    loggingBlock: () -> Unit,
-): TerribleFailureLog = assertLogsWtf(message, allowMultiple = true, loggingBlock)
-
-@JvmOverloads
-fun assertLogsWtfs(
-    message: String = "Expected Log.wtf to be called once or more",
-    loggingRunnable: Runnable,
-): TerribleFailureLog = assertLogsWtfs(message) { loggingRunnable.run() }
+    loggingBlock: () -> T,
+): WtfBlockResult<T> = assertLogsWtf(message, allowMultiple = true, loggingBlock)
 
 /** The data passed to [TerribleFailureHandler.onTerribleFailure] */
 data class TerribleFailureLog(
     val tag: String,
     val failure: Log.TerribleFailure,
-    val system: Boolean
+    val system: Boolean,
 )
+
+/** The [Log.wtf] logs and return value of the block */
+data class WtfBlockResult<T>(val logs: List<TerribleFailureLog>, val result: T)
+
+/** Assert that [loggingRunnable] makes a call to [Log.wtf] */
+@JvmOverloads
+fun assertRunnableLogsWtf(
+    message: String = "Expected Log.wtf to be called",
+    allowMultiple: Boolean = false,
+    loggingRunnable: Runnable,
+): WtfBlockResult<Unit> =
+    assertLogsWtf(message = message, allowMultiple = allowMultiple) { loggingRunnable.run() }
+
+/** Assert that [loggingRunnable] makes at least one call to [Log.wtf] */
+@JvmOverloads
+fun assertRunnableLogsWtfs(
+    message: String = "Expected Log.wtf to be called once or more",
+    loggingRunnable: Runnable,
+): WtfBlockResult<Unit> = assertRunnableLogsWtf(message, allowMultiple = true, loggingRunnable)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt
index e639326..0e348c8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogWtfHandlerRule.kt
@@ -24,21 +24,23 @@
 
 class LogWtfHandlerRule : TestRule {
 
-    private var started = false
-    private var handler = ThrowAndFailAtEnd
+    private var failureLogExemptions = mutableListOf<FailureLogExemption>()
 
     override fun apply(base: Statement, description: Description): Statement {
         return object : Statement() {
             override fun evaluate() {
-                started = true
+                val handler = TerribleFailureTestHandler()
                 val originalWtfHandler = Log.setWtfHandler(handler)
                 var failure: Throwable? = null
                 try {
                     base.evaluate()
                 } catch (ex: Throwable) {
-                    failure = ex.runAndAddSuppressed { handler.onTestFailure(ex) }
+                    failure = ex
                 } finally {
-                    failure = failure.runAndAddSuppressed { handler.onTestFinished() }
+                    failure =
+                        runAndAddSuppressed(failure) {
+                            handler.onTestFinished(failureLogExemptions)
+                        }
                     Log.setWtfHandler(originalWtfHandler)
                 }
                 if (failure != null) {
@@ -48,74 +50,52 @@
         }
     }
 
-    fun Throwable?.runAndAddSuppressed(block: () -> Unit): Throwable? {
+    /** Adds a log failure exemption. Exemptions are evaluated at the end of the test. */
+    fun addFailureLogExemption(exemption: FailureLogExemption) {
+        failureLogExemptions.add(exemption)
+    }
+
+    /** Clears and sets exemptions. Exemptions are evaluated at the end of the test. */
+    fun resetFailureLogExemptions(vararg exemptions: FailureLogExemption) {
+        failureLogExemptions = exemptions.toMutableList()
+    }
+
+    private fun runAndAddSuppressed(currentError: Throwable?, block: () -> Unit): Throwable? {
         try {
             block()
         } catch (t: Throwable) {
-            if (this == null) {
+            if (currentError == null) {
                 return t
             }
-            addSuppressed(t)
+            currentError.addSuppressed(t)
         }
-        return this
+        return currentError
     }
 
-    fun setWtfHandler(handler: TerribleFailureTestHandler) {
-        check(!started) { "Should only be called before the test starts" }
-        this.handler = handler
-    }
+    private class TerribleFailureTestHandler : TerribleFailureHandler {
+        private val failureLogs = mutableListOf<FailureLog>()
 
-    fun interface TerribleFailureTestHandler : TerribleFailureHandler {
-        fun onTestFailure(failure: Throwable) {}
-        fun onTestFinished() {}
-    }
+        override fun onTerribleFailure(tag: String, what: Log.TerribleFailure, system: Boolean) {
+            failureLogs.add(FailureLog(tag = tag, failure = what, system = system))
+        }
 
-    companion object Handlers {
-        val ThrowAndFailAtEnd
-            get() =
-                object : TerribleFailureTestHandler {
-                    val failures = mutableListOf<Log.TerribleFailure>()
-
-                    override fun onTerribleFailure(
-                        tag: String,
-                        what: Log.TerribleFailure,
-                        system: Boolean
-                    ) {
-                        failures.add(what)
-                        throw what
-                    }
-
-                    override fun onTestFailure(failure: Throwable) {
-                        super.onTestFailure(failure)
-                    }
-
-                    override fun onTestFinished() {
-                        if (failures.isNotEmpty()) {
-                            throw AssertionError("Unexpected Log.wtf calls: $failures", failures[0])
-                        }
-                    }
+        fun onTestFinished(exemptions: List<FailureLogExemption>) {
+            val failures =
+                failureLogs.filter { failureLog ->
+                    !exemptions.any { it.isFailureLogExempt(failureLog) }
                 }
+            if (failures.isNotEmpty()) {
+                throw AssertionError("Unexpected Log.wtf calls: $failures", failures[0].failure)
+            }
+        }
+    }
 
-        val JustThrow = TerribleFailureTestHandler { _, what, _ -> throw what }
+    /** All the information from a call to [Log.wtf] that was handed to [TerribleFailureHandler] */
+    data class FailureLog(val tag: String, val failure: Log.TerribleFailure, val system: Boolean)
 
-        val JustFailAtEnd
-            get() =
-                object : TerribleFailureTestHandler {
-                    val failures = mutableListOf<Log.TerribleFailure>()
-
-                    override fun onTerribleFailure(
-                        tag: String,
-                        what: Log.TerribleFailure,
-                        system: Boolean
-                    ) {
-                        failures.add(what)
-                    }
-
-                    override fun onTestFinished() {
-                        if (failures.isNotEmpty()) {
-                            throw AssertionError("Unexpected Log.wtf calls: $failures", failures[0])
-                        }
-                    }
-                }
+    /** An interface for exempting a [FailureLog] from causing a test failure. */
+    fun interface FailureLogExemption {
+        /** Determines whether a log should be except from failing the test. */
+        fun isFailureLogExempt(log: FailureLog): Boolean
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
index 11bd4c7..54261c7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/model/SysUiStateKosmos.kt
@@ -38,7 +38,9 @@
     }
 }
 
-val Kosmos.fakeSysUIStatePerDisplayRepository by Fixture { FakePerDisplayRepository<SysUiState>() }
+val Kosmos.fakeSysUIStatePerDisplayRepository by Fixture {
+    FakePerDisplayRepository<SysUiState>().apply { add(Display.DEFAULT_DISPLAY, sysUiState) }
+}
 
 val Kosmos.sysuiStateInteractor by Fixture {
     SysUIStateDisplaysInteractor(fakeSysUIStatePerDisplayRepository, displayRepository)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeTileDetailsViewModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeTileDetailsViewModel.kt
index 4f8d5a1..9457de1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeTileDetailsViewModel.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeTileDetailsViewModel.kt
@@ -18,18 +18,14 @@
 
 import com.android.systemui.plugins.qs.TileDetailsViewModel
 
-class FakeTileDetailsViewModel(var tileSpec: String?) : TileDetailsViewModel() {
+class FakeTileDetailsViewModel(var tileSpec: String?) : TileDetailsViewModel {
     private var _clickOnSettingsButton = 0
 
     override fun clickOnSettingsButton() {
         _clickOnSettingsButton++
     }
 
-    override fun getTitle(): String {
-        return tileSpec ?: " Fake title"
-    }
+    override val title = tileSpec ?: " Fake title"
 
-    override fun getSubTitle(): String {
-        return tileSpec ?: "Fake sub title"
-    }
+    override val subTitle = tileSpec ?: "Fake sub title"
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorKosmos.kt
index bd54fd4..2cd270e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorKosmos.kt
@@ -19,7 +19,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.panels.data.repository.iconAndNameCustomRepository
 import com.android.systemui.qs.panels.data.repository.stockTilesRepository
-import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.qSTileConfigProvider
 
 val Kosmos.editTilesListInteractor by
     Kosmos.Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt
index cc7eb6b..7887762 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt
@@ -17,12 +17,11 @@
 package com.android.systemui.qs.panels.domain.interactor
 
 import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
 import kotlinx.coroutines.flow.Flow
 
-class FakeTileAvailabilityInteractor(
-        private val availabilityFlows: Map<Int, Flow<Boolean>>
-) : QSTileAvailabilityInteractor {
+class FakeTileAvailabilityInteractor(private val availabilityFlows: Map<Int, Flow<Boolean>>) :
+    QSTileAvailabilityInteractor {
     override fun availability(user: UserHandle): Flow<Boolean> {
         return availabilityFlows.getValue(user.identifier)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt
index 40e6c75..6a65706 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt
@@ -17,16 +17,13 @@
 package com.android.systemui.qs.panels.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.user.data.repository.userRepository
 
-var Kosmos.tileAvailabilityInteractorsMap by Kosmos.Fixture {
-    emptyMap<String, QSTileAvailabilityInteractor>()
-}
+var Kosmos.tileAvailabilityInteractorsMap by
+    Kosmos.Fixture { emptyMap<String, QSTileAvailabilityInteractor>() }
 
-val Kosmos.newTilesAvailabilityInteractor by Kosmos.Fixture {
-    NewTilesAvailabilityInteractor(
-            tileAvailabilityInteractorsMap,
-            userRepository,
-    )
-}
+val Kosmos.newTilesAvailabilityInteractor by
+    Kosmos.Fixture {
+        NewTilesAvailabilityInteractor(tileAvailabilityInteractorsMap, userRepository)
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt
index 75ca311..4aa4a2b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/toolbar/ToolbarViewModelKosmos.kt
@@ -18,6 +18,7 @@
 
 import android.content.applicationContext
 import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.development.ui.viewmodel.buildNumberViewModelFactory
 import com.android.systemui.globalactions.globalActionsDialogLite
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.footerActionsInteractor
@@ -29,6 +30,7 @@
             override fun create(): ToolbarViewModel {
                 return ToolbarViewModel(
                     editModeButtonViewModelFactory,
+                    buildNumberViewModelFactory,
                     footerActionsInteractor,
                     { globalActionsDialogLite },
                     falsingInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
index d97a5b2..4823607 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
@@ -29,7 +29,7 @@
 import com.android.systemui.qs.pipeline.shared.logging.qsLogger
 import com.android.systemui.qs.pipeline.shared.pipelineFlagsRepository
 import com.android.systemui.qs.qsTileFactory
-import com.android.systemui.qs.tiles.di.newQSTileFactory
+import com.android.systemui.qs.tiles.base.ui.model.newQSTileFactory
 import com.android.systemui.settings.userTracker
 import com.android.systemui.user.data.repository.userRepository
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/FakeQSTileIntentUserInputHandler.kt
similarity index 89%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/FakeQSTileIntentUserInputHandler.kt
index f50443e..1319787 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/FakeQSTileIntentUserInputHandler.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.qs.tiles.base.domain.actions
 
 import android.app.PendingIntent
 import android.content.Intent
@@ -34,7 +34,7 @@
     override fun handle(
         expandable: Expandable?,
         intent: Intent,
-        handleDismissShadeShowOverLockScreenWhenLocked: Boolean
+        handleDismissShadeShowOverLockScreenWhenLocked: Boolean,
     ) {
         mutableInputs.add(Input.Intent(expandable, intent))
     }
@@ -42,7 +42,7 @@
     override fun handle(
         expandable: Expandable?,
         pendingIntent: PendingIntent,
-        requestLaunchingDefaultActivity: Boolean
+        requestLaunchingDefaultActivity: Boolean,
     ) {
         mutableInputs.add(
             Input.PendingIntent(expandable, pendingIntent, requestLaunchingDefaultActivity)
@@ -51,10 +51,11 @@
 
     sealed interface Input {
         data class Intent(val expandable: Expandable?, val intent: android.content.Intent) : Input
+
         data class PendingIntent(
             val expandable: Expandable?,
             val pendingIntent: android.app.PendingIntent,
-            val requestLaunchingDefaultActivity: Boolean
+            val requestLaunchingDefaultActivity: Boolean,
         ) : Input
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerKosmos.kt
similarity index 92%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerKosmos.kt
index ccfb609..b884b8a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerKosmos.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.qs.tiles.base.domain.actions
 
 import com.android.systemui.kosmos.Kosmos
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerSubject.kt
similarity index 90%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerSubject.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerSubject.kt
index e09f280..3b7810e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerSubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerSubject.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.qs.tiles.base.domain.actions
 
 import com.google.common.truth.FailureMetadata
 import com.google.common.truth.Subject
@@ -24,11 +24,11 @@
 class QSTileIntentUserInputHandlerSubject
 private constructor(
     failureMetadata: FailureMetadata,
-    private val subject: FakeQSTileIntentUserInputHandler
+    private val subject: FakeQSTileIntentUserInputHandler,
 ) : Subject(failureMetadata, subject) {
 
     fun handledOneIntentInput(
-        intentAssertions: (FakeQSTileIntentUserInputHandler.Input.Intent) -> Unit = {},
+        intentAssertions: (FakeQSTileIntentUserInputHandler.Input.Intent) -> Unit = {}
     ) {
         // check that there are no other inputs
         check("handledInputs").that(subject.handledInputs).hasSize(1)
@@ -39,7 +39,7 @@
     }
 
     fun handledOnePendingIntentInput(
-        intentAssertions: (FakeQSTileIntentUserInputHandler.Input.PendingIntent) -> Unit = {},
+        intentAssertions: (FakeQSTileIntentUserInputHandler.Input.PendingIntent) -> Unit = {}
     ) {
         // check that there are no other inputs
         check("handledInputs").that(subject.handledInputs).hasSize(1)
@@ -68,8 +68,8 @@
         ): QSTileIntentUserInputHandlerSubject =
             Truth.assertAbout {
                     failureMetadata: FailureMetadata,
-                    subject: FakeQSTileIntentUserInputHandler ->
-                    QSTileIntentUserInputHandlerSubject(failureMetadata, subject)
+                    subject: FakeQSTileIntentUserInputHandler? ->
+                    QSTileIntentUserInputHandlerSubject(failureMetadata, subject!!)
                 }
                 .that(handler)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorKosmos.kt
similarity index 92%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorKosmos.kt
index 9ad49f0..2ffc479 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorKosmos.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeDisabledByPolicyInteractor.kt
similarity index 94%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeDisabledByPolicyInteractor.kt
index fb6ba20..5bdeeab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeDisabledByPolicyInteractor.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
 
 import android.os.UserHandle
 import com.android.settingslib.RestrictedLockUtils
@@ -23,7 +23,7 @@
 
     override suspend fun isDisabled(
         user: UserHandle,
-        userRestriction: String?
+        userRestriction: String?,
     ): DisabledByPolicyInteractor.PolicyResult =
         if (userRestriction == DISABLED_RESTRICTION || userRestriction == DISABLED_RESTRICTION_2) {
             DisabledByPolicyInteractor.PolicyResult.TileDisabled(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileDataInteractor.kt
similarity index 94%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileDataInteractor.kt
index 3fcf8a9..6bd8152 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileDataInteractor.kt
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
 
 import android.os.UserHandle
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -26,6 +27,7 @@
     private val dataFlow: MutableSharedFlow<T> = MutableSharedFlow(replay = 1)
     val dataSubscriptionCount
         get() = dataFlow.subscriptionCount
+
     private val availabilityFlow: MutableSharedFlow<Boolean> = MutableSharedFlow(replay = 1)
     val availabilitySubscriptionCount
         get() = availabilityFlow.subscriptionCount
@@ -42,6 +44,7 @@
     suspend fun emitData(data: T): Unit = dataFlow.emit(data)
 
     fun tryEmitAvailability(isAvailable: Boolean): Boolean = availabilityFlow.tryEmit(isAvailable)
+
     suspend fun emitAvailability(isAvailable: Boolean) = availabilityFlow.emit(isAvailable)
 
     override fun tileData(user: UserHandle, triggers: Flow<DataUpdateTrigger>): Flow<T> {
@@ -58,5 +61,6 @@
     }
 
     data class DataRequest(val user: UserHandle)
+
     data class AvailabilityRequest(val user: UserHandle)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileUserActionInteractor.kt
similarity index 86%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileUserActionInteractor.kt
index c058490..8fd87a3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileUserActionInteractor.kt
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
 
-import com.android.systemui.plugins.qs.TileDetailsViewModel
-import com.android.systemui.qs.FakeTileDetailsViewModel
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInputTestKtx.kt
similarity index 87%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInputTestKtx.kt
index 3943d1d..5c2b4a6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInputTestKtx.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.model
 
 import android.os.UserHandle
 import com.android.systemui.animation.Expandable
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
 
 object QSTileInputTestKtx {
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/FakeQSTileConfigProvider.kt
similarity index 90%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/FakeQSTileConfigProvider.kt
index d231d63..97e6583 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/FakeQSTileConfigProvider.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
 
 import com.android.systemui.qs.pipeline.shared.TileSpec
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderKosmos.kt
similarity index 87%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderKosmos.kt
index 1d57979..d1b410c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
 
 import com.android.systemui.kosmos.Kosmos
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigTestBuilder.kt
similarity index 79%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigTestBuilder.kt
index 73d9b32..8938ebc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigTestBuilder.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
 
 import com.android.internal.logging.InstanceId
 import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -33,14 +33,6 @@
         var policy: QSTilePolicy = QSTilePolicy.NoRestrictions
         var category: TileCategory = TileCategory.UNKNOWN
 
-        fun build() =
-            QSTileConfig(
-                tileSpec,
-                uiConfig,
-                instanceId,
-                category,
-                metricsSpec,
-                policy,
-            )
+        fun build() = QSTileConfig(tileSpec, uiConfig, instanceId, category, metricsSpec, policy)
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsKosmos.kt
similarity index 93%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsKosmos.kt
index 146c1ad..753824b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsKosmos.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.analytics
+package com.android.systemui.qs.tiles.base.ui.analytics
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactoryKosmos.kt
similarity index 80%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactoryKosmos.kt
index c223be4..3c29730 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactoryKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.di
+package com.android.systemui.qs.tiles.base.ui.model
 
 import android.os.UserHandle
 import com.android.systemui.kosmos.Kosmos
@@ -22,14 +22,14 @@
 import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.shared.model.TileCategory
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.qsTileViewModelAdaperFactory
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.qSTileConfigProvider
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.qsTileViewModelAdaperFactory
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/QSTileStateSubject.kt
similarity index 83%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/QSTileStateSubject.kt
index aa29808..54e85f1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/QSTileStateSubject.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom
+package com.android.systemui.qs.tiles.base.ui.model
 
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject.Companion.assertThat
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject.Companion.states
-import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
 import com.google.common.truth.FailureMetadata
 import com.google.common.truth.Subject
-import com.google.common.truth.Subject.Factory
 import com.google.common.truth.Truth
 
 /**
@@ -63,7 +59,7 @@
     companion object {
 
         /** Returns a factory to be used with [Truth.assertAbout]. */
-        fun states(): Factory<QSTileStateSubject, QSTileState?> {
+        fun states(): Factory<QSTileStateSubject, QSTileState> {
             return Factory { failureMetadata: FailureMetadata, subject: QSTileState? ->
                 QSTileStateSubject(failureMetadata, subject)
             }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapterKosmos.kt
similarity index 91%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapterKosmos.kt
index de9f629..cbadf8e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapterKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
index 42437d5a..77f561e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
@@ -22,11 +22,12 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.activityStarter
 import com.android.systemui.qs.external.FakeCustomTileStatePersister
-import com.android.systemui.qs.external.tileServices
 import com.android.systemui.qs.external.tileServicesFacade
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
 import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTilePackageUpdatesRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileRepository
@@ -34,8 +35,6 @@
 import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileInteractor
 import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileServiceInteractor
 import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileUserActionInteractor
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
 import com.android.systemui.user.data.repository.userRepository
 import com.android.systemui.util.mockito.mock
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt
index d2351dc..cc8b44b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt
@@ -59,7 +59,7 @@
     companion object {
 
         /** Returns a factory to be used with [Truth.assertAbout]. */
-        fun tiles(): Factory<TileSubject, Tile?> {
+        fun tiles(): Factory<TileSubject, Tile> {
             return Factory { failureMetadata: FailureMetadata, subject: Tile? ->
                 TileSubject(failureMetadata, subject)
             }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
index ccba072..7b9d601 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
@@ -18,7 +18,7 @@
 
 import android.content.ComponentName
 import android.os.UserHandle
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
 import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -31,7 +31,7 @@
     private val defaultsFlow =
         MutableSharedFlow<DefaultsRequest>(
             replay = 1,
-            onBufferOverflow = BufferOverflow.DROP_OLDEST
+            onBufferOverflow = BufferOverflow.DROP_OLDEST,
         )
 
     private val mutableDefaultsRequests: MutableList<DefaultsRequest> = mutableListOf()
@@ -51,7 +51,7 @@
     override fun requestNewDefaults(
         user: UserHandle,
         componentName: ComponentName,
-        force: Boolean
+        force: Boolean,
     ) {
         val request = DefaultsRequest(user, componentName, force)
         mutableDefaultsRequests.add(request)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
index c110da0..c9ed71c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
@@ -20,7 +20,7 @@
 import android.service.quicksettings.Tile
 import com.android.systemui.qs.external.FakeCustomTileStatePersister
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.flow.Flow
 
@@ -54,11 +54,8 @@
 
     override suspend fun isTileToggleable(): Boolean = realDelegate.isTileToggleable()
 
-    override suspend fun updateWithTile(
-        user: UserHandle,
-        newTile: Tile,
-        isPersistable: Boolean,
-    ) = realDelegate.updateWithTile(user, newTile, isPersistable)
+    override suspend fun updateWithTile(user: UserHandle, newTile: Tile, isPersistable: Boolean) =
+        realDelegate.updateWithTile(user, newTile, isPersistable)
 
     override suspend fun updateWithDefaults(
         user: UserHandle,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
index 3f07d05..f94da17 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
@@ -18,7 +18,7 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.mainCoroutineContext
-import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.qsTileIntentUserInputHandler
 import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
 import com.android.systemui.statusbar.policy.ui.dialog.modesDialogDelegate
 import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
index 537be4f..5cd11e0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
@@ -23,17 +23,17 @@
 import com.android.systemui.kosmos.backgroundCoroutineContext
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule
 import com.android.systemui.qrcodescanner.qrCodeScannerController
 import com.android.systemui.qs.qsEventLogger
-import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.analytics.qsTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.fakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.qs.tiles.base.domain.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.fakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.ui.analytics.qsTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelImpl
 import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
 import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor
 import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper
+import com.android.systemui.qs.tiles.impl.qr.ui.mapper.QRCodeScannerTileMapper
+import com.android.systemui.qs.tiles.impl.qr.ui.model.QRCodeScannerModule
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.time.systemClock
 
@@ -45,7 +45,7 @@
         QRCodeScannerTileDataInteractor(
             backgroundCoroutineContext,
             applicationCoroutineScope,
-            qrCodeScannerController
+            qrCodeScannerController,
         )
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/uimodenight/UiModeNightTileModelHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/uimodenight/UiModeNightTileModelHelper.kt
index 1fe18e3..0045960 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/uimodenight/UiModeNightTileModelHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/uimodenight/UiModeNightTileModelHelper.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.qs.tiles.impl.uimodenight
 
 import android.content.res.Configuration
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
 import java.time.LocalTime
 
 object UiModeNightTileModelHelper {
@@ -34,7 +34,7 @@
         nighModeCustomType: Int = DEFAULT_NIGHT_MODE_CUSTOM_TYPE,
         is24HourFormat: Boolean = false,
         customNightModeEnd: LocalTime = defaultCustomNightEnd,
-        customNightModeStart: LocalTime = defaultCustomNightStart
+        customNightModeStart: LocalTime = defaultCustomNightStart,
     ): UiModeNightTileModel {
         return UiModeNightTileModel(
             uiMode,
@@ -44,7 +44,7 @@
             nighModeCustomType,
             is24HourFormat,
             customNightModeEnd,
-            customNightModeStart
+            customNightModeStart,
         )
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationEntryBuilderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationEntryBuilderKosmos.kt
deleted file mode 100644
index 59f5ecd..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationEntryBuilderKosmos.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification
-
-import android.app.Notification
-import android.app.PendingIntent
-import android.app.Person
-import android.content.Intent
-import android.content.applicationContext
-import android.graphics.drawable.Icon
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.icon.IconPack
-import com.android.systemui.statusbar.notification.promoted.setPromotedContent
-import org.mockito.kotlin.mock
-
-fun Kosmos.setIconPackWithMockIconViews(entry: NotificationEntry) {
-    entry.icons =
-        IconPack.buildPack(
-            /* statusBarIcon = */ mock(),
-            /* statusBarChipIcon = */ mock(),
-            /* shelfIcon = */ mock(),
-            /* aodIcon = */ mock(),
-            /* source = */ null,
-        )
-}
-
-fun Kosmos.buildOngoingCallEntry(
-    promoted: Boolean = false,
-    block: NotificationEntryBuilder.() -> Unit = {},
-): NotificationEntry =
-    buildNotificationEntry(
-        tag = "call",
-        promoted = promoted,
-        style = makeOngoingCallStyle(),
-        block = block,
-    )
-
-fun Kosmos.buildPromotedOngoingEntry(
-    block: NotificationEntryBuilder.() -> Unit = {}
-): NotificationEntry =
-    buildNotificationEntry(tag = "ron", promoted = true, style = null, block = block)
-
-fun Kosmos.buildNotificationEntry(
-    tag: String? = null,
-    promoted: Boolean = false,
-    style: Notification.Style? = null,
-    block: NotificationEntryBuilder.() -> Unit = {},
-): NotificationEntry =
-    NotificationEntryBuilder()
-        .apply {
-            setTag(tag)
-            setFlag(applicationContext, Notification.FLAG_PROMOTED_ONGOING, promoted)
-            modifyNotification(applicationContext)
-                .setSmallIcon(Icon.createWithContentUri("content://null"))
-                .setStyle(style)
-        }
-        .apply(block)
-        .build()
-        .also {
-            setIconPackWithMockIconViews(it)
-            if (promoted) setPromotedContent(it)
-        }
-
-private fun Kosmos.makeOngoingCallStyle(): Notification.CallStyle {
-    val pendingIntent =
-        PendingIntent.getBroadcast(
-            applicationContext,
-            0,
-            Intent("action"),
-            PendingIntent.FLAG_IMMUTABLE,
-        )
-    val person = Person.Builder().setName("person").build()
-    return Notification.CallStyle.forOngoingCall(person, pendingIntent)
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
index 4efcada..215df9d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
@@ -58,7 +58,7 @@
         return this;
     }
 
-    /** Sets the creation time. */
+    /** Sets the creation time. Should be SystemClock.elapsedRealtime */
     public GroupEntryBuilder setCreationTime(long creationTime) {
         mCreationTime = creationTime;
         return this;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilderKosmos.kt
new file mode 100644
index 0000000..00c6c94
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilderKosmos.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.PendingIntent
+import android.app.Person
+import android.content.Intent
+import android.content.applicationContext
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import com.android.systemui.activity.EmptyTestActivity
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.icon.IconPack
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
+import com.android.systemui.statusbar.notification.promoted.setPromotedContent
+import org.mockito.kotlin.mock
+
+fun Kosmos.setIconPackWithMockIconViews(entry: NotificationEntry) {
+    entry.icons =
+        IconPack.buildPack(
+            /* statusBarIcon = */ mock(),
+            /* statusBarChipIcon = */ mock(),
+            /* shelfIcon = */ mock(),
+            /* aodIcon = */ mock(),
+            /* source = */ null,
+        )
+}
+
+fun Kosmos.buildPromotedOngoingEntry(
+    block: NotificationEntryBuilder.() -> Unit = {}
+): NotificationEntry =
+    buildNotificationEntry(tag = "ron", promoted = true, style = null, block = block)
+
+fun Kosmos.buildOngoingCallEntry(
+    promoted: Boolean = false,
+    block: NotificationEntryBuilder.() -> Unit = {},
+): NotificationEntry =
+    buildNotificationEntry(
+        tag = "call",
+        promoted = promoted,
+        style = makeOngoingCallStyle(),
+        block = block,
+    )
+
+fun Kosmos.buildNotificationEntry(
+    tag: String? = null,
+    promoted: Boolean = false,
+    style: Notification.Style? = null,
+    block: NotificationEntryBuilder.() -> Unit = {},
+): NotificationEntry =
+    NotificationEntryBuilder()
+        .apply {
+            setTag(tag)
+            setFlag(applicationContext, Notification.FLAG_PROMOTED_ONGOING, promoted)
+            modifyNotification(applicationContext)
+                .setSmallIcon(Icon.createWithContentUri("content://null"))
+                .setStyle(style)
+        }
+        .apply(block)
+        .build()
+        .also {
+            setIconPackWithMockIconViews(it)
+            if (promoted) setPromotedContent(it)
+        }
+
+private fun Kosmos.makeOngoingCallStyle(): Notification.CallStyle {
+    val pendingIntent =
+        PendingIntent.getBroadcast(
+            applicationContext,
+            0,
+            Intent("action"),
+            PendingIntent.FLAG_IMMUTABLE,
+        )
+    val person = Person.Builder().setName("person").build()
+    return Notification.CallStyle.forOngoingCall(person, pendingIntent)
+}
+
+private fun Kosmos.makeMessagingStyleNotification(): Notification.Builder {
+    val personIcon = Icon.createWithBitmap(Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888))
+    val person = Person.Builder().setIcon(personIcon).setName("Person").build()
+    val message = Notification.MessagingStyle.Message("Message!", 4323, person)
+    val bubbleIntent =
+        PendingIntent.getActivity(
+            applicationContext,
+            0,
+            Intent(applicationContext, EmptyTestActivity::class.java),
+            PendingIntent.FLAG_MUTABLE,
+        )
+
+    return Notification.Builder(applicationContext, "channelId")
+        .setSmallIcon(R.drawable.ic_person)
+        .setContentTitle("Title")
+        .setContentText("Text")
+        .setStyle(Notification.MessagingStyle(person).addMessage(message))
+        .setBubbleMetadata(
+            Notification.BubbleMetadata.Builder(
+                    bubbleIntent,
+                    Icon.createWithResource(applicationContext, R.drawable.android),
+                )
+                .setDeleteIntent(mock<PendingIntent>())
+                .setDesiredHeight(314)
+                .setAutoExpandBubble(false)
+                .build()
+        )
+}
+
+fun Kosmos.makeEntryOfPeopleType(@PeopleNotificationType type: Int): NotificationEntryBuilder {
+    val channel = NotificationChannel("messages", "messages", IMPORTANCE_DEFAULT)
+    channel.isImportantConversation = (type == TYPE_IMPORTANT_PERSON)
+    channel.setConversationId("parent", "convo")
+
+    val entry =
+        NotificationEntryBuilder().apply {
+            updateRanking {
+                it.setIsConversation(type != TYPE_NON_PERSON)
+                it.setShortcutInfo(if (type >= TYPE_FULL_PERSON) mock() else null)
+                it.setChannel(channel)
+            }
+            setNotification(makeMessagingStyleNotification().build())
+        }
+    return entry
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryKosmos.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryKosmos.kt
index ccfb609..e127a70 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.statusbar.notification.collection
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
 
-val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
+val Kosmos.msgStyleBubbleableFullPerson by
+    Kosmos.Fixture { makeEntryOfPeopleType(TYPE_FULL_PERSON).build() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
index ccfb609..38b5994 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PackageDemotionInteractorKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.statusbar.notification.promoted.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
+val Kosmos.packageDemotionInteractor by Kosmos.Fixture { PackageDemotionInteractor() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt
index e99f61e..067e420 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt
@@ -25,12 +25,13 @@
 import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
 
 val Kosmos.entryAdapterFactory by
-Kosmos.Fixture {
-    EntryAdapterFactoryImpl(
-        mockNotificationActivityStarter,
-        metricsLogger,
-        peopleNotificationIdentifier,
-        notificationIconStyleProvider,
-        visualStabilityCoordinator,
-    )
-}
\ No newline at end of file
+    Kosmos.Fixture {
+        EntryAdapterFactoryImpl(
+            mockNotificationActivityStarter,
+            metricsLogger,
+            peopleNotificationIdentifier,
+            notificationIconStyleProvider,
+            visualStabilityCoordinator,
+            mockNotificationActionClickManager,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index ff4fbf9..6a674ca 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.media.NotificationMediaManager
 import com.android.systemui.media.controls.util.MediaFeatureFlag
 import com.android.systemui.media.dialog.MediaOutputDialogManager
 import com.android.systemui.plugins.ActivityStarter
@@ -45,7 +46,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper
 import com.android.systemui.shared.system.PackageManagerWrapper
-import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.statusbar.NotificationRemoteInputManager
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.RankingBuilder
@@ -375,6 +375,7 @@
                     Mockito.mock(PeopleNotificationIdentifier::class.java),
                     Mockito.mock(NotificationIconStyleProvider::class.java),
                     Mockito.mock(VisualStabilityCoordinator::class.java),
+                    Mockito.mock(NotificationActionClickManager::class.java),
                 )
                 .create(entry)
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationActionClickManagerKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationActionClickManagerKosmos.kt
index ccfb609..8e62ae8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationActionClickManagerKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.statusbar.notification.row
 
 import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
 
-val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
+var Kosmos.mockNotificationActionClickManager: NotificationActionClickManager by
+    Kosmos.Fixture { mock<NotificationActionClickManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
index 7ccbdb7..2523975 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
@@ -19,11 +19,9 @@
 import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
 
 val Kosmos.activatableNotificationViewModel by Fixture {
     ActivatableNotificationViewModel.invoke(
         a11yInteractor = accessibilityInteractor,
-        windowRootViewBlurInteractor = windowRootViewBlurInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt
index b906b60..81b1349 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt
@@ -20,10 +20,12 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.activatableNotificationViewModel
 import com.android.systemui.statusbar.notification.shelf.domain.interactor.notificationShelfInteractor
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
 
 val Kosmos.notificationShelfViewModel by Fixture {
     NotificationShelfViewModel(
         interactor = notificationShelfInteractor,
+        windowRootViewBlurInteractor = windowRootViewBlurInteractor,
         activatableViewModel = activatableNotificationViewModel,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
index 8ff7c7d..3e96fd7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.phone.ongoingcall.shared.model
 
 import android.app.PendingIntent
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
@@ -38,6 +40,7 @@
     notificationKey: String = "test",
     appName: String = "",
     promotedContent: PromotedNotificationContentModel? = null,
+    isAppVisible: Boolean = false,
 ) =
     OngoingCallModel.InCall(
         startTimeMs,
@@ -46,6 +49,7 @@
         notificationKey,
         appName,
         promotedContent,
+        isAppVisible,
     )
 
 object OngoingCallTestHelper {
@@ -77,8 +81,10 @@
         contentIntent: PendingIntent? = null,
         uid: Int = DEFAULT_UID,
         appName: String = "Fake name",
+        isAppVisible: Boolean = false,
     ) {
         if (StatusBarChipsModernization.isEnabled) {
+            activityManagerRepository.fake.startingIsAppVisibleValue = isAppVisible
             activeNotificationListRepository.addNotif(
                 activeNotificationModel(
                     key = key,
@@ -100,6 +106,7 @@
                     notificationKey = key,
                     appName = appName,
                     promotedContent = promotedContent,
+                    isAppVisible = isAppVisible,
                 )
             )
         }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileDomainInteractorKairosKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileDomainInteractorKairosKosmos.kt
new file mode 100644
index 0000000..d9a327b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileDomainInteractorKairosKosmos.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
+
+import android.content.applicationContext
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kairos.ActivatedKairosFixture
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.table.logcatTableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.policy.data.repository.userSetupRepository
+import com.android.systemui.util.carrierConfigTracker
+
+@ExperimentalKairosApi
+val Kosmos.mobileIconsInteractorKairos by ActivatedKairosFixture {
+    MobileIconsInteractorKairosImpl(
+        mobileConnectionsRepositoryKairos,
+        carrierConfigTracker,
+        logcatTableLogBuffer(this),
+        connectivityRepository,
+        userSetupRepository,
+        applicationContext,
+        featureFlagsClassic,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt
similarity index 70%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt
index ccfb609..3ee3380 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
+val Kosmos.stackedMobileIconViewModelKairos by
+    Kosmos.Fixture { StackedMobileIconViewModelKairos(mobileIconsViewModel) }
diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/topwindoweffects/data/repository/FakeSqueezeEffectRepository.kt
similarity index 65%
copy from media/java/android/media/quality/PictureProfileHandle.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/topwindoweffects/data/repository/FakeSqueezeEffectRepository.kt
index 5d14631..2d2a8158 100644
--- a/media/java/android/media/quality/PictureProfileHandle.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/topwindoweffects/data/repository/FakeSqueezeEffectRepository.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,6 +14,10 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package com.android.systemui.topwindoweffects.data.repository
 
-parcelable PictureProfileHandle;
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeSqueezeEffectRepository : SqueezeEffectRepository {
+    override val isSqueezeEffectEnabled = MutableStateFlow(false)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryKosmos.kt
similarity index 74%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryKosmos.kt
index ccfb609..aa8bb6b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/topwindoweffects/data/repository/SqueezeEffectRepositoryKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open 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,8 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.topwindoweffects.data.repository
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
+val Kosmos.fakeSqueezeEffectRepository by Kosmos.Fixture { FakeSqueezeEffectRepository() }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/truth/correspondence/FakeUiEvent.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/truth/correspondence/FakeUiEvent.kt
index 48cd345..6c10526 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/truth/correspondence/FakeUiEvent.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/truth/correspondence/FakeUiEvent.kt
@@ -24,7 +24,7 @@
 object FakeUiEvent {
     val EVENT_ID =
         Correspondence.transforming<FakeUiEvent, Int>(
-            { it?.eventId },
+            { it.eventId },
             "has a eventId of",
         )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/truth/correspondence/LogMaker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/truth/correspondence/LogMaker.kt
index 3f0a9524..9664bf3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/truth/correspondence/LogMaker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/truth/correspondence/LogMaker.kt
@@ -23,7 +23,7 @@
 object LogMaker {
     val CATEGORY =
         Correspondence.transforming<LogMaker, Int>(
-            { it?.category },
+            { it.category },
             "has a category of",
         )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt
index 933c351..6bb908a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt
@@ -22,5 +22,9 @@
 
 val Kosmos.userLockedInteractor by
     Kosmos.Fixture {
-        UserLockedInteractor(backgroundDispatcher = testDispatcher, userRepository = userRepository)
+        UserLockedInteractor(
+            backgroundDispatcher = testDispatcher,
+            userRepository = userRepository,
+            selectedUserInteractor = selectedUserInteractor,
+        )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/CarrierConfigTrackerKosmos.kt
similarity index 80%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/util/CarrierConfigTrackerKosmos.kt
index ccfb609..5847369 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/CarrierConfigTrackerKosmos.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.util
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mockFixture
 
-val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
+var Kosmos.carrierConfigTracker: CarrierConfigTracker by mockFixture()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
index f31697e8..a6332bf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
@@ -289,6 +289,37 @@
         return putString(name, value)
     }
 
+    override fun getInt(name: String): Int {
+        return getIntForUser(name, userId)
+    }
+
+    override fun getInt(name: String, default: Int): Int {
+        return getIntForUser(name, default, userId)
+    }
+
+    override fun getIntForUser(name: String, userHandle: Int): Int {
+        return getIntForUser(name, 0, userHandle)
+    }
+
+    override fun getIntForUser(name: String, default: Int, userHandle: Int): Int {
+        return values[SettingsKey(userHandle, getUriFor(name).toString())]?.toInt() ?: default
+    }
+
+    override fun putIntForUser(name: String, value: Int, userHandle: Int): Boolean {
+        val key = SettingsKey(userHandle, getUriFor(name).toString())
+        values[key] = value.toString()
+        val uri = getUriFor(name)
+        contentObservers[key]?.onEach { it.dispatchChange(false, listOf(uri), 0, userHandle) }
+        contentObserversAllUsers[uri.toString()]?.onEach {
+            it.dispatchChange(false, listOf(uri), 0, userHandle)
+        }
+        return true
+    }
+
+    override fun putInt(name: String, value: Int): Boolean {
+        return putIntForUser(name, value, userId)
+    }
+
     /** Runs current jobs on dispatcher after calling the method. */
     private fun <T> advanceDispatcher(f: () -> T): T {
         val result = f()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
index 66bb803..fd90d17 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -29,6 +29,9 @@
 class FakeWallpaperRepository : WallpaperRepository {
     private val _wallpaperInfo: MutableStateFlow<WallpaperInfo?> = MutableStateFlow(null)
     override val wallpaperInfo: StateFlow<WallpaperInfo?> = _wallpaperInfo.asStateFlow()
+    private val _lockscreenWallpaperInfo: MutableStateFlow<WallpaperInfo?> = MutableStateFlow(null)
+    override val lockscreenWallpaperInfo: StateFlow<WallpaperInfo?> =
+        _lockscreenWallpaperInfo.asStateFlow()
     private val _wallpaperSupportsAmbientMode = MutableStateFlow(false)
     override val wallpaperSupportsAmbientMode: Flow<Boolean> =
         _wallpaperSupportsAmbientMode.asStateFlow()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt
index 1761503..b4a4475 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryKosmos.kt
@@ -19,6 +19,7 @@
 import android.content.applicationContext
 import com.android.app.wallpaperManager
 import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testDispatcher
@@ -35,5 +36,6 @@
         userRepository = userRepository,
         wallpaperManager = wallpaperManager,
         secureSettings = fakeSettings,
+        configurationInteractor = configurationInteractor,
     )
 }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
index b956e44..90a9271 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
@@ -281,7 +281,6 @@
                             },
                     )
                 }
-                downstreamSet.clear()
             }
         }
         reset()
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
index c11eb12..81f3702 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
@@ -145,7 +145,14 @@
                 val conn = branchNode.upstream
                 severed.add(conn)
                 conn.removeDownstream(downstream = branchNode.schedulable)
-                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                if (conn.depthTracker.snapshotIsDirect) {
+                    depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                } else {
+                    depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+                    depthTracker.updateIndirectRoots(
+                        removals = conn.depthTracker.snapshotIndirectRoots
+                    )
+                }
             }
         }
 
@@ -156,7 +163,14 @@
                 val conn = branchNode.upstream
                 severed.add(conn)
                 conn.removeDownstream(downstream = branchNode.schedulable)
-                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                if (conn.depthTracker.snapshotIsDirect) {
+                    depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                } else {
+                    depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+                    depthTracker.updateIndirectRoots(
+                        removals = conn.depthTracker.snapshotIndirectRoots
+                    )
+                }
             }
 
             // add new
@@ -343,13 +357,8 @@
                 val (patchesConn, needsEval) =
                     getPatches(evalScope).activate(evalScope, downstream = muxNode.schedulable)
                         ?: run {
-                            // Turns out we can't connect to patches, so update our depth and
-                            // propagate
-                            if (muxNode.depthTracker.setIsIndirectRoot(false)) {
-                                // TODO: schedules might not be necessary now that we're not
-                                // parallel?
-                                muxNode.depthTracker.schedule(evalScope.scheduler, muxNode)
-                            }
+                            // Turns out we can't connect to patches, so update our depth
+                            muxNode.depthTracker.setIsIndirectRoot(false)
                             return
                         }
                 muxNode.patches = patchesConn
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
index cb2c6e5..faef6a2 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
@@ -109,7 +109,14 @@
                 val conn: NodeConnection<V> = branchNode.upstream
                 severed.add(conn)
                 conn.removeDownstream(downstream = branchNode.schedulable)
-                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                if (conn.depthTracker.snapshotIsDirect) {
+                    depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                } else {
+                    depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+                    depthTracker.updateIndirectRoots(
+                        removals = conn.depthTracker.snapshotIndirectRoots
+                    )
+                }
             }
         }
 
@@ -123,7 +130,14 @@
                 val conn: NodeConnection<V> = oldBranch.upstream
                 severed.add(conn)
                 conn.removeDownstream(downstream = oldBranch.schedulable)
-                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                if (conn.depthTracker.snapshotIsDirect) {
+                    depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                } else {
+                    depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+                    depthTracker.updateIndirectRoots(
+                        removals = conn.depthTracker.snapshotIndirectRoots
+                    )
+                }
             }
 
             // add new
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index ccbc46f..5424ac3b 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -16,7 +16,7 @@
     srcs: [
         "texts/ravenwood-common-policies.txt",
     ],
-    visibility: ["//visibility:private"],
+    visibility: [":__subpackages__"],
 }
 
 filegroup {
@@ -44,6 +44,22 @@
 }
 
 filegroup {
+    name: "ravenwood-standard-annotations",
+    srcs: [
+        "texts/ravenwood-standard-annotations.txt",
+    ],
+    visibility: [":__subpackages__"],
+}
+
+filegroup {
+    name: "ravenizer-standard-options",
+    srcs: [
+        "texts/ravenizer-standard-options.txt",
+    ],
+    visibility: [":__subpackages__"],
+}
+
+filegroup {
     name: "ravenwood-annotation-allowed-classes",
     srcs: [
         "texts/ravenwood-annotation-allowed-classes.txt",
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index e366771..f5b075b 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -33,6 +33,7 @@
         ":ravenwood-common-policies",
         ":ravenwood-framework-policies",
         ":ravenwood-standard-options",
+        ":ravenwood-standard-annotations",
         ":ravenwood-annotation-allowed-classes",
     ],
     out: [
@@ -44,6 +45,7 @@
 
 framework_minus_apex_cmd = "$(location hoststubgen) " +
     "@$(location :ravenwood-standard-options) " +
+    "@$(location :ravenwood-standard-annotations) " +
     "--debug-log $(location hoststubgen_framework-minus-apex.log) " +
     "--out-jar $(location ravenwood.jar) " +
     "--in-jar $(location :framework-minus-apex-for-host) " +
@@ -178,6 +180,7 @@
     tools: ["hoststubgen"],
     cmd: "$(location hoststubgen) " +
         "@$(location :ravenwood-standard-options) " +
+        "@$(location :ravenwood-standard-annotations) " +
 
         "--debug-log $(location hoststubgen_services.core.log) " +
         "--stats-file $(location hoststubgen_services.core_stats.csv) " +
@@ -196,6 +199,7 @@
         ":ravenwood-common-policies",
         ":ravenwood-services-policies",
         ":ravenwood-standard-options",
+        ":ravenwood-standard-annotations",
         ":ravenwood-annotation-allowed-classes",
     ],
     out: [
@@ -247,6 +251,7 @@
     tools: ["hoststubgen"],
     cmd: "$(location hoststubgen) " +
         "@$(location :ravenwood-standard-options) " +
+        "@$(location :ravenwood-standard-annotations) " +
 
         "--debug-log $(location hoststubgen_core-icu4j-for-host.log) " +
         "--stats-file $(location hoststubgen_core-icu4j-for-host_stats.csv) " +
@@ -265,6 +270,7 @@
         ":ravenwood-common-policies",
         ":icu-ravenwood-policies",
         ":ravenwood-standard-options",
+        ":ravenwood-standard-annotations",
     ],
     out: [
         "ravenwood.jar",
@@ -301,6 +307,7 @@
     tools: ["hoststubgen"],
     cmd: "$(location hoststubgen) " +
         "@$(location :ravenwood-standard-options) " +
+        "@$(location :ravenwood-standard-annotations) " +
 
         "--debug-log $(location framework-configinfrastructure.log) " +
         "--stats-file $(location framework-configinfrastructure_stats.csv) " +
@@ -319,6 +326,7 @@
         ":ravenwood-common-policies",
         ":framework-configinfrastructure-ravenwood-policies",
         ":ravenwood-standard-options",
+        ":ravenwood-standard-annotations",
     ],
     out: [
         "ravenwood.jar",
@@ -355,6 +363,7 @@
     tools: ["hoststubgen"],
     cmd: "$(location hoststubgen) " +
         "@$(location :ravenwood-standard-options) " +
+        "@$(location :ravenwood-standard-annotations) " +
 
         "--debug-log $(location framework-statsd.log) " +
         "--stats-file $(location framework-statsd_stats.csv) " +
@@ -373,6 +382,7 @@
         ":ravenwood-common-policies",
         ":framework-statsd-ravenwood-policies",
         ":ravenwood-standard-options",
+        ":ravenwood-standard-annotations",
     ],
     out: [
         "ravenwood.jar",
@@ -409,6 +419,7 @@
     tools: ["hoststubgen"],
     cmd: "$(location hoststubgen) " +
         "@$(location :ravenwood-standard-options) " +
+        "@$(location :ravenwood-standard-annotations) " +
 
         "--debug-log $(location framework-graphics.log) " +
         "--stats-file $(location framework-graphics_stats.csv) " +
@@ -427,6 +438,7 @@
         ":ravenwood-common-policies",
         ":framework-graphics-ravenwood-policies",
         ":ravenwood-standard-options",
+        ":ravenwood-standard-annotations",
     ],
     out: [
         "ravenwood.jar",
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index df63cb9..083d2aa 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -150,6 +150,10 @@
       "host": true
     },
     {
+      "name": "RavenwoodCoreTest-light",
+      "host": true
+    },
+    {
       "name": "RavenwoodMinimumTest",
       "host": true
     },
@@ -168,13 +172,19 @@
     {
       "name": "RavenwoodServicesTest",
       "host": true
+    },
+    {
+      "name": "UinputTestsRavenwood",
+      "host": true
     }
     // AUTO-GENERATED-END
   ],
   "ravenwood-postsubmit": [
-    {
-      "name": "SystemUiRavenTests",
-      "host": true
-    }
+    // We haven't maintained SystemUiRavenTests, and as a result, it's been demoted already.
+    // Disable it until we fix the issues: b/319647875
+    // {
+    //   "name": "SystemUiRavenTests",
+    //   "host": true
+    // }
   ]
 }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
index 3cb2c67..aa37d12 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
@@ -19,7 +19,6 @@
 import android.annotation.Nullable;
 import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.EnabledOnRavenwood;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
 
 import org.junit.runner.Description;
 
@@ -51,8 +50,6 @@
                 result = true;
             } else if (description.getAnnotation(DisabledOnRavenwood.class) != null) {
                 result = false;
-            } else if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
-                result = false;
             }
             if (result != null) {
                 if (takeIntoAccountRunDisabledTestsFlag
@@ -78,8 +75,6 @@
             result = true;
         } else if (testClass.getAnnotation(DisabledOnRavenwood.class) != null) {
             result = false;
-        } else if (testClass.getAnnotation(IgnoreUnderRavenwood.class) != null) {
-            result = false;
         }
         if (!result) {
             if (takeIntoAccountRunDisabledTestsFlag
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index d935626..d874197 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -23,7 +23,9 @@
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RUNTIME_PATH_JAVA_SYSPROP;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRuntimePath;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.parseNullableInt;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault;
 
@@ -271,6 +273,13 @@
         dumpJavaProperties();
         dumpOtherInfo();
 
+        System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
+        var runtimePath = getRavenwoodRuntimePath();
+        System.setProperty(RAVENWOOD_RUNTIME_PATH_JAVA_SYSPROP, runtimePath);
+
+        Log.i(TAG, "PWD=" + System.getProperty("user.dir"));
+        Log.i(TAG, "RuntimePath=" + runtimePath);
+
         // Make sure libravenwood_runtime is loaded.
         System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
 
@@ -314,7 +323,6 @@
         Typeface.loadPreinstalledSystemFontMap();
         Typeface.loadNativeSystemFonts();
 
-        System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
         // This will let AndroidJUnit4 use the original runner.
         System.setProperty("android.junit.runner",
                 "androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner");
diff --git a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
deleted file mode 100644
index 1c06829..0000000
--- a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
+++ /dev/null
@@ -1,59 +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 android.platform.test.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Test methods marked with this annotation are quietly ignored when running under a Ravenwood test
- * environment. The test continues to execute normally under all other non-Ravenwood test
- * environments.
- *
- * This annotation only takes effect when the containing class has a {@code
- * RavenwoodRule} configured. Ignoring is accomplished by throwing an {@code org.junit
- * .AssumptionViolatedException} which test infrastructure treats as being ignored.
- *
- * Developers are encouraged to use either the {@code blockedBy} and/or {@code reason} arguments
- * to document why a test is being ignored, to aid in future audits of tests that are candidates
- * to be enabled.
- *
- * @hide
- *
- * @deprecated Use {@link DisabledOnRavenwood} instead.
- */
-@Target({ElementType.METHOD, ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-@Deprecated
-public @interface IgnoreUnderRavenwood {
-    /**
-     * One or more classes that aren't yet supported by Ravenwood, which this test depends on.
-     */
-    Class<?>[] blockedBy() default {};
-
-    /**
-     * General free-form description of why this test is being ignored.
-     */
-    String reason() default "";
-
-    /**
-     * Tracking bug number, if any.
-     */
-    long bug() default 0;
-}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
deleted file mode 100644
index 85297fe..0000000
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.platform.test.ravenwood;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * No longer needed.
- *
- * @deprecated this class used to be used to handle the class level annotation, which
- * is now done by the test runner, so this class is not needed.
- */
-@Deprecated
-public class RavenwoodClassRule implements TestRule {
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return base;
-    }
-}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index ffe5f6c..b0b4c68 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -100,38 +100,6 @@
         }
 
         /**
-         * @deprecated no longer used. We always use an app UID.
-         */
-        @Deprecated
-        public Builder setProcessSystem() {
-            return this;
-        }
-
-        /**
-         * @deprecated no longer used. We always use an app UID.
-         */
-        @Deprecated
-        public Builder setProcessApp() {
-            return this;
-        }
-
-        /**
-         * @deprecated no longer used.
-         */
-        @Deprecated
-        public Builder setPackageName(@NonNull String packageName) {
-            return this;
-        }
-
-        /**
-         * @deprecated no longer used. Main thread is always available.
-         */
-        @Deprecated
-        public Builder setProvideMainThread(boolean provideMainThread) {
-            return this;
-        }
-
-        /**
          * Configure the given system property as immutable for the duration of the test.
          * Read access to the key is allowed, and write access will fail. When {@code value} is
          * {@code null}, the value is left as undefined.
@@ -163,28 +131,12 @@
             return this;
         }
 
-        /**
-         * @deprecated no longer used. All supported services are available.
-         */
-        @Deprecated
-        public Builder setServicesRequired(@NonNull Class<?>... services) {
-            return this;
-        }
-
         public RavenwoodRule build() {
             return mRule;
         }
     }
 
     /**
-     * @deprecated replaced by {@link #isOnRavenwood()}
-     */
-    @Deprecated
-    public static boolean isUnderRavenwood() {
-        return IS_ON_RAVENWOOD;
-    }
-
-    /**
      * Return if the current process is running on a Ravenwood test environment.
      */
     public static boolean isOnRavenwood() {
@@ -197,26 +149,6 @@
         }
     }
 
-    /**
-     * @deprecated Use
-     * {@code androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getContext()}
-     * instead.
-     */
-    @Deprecated
-    public Context getContext() {
-        return InstrumentationRegistry.getInstrumentation().getContext();
-    }
-
-    /**
-     * @deprecated Use
-     * {@code androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()}
-     * instead.
-     */
-    @Deprecated
-    public Instrumentation getInstrumentation() {
-        return InstrumentationRegistry.getInstrumentation();
-    }
-
     @Override
     public Statement apply(Statement base, Description description) {
         if (!IS_ON_RAVENWOOD) {
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
index 3e2c405..f25ae6a 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
@@ -128,28 +128,6 @@
         runOnHandlerSync(getMainHandler(), r);
     }
 
-    public static class MockitoHelper {
-        private MockitoHelper() {
-        }
-
-        /**
-         * Allow verifyZeroInteractions to work on ravenwood. It was replaced with a different
-         * method on. (Maybe we should do it in Ravenizer.)
-         */
-        public static void verifyZeroInteractions(Object... mocks) {
-            if (RavenwoodRule.isOnRavenwood()) {
-                // Mockito 4 or later
-                reflectMethod("org.mockito.Mockito", "verifyNoInteractions", Object[].class)
-                        .callStatic(new Object[]{mocks});
-            } else {
-                // Mockito 2
-                reflectMethod("org.mockito.Mockito", "verifyZeroInteractions", Object[].class)
-                        .callStatic(new Object[]{mocks});
-            }
-        }
-    }
-
-
     /**
      * Wrap the given {@link Supplier} to become memoized.
      *
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
index 893b354..e1b537e 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -68,6 +68,8 @@
             RAVENWOOD_RUNTIME_PATH + "ravenwood-data/ravenwood-empty-res.apk";
 
     public static final String RAVENWOOD_VERSION_JAVA_SYSPROP = "android.ravenwood.version";
+    public static final String RAVENWOOD_RUNTIME_PATH_JAVA_SYSPROP =
+            "android.ravenwood.runtime_path";
 
     /**
      * @return if we're running on Ravenwood.
diff --git a/ravenwood/scripts/extract-last-soong-commands.py b/ravenwood/scripts/extract-last-soong-commands.py
index 0629b77..b8d6f20 100755
--- a/ravenwood/scripts/extract-last-soong-commands.py
+++ b/ravenwood/scripts/extract-last-soong-commands.py
@@ -55,7 +55,7 @@
 
                 if s.startswith("verbose"):
                     continue
-                if re.match('^\[.*bootstrap blueprint', s):
+                if re.match('^\\[.*bootstrap blueprint', s):
                     continue
 
                 s = s.rstrip()
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java
index e8f59db..da27778 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java
@@ -16,13 +16,11 @@
 package com.android.ravenwoodtest.bivalenttest;
 
 import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodClassRule;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Assert;
-import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -33,9 +31,6 @@
 @RunWith(AndroidJUnit4.class)
 @DisabledOnRavenwood
 public class RavenwoodClassRuleDeviceOnlyTest {
-    @ClassRule
-    public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule();
-
     @Test
     public void testDeviceOnly() {
         Assert.assertFalse(RavenwoodRule.isOnRavenwood());
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt
index fd6d6fb..b3af875 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt
@@ -25,7 +25,6 @@
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -40,7 +39,6 @@
     }
 
     @Test
-    @Ignore // TODO: Enable this test after rolling out the "2" flags.
     fun testTrueFlags() {
         assertTrue(Flags.ravenwoodFlagRo2())
         assertTrue(Flags.ravenwoodFlagRw2())
@@ -67,14 +65,12 @@
 
     @Test
     @RequiresFlagsDisabled(Flags.FLAG_RAVENWOOD_FLAG_RO_2)
-    @Ignore // TODO: Enable this test after rolling out the "2" flags.
     fun testRequireFlagsDisabledRo() {
         fail("This test shouldn't be executed")
     }
 
     @Test
     @RequiresFlagsDisabled(Flags.FLAG_RAVENWOOD_FLAG_RW_2)
-    @Ignore // TODO: Enable this test after rolling out the "2" flags.
     fun testRequireFlagsDisabledRw() {
         fail("This test shouldn't be executed")
     }
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodJdkPatchTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodJdkPatchTest.java
new file mode 100644
index 0000000..cdfd4a8
--- /dev/null
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodJdkPatchTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.bivalenttest.ravenizer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import org.junit.Test;
+
+import java.io.FileDescriptor;
+import java.util.LinkedHashMap;
+import java.util.regex.Pattern;
+
+public class RavenwoodJdkPatchTest {
+
+    @Test
+    public void testUnicodeRegex() {
+        var pattern = Pattern.compile("\\w+");
+        assertTrue(pattern.matcher("über").matches());
+    }
+
+    @Test
+    public void testLinkedHashMapEldest() {
+        var map = new LinkedHashMap<String, String>();
+        map.put("a", "b");
+        map.put("x", "y");
+        assertEquals(map.entrySet().iterator().next(), map.eldest());
+    }
+
+    @Test
+    public void testFileDescriptorGetSetInt() throws ErrnoException {
+        FileDescriptor fd = Os.open("/dev/zero", OsConstants.O_RDONLY, 0);
+        try {
+            int fdRaw = fd.getInt$();
+            assertNotEquals(-1, fdRaw);
+            fd.setInt$(-1);
+            assertEquals(-1, fd.getInt$());
+            fd.setInt$(fdRaw);
+            Os.close(fd);
+            assertEquals(-1, fd.getInt$());
+        } finally {
+            Os.close(fd);
+        }
+    }
+}
diff --git a/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
index b1a40f0..0928625 100644
--- a/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
+++ b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
@@ -15,7 +15,7 @@
  */
 package com.android.ravenwoodtest;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
@@ -37,7 +37,7 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood
+    @DisabledOnRavenwood
     public void testIgnored() {
         throw new RuntimeException("Shouldn't be executed under ravenwood");
     }
diff --git a/ravenwood/tests/resapk_test/Android.bp b/ravenwood/tests/resapk_test/Android.bp
index c145765..960b3ed 100644
--- a/ravenwood/tests/resapk_test/Android.bp
+++ b/ravenwood/tests/resapk_test/Android.bp
@@ -10,7 +10,7 @@
 android_ravenwood_test {
     name: "RavenwoodResApkTest",
 
-    resource_apk: "RavenwoodResApkTest-apk",
+    resource_apk: "RavenwoodResApkTest-res",
 
     libs: [
         // Normally, tests shouldn't directly access it, but we need to access RavenwoodCommonUtils
@@ -24,6 +24,7 @@
     ],
     srcs: [
         "test/**/*.java",
+        ":RavenwoodResApkTest-res{.aapt.srcjar}",
     ],
     sdk_version: "test_current",
     auto_gen_config: true,
diff --git a/ravenwood/tests/resapk_test/apk/Android.bp b/ravenwood/tests/resapk_test/apk/Android.bp
index 10ed5e2..fd8976d 100644
--- a/ravenwood/tests/resapk_test/apk/Android.bp
+++ b/ravenwood/tests/resapk_test/apk/Android.bp
@@ -8,7 +8,13 @@
 }
 
 android_app {
-    name: "RavenwoodResApkTest-apk",
+    name: "RavenwoodResApkTest-res",
 
     sdk_version: "current",
+
+    use_resource_processor: false,
+
+    flags_packages: [
+        "com.android.internal.os.flags-aconfig",
+    ],
 }
diff --git a/ravenwood/tests/resapk_test/apk/res/layout/testlayout.xml b/ravenwood/tests/resapk_test/apk/res/layout/testlayout.xml
new file mode 100644
index 0000000..17cdb86
--- /dev/null
+++ b/ravenwood/tests/resapk_test/apk/res/layout/testlayout.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+    <View android:id="@+id/view1" text="no-flags" />
+    <View android:id="@+id/view2" text="ro-enabled" android:featureFlag="com.android.internal.os.ravenwood_flag_ro_2"/>
+    <View android:id="@+id/view3" text="ro-disabled" android:featureFlag="com.android.internal.os.ravenwood_flag_ro_1"/>
+    <View android:id="@+id/view2" text="rw-enabled" android:featureFlag="com.android.internal.os.ravenwood_flag_rw_2"/>
+    <View android:id="@+id/view3" text="rw-disabled" android:featureFlag="com.android.internal.os.ravenwood_flag_rw_1"/>
+</LinearLayout>
diff --git a/ravenwood/tests/resapk_test/apk/res/values/strings.xml b/ravenwood/tests/resapk_test/apk/res/values/strings.xml
index 23d4c0f..5abf747 100644
--- a/ravenwood/tests/resapk_test/apk/res/values/strings.xml
+++ b/ravenwood/tests/resapk_test/apk/res/values/strings.xml
@@ -13,7 +13,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
     <!-- Test string 1 -->
     <string name="test_string_1" translatable="false" >Test String 1</string>
+    <!-- values can only use readonly flags -->
+    <string name="test_string_enabled" translatable="false" android:featureFlag="com.android.internal.os.ravenwood_flag_ro_2">Enabled</string>
+    <string name="test_string_disabled" translatable="false" android:featureFlag="com.android.internal.os.ravenwood_flag_ro_1">Disabled</string>
 </resources>
diff --git a/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
index e547114..89f8d40 100644
--- a/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
+++ b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
@@ -16,23 +16,41 @@
 package com.android.ravenwoodtest.resapk_test;
 
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.TestCase.assertTrue;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.util.Log;
+
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.ravenwood.common.RavenwoodCommonUtils;
+import com.android.ravenwood.restest_apk.R;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
 public class RavenwoodResApkTest {
+    private static final String TAG = "RavenwoodResApkTest";
+
+    private static final Context sContext =
+            InstrumentationRegistry.getInstrumentation().getContext();
+
     /**
      * Ensure the file "ravenwood-res.apk" exists.
-     * TODO Check the content of it, once Ravenwood supports resources. The file should
-     * be a copy of RavenwoodResApkTest-apk.apk
      */
     @Test
     public void testResApkExists() {
@@ -48,4 +66,73 @@
         assertTrue(new File(
                 RavenwoodCommonUtils.getRavenwoodRuntimePath() + "/" + file).exists());
     }
+
+    @Test
+    public void testReadStringNoFlag() {
+        assertThat(sContext.getString(R.string.test_string_1)).isEqualTo("Test String 1");
+    }
+
+    @Test
+    public void testReadStringRoFlagEnabled() {
+        assertThat(sContext.getString(R.string.test_string_enabled)).isEqualTo("Enabled");
+    }
+
+    @Test
+    public void testReadStringRoFlagDisabled() {
+        assertThrows(android.content.res.Resources.NotFoundException.class, () -> {
+            sContext.getString(R.string.test_string_disabled);
+        });
+    }
+
+    /**
+     * Look into the layout and collect the "text" attribute.
+     *
+     * It _should_ respect android:featureFlag, but until b/396458006 gets fixed, this returns
+     * even disabled elements.
+     */
+    private List<String> getTextsFromEnabledChildren() throws Exception {
+        try (XmlResourceParser parser = sContext.getResources().getLayout(R.layout.testlayout)) {
+            assertNotNull(parser);
+
+            var ret = new ArrayList<String>();
+
+            while (parser.next() != XmlPullParser.END_DOCUMENT) {
+                var text = parser.getAttributeValue(null, "text");
+                if (text == null) {
+                    continue;
+                }
+
+                Log.d(TAG, "Found tag: " + parser.getName() + " text='" + text + "'");
+                ret.add(text);
+            }
+            return ret;
+        }
+    }
+
+    @Test
+    public void testElementNoFlag() throws Exception {
+        assertThat(getTextsFromEnabledChildren()).contains("no-flags");
+    }
+
+    @Test
+    public void testElementWithRoFlagEnabled() throws Exception {
+        assertThat(getTextsFromEnabledChildren()).contains("ro-enabled");
+    }
+
+    @Test
+    public void testElementWithRoFlagDisabled() throws Exception {
+        assertThat(getTextsFromEnabledChildren()).doesNotContain("ro-disabled");
+    }
+
+    @Test
+    public void testElementWithRwFlagEnabled() throws Exception {
+        assertThat(getTextsFromEnabledChildren()).contains("rw-enabled");
+    }
+
+    @Test
+    @DisabledOnRavenwood(bug = 396458006,
+            reason = "RW flags in XML are all handled as enabled for now")
+    public void testElementWithRwFlagDisabled() throws Exception {
+        assertThat(getTextsFromEnabledChildren()).doesNotContain("rw-disabled");
+    }
 }
diff --git a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
index f833782..2ff5dab 100644
--- a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
+++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
@@ -18,37 +18,28 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.ravenwood.example.BlueManager;
 import android.ravenwood.example.RedManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 public class RavenwoodServicesDependenciesTest {
-    // NOTE: we carefully only ask for RedManager here, and rely on Ravenwood internals to spin
-    // up the implicit dependency on BlueManager
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProcessSystem()
-            .setServicesRequired(RedManager.class)
-            .build();
-
     @Test
     public void testDirect() {
-        final RedManager red = mRavenwood.getContext().getSystemService(
-                RedManager.class);
+        final RedManager red = InstrumentationRegistry.getInstrumentation().getContext()
+                .getSystemService(RedManager.class);
         assertEquals("blue+red", red.getInterfaceDescriptor());
     }
 
     @Test
     public void testIndirect() {
-        final BlueManager blue = mRavenwood.getContext().getSystemService(
-                BlueManager.class);
+        final BlueManager blue = InstrumentationRegistry.getInstrumentation().getContext()
+                .getSystemService(BlueManager.class);
         assertEquals("blue", blue.getInterfaceDescriptor());
     }
 }
diff --git a/ravenwood/texts/ravenizer-standard-options.txt b/ravenwood/texts/ravenizer-standard-options.txt
new file mode 100644
index 0000000..cef736f
--- /dev/null
+++ b/ravenwood/texts/ravenizer-standard-options.txt
@@ -0,0 +1,13 @@
+# File containing standard options to Ravenizer for Ravenwood
+
+# Keep all classes / methods / fields in tests and its target
+--default-keep
+
+--delete-finals
+
+# Include standard annotations
+@jar:texts/ravenwood-standard-annotations.txt
+
+# Apply common policies
+--policy-override-file
+    jar:texts/ravenwood-common-policies.txt
diff --git a/ravenwood/texts/ravenwood-standard-annotations.txt b/ravenwood/texts/ravenwood-standard-annotations.txt
new file mode 100644
index 0000000..75ec5ca
--- /dev/null
+++ b/ravenwood/texts/ravenwood-standard-annotations.txt
@@ -0,0 +1,37 @@
+# Standard annotations.
+# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
+--keep-annotation
+    android.ravenwood.annotation.RavenwoodKeep
+
+--keep-annotation
+    android.ravenwood.annotation.RavenwoodKeepPartialClass
+
+--keep-class-annotation
+    android.ravenwood.annotation.RavenwoodKeepWholeClass
+
+--throw-annotation
+    android.ravenwood.annotation.RavenwoodThrow
+
+--remove-annotation
+    android.ravenwood.annotation.RavenwoodRemove
+
+--ignore-annotation
+    android.ravenwood.annotation.RavenwoodIgnore
+
+--partially-allowed-annotation
+    android.ravenwood.annotation.RavenwoodPartiallyAllowlisted
+
+--substitute-annotation
+    android.ravenwood.annotation.RavenwoodReplace
+
+--redirect-annotation
+    android.ravenwood.annotation.RavenwoodRedirect
+
+--redirection-class-annotation
+    android.ravenwood.annotation.RavenwoodRedirectionClass
+
+--class-load-hook-annotation
+    android.ravenwood.annotation.RavenwoodClassLoadHook
+
+--keep-static-initializer-annotation
+    android.ravenwood.annotation.RavenwoodKeepStaticInitializer
diff --git a/ravenwood/texts/ravenwood-standard-options.txt b/ravenwood/texts/ravenwood-standard-options.txt
index 2336575..0a65025 100644
--- a/ravenwood/texts/ravenwood-standard-options.txt
+++ b/ravenwood/texts/ravenwood-standard-options.txt
@@ -15,41 +15,3 @@
 
 #--default-class-load-hook
 #    com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-
-# Standard annotations.
-# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
---keep-annotation
-    android.ravenwood.annotation.RavenwoodKeep
-
---keep-annotation
-    android.ravenwood.annotation.RavenwoodKeepPartialClass
-
---keep-class-annotation
-    android.ravenwood.annotation.RavenwoodKeepWholeClass
-
---throw-annotation
-    android.ravenwood.annotation.RavenwoodThrow
-
---remove-annotation
-    android.ravenwood.annotation.RavenwoodRemove
-
---ignore-annotation
-    android.ravenwood.annotation.RavenwoodIgnore
-
---partially-allowed-annotation
-    android.ravenwood.annotation.RavenwoodPartiallyAllowlisted
-
---substitute-annotation
-    android.ravenwood.annotation.RavenwoodReplace
-
---redirect-annotation
-    android.ravenwood.annotation.RavenwoodRedirect
-
---redirection-class-annotation
-    android.ravenwood.annotation.RavenwoodRedirectionClass
-
---class-load-hook-annotation
-    android.ravenwood.annotation.RavenwoodClassLoadHook
-
---keep-static-initializer-annotation
-    android.ravenwood.annotation.RavenwoodKeepStaticInitializer
diff --git a/ravenwood/tools/hoststubgen/Android.bp b/ravenwood/tools/hoststubgen/Android.bp
index a5ff496..004834e 100644
--- a/ravenwood/tools/hoststubgen/Android.bp
+++ b/ravenwood/tools/hoststubgen/Android.bp
@@ -90,11 +90,9 @@
 java_library_host {
     name: "hoststubgen-lib",
     defaults: ["ravenwood-internal-only-visibility-java"],
-    srcs: ["src/**/*.kt"],
+    srcs: ["lib/**/*.kt"],
     static_libs: [
         "hoststubgen-helper-runtime",
-    ],
-    libs: [
         "junit",
         "ow2-asm",
         "ow2-asm-analysis",
@@ -108,15 +106,8 @@
 java_binary_host {
     name: "hoststubgen",
     main_class: "com.android.hoststubgen.HostStubGenMain",
-    static_libs: [
-        "hoststubgen-lib",
-        "junit",
-        "ow2-asm",
-        "ow2-asm-analysis",
-        "ow2-asm-commons",
-        "ow2-asm-tree",
-        "ow2-asm-util",
-    ],
+    srcs: ["src/**/*.kt"],
+    static_libs: ["hoststubgen-lib"],
     visibility: ["//visibility:public"],
 }
 
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Exceptions.kt
similarity index 92%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Exceptions.kt
index f59e143..ae0a008 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Exceptions.kt
@@ -15,8 +15,6 @@
  */
 package com.android.hoststubgen
 
-import java.io.File
-
 /**
  * We will not print the stack trace for exceptions implementing it.
  */
@@ -64,9 +62,6 @@
 class InputFileNotFoundException(filename: String) :
     ArgumentsException("File '$filename' not found")
 
-fun String.ensureFileExists(): String {
-    if (!File(this).exists()) {
-        throw InputFileNotFoundException(this)
-    }
-    return this
-}
+/** Thrown when a JAR resource does not exist. */
+class JarResourceNotFoundException(path: String) :
+    ArgumentsException("JAR resource '$path' not found")
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenClassProcessor.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenClassProcessor.kt
new file mode 100644
index 0000000..98f96a8
--- /dev/null
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenClassProcessor.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.findAnyAnnotation
+import com.android.hoststubgen.filters.AnnotationBasedFilter
+import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter
+import com.android.hoststubgen.filters.ConstantFilter
+import com.android.hoststubgen.filters.DefaultHookInjectingFilter
+import com.android.hoststubgen.filters.FilterRemapper
+import com.android.hoststubgen.filters.ImplicitOutputFilter
+import com.android.hoststubgen.filters.KeepNativeFilter
+import com.android.hoststubgen.filters.OutputFilter
+import com.android.hoststubgen.filters.SanitizationFilter
+import com.android.hoststubgen.filters.TextFileFilterPolicyBuilder
+import com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+import com.android.hoststubgen.utils.ClassPredicate
+import com.android.hoststubgen.visitors.BaseAdapter
+import com.android.hoststubgen.visitors.ImplGeneratingAdapter
+import com.android.hoststubgen.visitors.PackageRedirectRemapper
+import java.io.PrintWriter
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.ClassWriter
+import org.objectweb.asm.commons.ClassRemapper
+import org.objectweb.asm.util.CheckClassAdapter
+import org.objectweb.asm.util.TraceClassVisitor
+
+/**
+ * This class implements bytecode transformation of HostStubGen.
+ */
+class HostStubGenClassProcessor(
+    private val options: HostStubGenClassProcessorOptions,
+    val allClasses: ClassNodes,
+    private val errors: HostStubGenErrors = HostStubGenErrors(),
+    private val stats: HostStubGenStats? = null,
+) {
+    val filter = buildFilter()
+    val remapper = FilterRemapper(filter)
+
+    private val packageRedirector = PackageRedirectRemapper(options.packageRedirects)
+    private val processedAnnotation = setOf(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR)
+
+    /**
+     * Build the filter, which decides what classes/methods/fields should be put in stub or impl
+     * jars, and "how". (e.g. with substitution?)
+     */
+    private fun buildFilter(): OutputFilter {
+        // We build a "chain" of multiple filters here.
+        //
+        // The filters are build in from "inside", meaning the first filter created here is
+        // the last filter used, so it has the least precedence.
+        //
+        // So, for example, the "remove" annotation, which is handled by AnnotationBasedFilter,
+        // can override a class-wide annotation, which is handled by
+        // ClassWidePolicyPropagatingFilter, and any annotations can be overridden by the
+        // text-file based filter, which is handled by parseTextFilterPolicyFile.
+
+        // The first filter is for the default policy from the command line options.
+        var filter: OutputFilter = ConstantFilter(options.defaultPolicy.get, "default-by-options")
+
+        // Next, we build a filter that preserves all native methods by default
+        filter = KeepNativeFilter(allClasses, filter)
+
+        // Next, we need a filter that resolves "class-wide" policies.
+        // This is used when a member (methods, fields, nested classes) don't get any policies
+        // from upper filters. e.g. when a method has no annotations, then this filter will apply
+        // the class-wide policy, if any. (if not, we'll fall back to the above filter.)
+        filter = ClassWidePolicyPropagatingFilter(allClasses, filter)
+
+        // Inject default hooks from options.
+        filter = DefaultHookInjectingFilter(
+            allClasses,
+            options.defaultClassLoadHook.get,
+            options.defaultMethodCallHook.get,
+            filter
+        )
+
+        val annotationAllowedPredicate = options.annotationAllowedClassesFile.get.let { file ->
+            if (file == null) {
+                ClassPredicate.newConstantPredicate(true) // Allow all classes
+            } else {
+                ClassPredicate.loadFromFile(file, false)
+            }
+        }
+
+        // Next, Java annotation based filter.
+        val annotFilter = AnnotationBasedFilter(
+            errors,
+            allClasses,
+            options.keepAnnotations,
+            options.keepClassAnnotations,
+            options.throwAnnotations,
+            options.removeAnnotations,
+            options.ignoreAnnotations,
+            options.substituteAnnotations,
+            options.redirectAnnotations,
+            options.redirectionClassAnnotations,
+            options.classLoadHookAnnotations,
+            options.partiallyAllowedAnnotations,
+            options.keepStaticInitializerAnnotations,
+            annotationAllowedPredicate,
+            filter
+        )
+        filter = annotFilter
+
+        // Next, "text based" filter, which allows to override policies without touching
+        // the target code.
+        if (options.policyOverrideFiles.isNotEmpty()) {
+            val builder = TextFileFilterPolicyBuilder(allClasses, filter)
+            options.policyOverrideFiles.forEach(builder::parse)
+            filter = builder.createOutputFilter()
+            annotFilter.annotationAllowedMembers = builder.annotationAllowedMembersFilter
+        }
+
+        // Apply the implicit filter.
+        filter = ImplicitOutputFilter(errors, allClasses, filter)
+
+        // Add a final sanitization step.
+        filter = SanitizationFilter(errors, allClasses, filter)
+
+        return filter
+    }
+
+    private fun buildVisitor(base: ClassVisitor, className: String): ClassVisitor {
+        // Connect to the base visitor
+        var outVisitor: ClassVisitor = base
+
+        if (options.enableClassChecker.get) {
+            outVisitor = CheckClassAdapter(outVisitor)
+        }
+
+        // Remapping should happen at the end.
+        outVisitor = ClassRemapper(outVisitor, remapper)
+
+        val visitorOptions = BaseAdapter.Options(
+            errors = errors,
+            stats = stats,
+            deleteClassFinals = options.deleteFinals.get,
+            deleteMethodFinals = options.deleteFinals.get,
+        )
+
+        val verbosePrinter = PrintWriter(log.getWriter(LogLevel.Verbose))
+
+        // Inject TraceClassVisitor for debugging.
+        if (options.enablePostTrace.get) {
+            outVisitor = TraceClassVisitor(outVisitor, verbosePrinter)
+        }
+
+        // Handle --package-redirect
+        if (!packageRedirector.isEmpty) {
+            // Don't apply the remapper on redirect-from classes.
+            // Otherwise, if the target jar actually contains the "from" classes (which
+            // may or may not be the case) they'd be renamed.
+            // But we update all references in other places, so, a method call to a "from" class
+            // would be replaced with the "to" class. All type references (e.g. variable types)
+            // will be updated too.
+            if (!packageRedirector.isTarget(className)) {
+                outVisitor = ClassRemapper(outVisitor, packageRedirector)
+            } else {
+                log.v(
+                    "Class $className is a redirect-from class, not applying" +
+                            " --package-redirect"
+                )
+            }
+        }
+
+        outVisitor = ImplGeneratingAdapter(allClasses, outVisitor, filter, visitorOptions)
+
+        // Inject TraceClassVisitor for debugging.
+        if (options.enablePreTrace.get) {
+            outVisitor = TraceClassVisitor(outVisitor, verbosePrinter)
+        }
+
+        return outVisitor
+    }
+
+    fun processClassBytecode(bytecode: ByteArray): ByteArray {
+        val cr = ClassReader(bytecode)
+
+        // If the class was already processed previously, skip
+        val clz = allClasses.getClass(cr.className)
+        if (clz.findAnyAnnotation(processedAnnotation) != null) {
+            return bytecode
+        }
+
+        // COMPUTE_FRAMES wouldn't be happy if code uses
+        val flags = ClassWriter.COMPUTE_MAXS // or ClassWriter.COMPUTE_FRAMES
+        val cw = ClassWriter(flags)
+
+        val outVisitor = buildVisitor(cw, cr.className)
+
+        cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
+        return cw.toByteArray()
+    }
+}
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenClassProcessorOptions.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenClassProcessorOptions.kt
new file mode 100644
index 0000000..e7166f1
--- /dev/null
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenClassProcessorOptions.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen
+
+import com.android.hoststubgen.filters.FilterPolicy
+import com.android.hoststubgen.utils.ArgIterator
+import com.android.hoststubgen.utils.BaseOptions
+import com.android.hoststubgen.utils.FileOrResource
+import com.android.hoststubgen.utils.SetOnce
+
+private fun parsePackageRedirect(fromColonTo: String): Pair<String, String> {
+    val colon = fromColonTo.indexOf(':')
+    if ((colon < 1) || (colon + 1 >= fromColonTo.length)) {
+        throw ArgumentsException("--package-redirect must be a colon-separated string")
+    }
+    // TODO check for duplicates
+    return Pair(fromColonTo.substring(0, colon), fromColonTo.substring(colon + 1))
+}
+
+/**
+ * Options to configure [HostStubGenClassProcessor].
+ */
+open class HostStubGenClassProcessorOptions(
+    var keepAnnotations: MutableSet<String> = mutableSetOf(),
+    var throwAnnotations: MutableSet<String> = mutableSetOf(),
+    var removeAnnotations: MutableSet<String> = mutableSetOf(),
+    var ignoreAnnotations: MutableSet<String> = mutableSetOf(),
+    var keepClassAnnotations: MutableSet<String> = mutableSetOf(),
+    var partiallyAllowedAnnotations: MutableSet<String> = mutableSetOf(),
+    var redirectAnnotations: MutableSet<String> = mutableSetOf(),
+
+    var substituteAnnotations: MutableSet<String> = mutableSetOf(),
+    var redirectionClassAnnotations: MutableSet<String> = mutableSetOf(),
+    var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),
+    var keepStaticInitializerAnnotations: MutableSet<String> = mutableSetOf(),
+
+    var packageRedirects: MutableList<Pair<String, String>> = mutableListOf(),
+
+    var annotationAllowedClassesFile: SetOnce<String?> = SetOnce(null),
+
+    var defaultClassLoadHook: SetOnce<String?> = SetOnce(null),
+    var defaultMethodCallHook: SetOnce<String?> = SetOnce(null),
+
+    var policyOverrideFiles: MutableList<FileOrResource> = mutableListOf(),
+
+    var defaultPolicy: SetOnce<FilterPolicy> = SetOnce(FilterPolicy.Remove),
+
+    var deleteFinals: SetOnce<Boolean> = SetOnce(false),
+
+    var enableClassChecker: SetOnce<Boolean> = SetOnce(false),
+    var enablePreTrace: SetOnce<Boolean> = SetOnce(false),
+    var enablePostTrace: SetOnce<Boolean> = SetOnce(false),
+) : BaseOptions() {
+
+    private val allAnnotations = mutableSetOf<String>()
+
+    private fun ensureUniqueAnnotation(name: String): String {
+        if (!allAnnotations.add(name)) {
+            throw DuplicateAnnotationException(name)
+        }
+        return name
+    }
+
+    override fun parseOption(option: String, args: ArgIterator): Boolean {
+        // Define some shorthands...
+        fun nextArg(): String = args.nextArgRequired(option)
+        fun MutableSet<String>.addUniqueAnnotationArg(): String =
+            nextArg().also { this += ensureUniqueAnnotation(it) }
+
+        when (option) {
+            "--policy-override-file" -> policyOverrideFiles.add(FileOrResource(nextArg()))
+
+            "--default-remove" -> defaultPolicy.set(FilterPolicy.Remove)
+            "--default-throw" -> defaultPolicy.set(FilterPolicy.Throw)
+            "--default-keep" -> defaultPolicy.set(FilterPolicy.Keep)
+
+            "--keep-annotation" ->
+                keepAnnotations.addUniqueAnnotationArg()
+
+            "--keep-class-annotation" ->
+                keepClassAnnotations.addUniqueAnnotationArg()
+
+            "--partially-allowed-annotation" ->
+                partiallyAllowedAnnotations.addUniqueAnnotationArg()
+
+            "--throw-annotation" ->
+                throwAnnotations.addUniqueAnnotationArg()
+
+            "--remove-annotation" ->
+                removeAnnotations.addUniqueAnnotationArg()
+
+            "--ignore-annotation" ->
+                ignoreAnnotations.addUniqueAnnotationArg()
+
+            "--substitute-annotation" ->
+                substituteAnnotations.addUniqueAnnotationArg()
+
+            "--redirect-annotation" ->
+                redirectAnnotations.addUniqueAnnotationArg()
+
+            "--redirection-class-annotation" ->
+                redirectionClassAnnotations.addUniqueAnnotationArg()
+
+            "--class-load-hook-annotation" ->
+                classLoadHookAnnotations.addUniqueAnnotationArg()
+
+            "--keep-static-initializer-annotation" ->
+                keepStaticInitializerAnnotations.addUniqueAnnotationArg()
+
+            "--package-redirect" ->
+                packageRedirects += parsePackageRedirect(nextArg())
+
+            "--annotation-allowed-classes-file" ->
+                annotationAllowedClassesFile.set(nextArg())
+
+            "--default-class-load-hook" ->
+                defaultClassLoadHook.set(nextArg())
+
+            "--default-method-call-hook" ->
+                defaultMethodCallHook.set(nextArg())
+
+            "--delete-finals" -> deleteFinals.set(true)
+
+            // Following options are for debugging.
+            "--enable-class-checker" -> enableClassChecker.set(true)
+            "--no-class-checker" -> enableClassChecker.set(false)
+
+            "--enable-pre-trace" -> enablePreTrace.set(true)
+            "--no-pre-trace" -> enablePreTrace.set(false)
+
+            "--enable-post-trace" -> enablePostTrace.set(true)
+            "--no-post-trace" -> enablePostTrace.set(false)
+
+            else -> return false
+        }
+
+        return true
+    }
+
+    override fun dumpFields(): String {
+        return """
+            keepAnnotations=$keepAnnotations,
+            throwAnnotations=$throwAnnotations,
+            removeAnnotations=$removeAnnotations,
+            ignoreAnnotations=$ignoreAnnotations,
+            keepClassAnnotations=$keepClassAnnotations,
+            partiallyAllowedAnnotations=$partiallyAllowedAnnotations,
+            substituteAnnotations=$substituteAnnotations,
+            nativeSubstituteAnnotations=$redirectionClassAnnotations,
+            classLoadHookAnnotations=$classLoadHookAnnotations,
+            keepStaticInitializerAnnotations=$keepStaticInitializerAnnotations,
+            packageRedirects=$packageRedirects,
+            annotationAllowedClassesFile=$annotationAllowedClassesFile,
+            defaultClassLoadHook=$defaultClassLoadHook,
+            defaultMethodCallHook=$defaultMethodCallHook,
+            policyOverrideFiles=${policyOverrideFiles.toTypedArray().contentToString()},
+            defaultPolicy=$defaultPolicy,
+            deleteFinals=$deleteFinals,
+            enableClassChecker=$enableClassChecker,
+            enablePreTrace=$enablePreTrace,
+            enablePostTrace=$enablePostTrace,
+        """.trimIndent()
+    }
+}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenErrors.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenErrors.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenLogger.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenLogger.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt
similarity index 84%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt
index 10179ee..b2af782 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt
@@ -15,6 +15,8 @@
  */
 package com.android.hoststubgen
 
+import java.io.PrintWriter
+
 /**
  * Name of this executable. Set it in the main method.
  */
@@ -96,3 +98,23 @@
 fun csvEscape(value: String): String {
     return "\"" + value.replace("\"", "\"\"") + "\""
 }
+
+inline fun runMainWithBoilerplate(realMain: () -> Unit) {
+    var success = false
+
+    try {
+        realMain()
+
+        success = true
+    } catch (e: Throwable) {
+        log.e("$executableName: Error: ${e.message}")
+        if (e !is UserErrorException) {
+            e.printStackTrace(PrintWriter(log.getWriter(LogLevel.Error)))
+        }
+    } finally {
+        log.i("$executableName finished")
+        log.flush()
+    }
+
+    System.exit(if (success) 0 else 1 )
+}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/AsmUtils.kt
similarity index 99%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/AsmUtils.kt
index b41ce0f..112ef01e 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/AsmUtils.kt
@@ -217,7 +217,7 @@
 
 fun isAnonymousInnerClass(cn: ClassNode): Boolean {
     // TODO: Is there a better way?
-    return cn.name.matches(numericalInnerClassName)
+    return cn.outerClass != null && cn.name.matches(numericalInnerClassName)
 }
 
 /**
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
similarity index 97%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 6b360b7..b225209 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
@@ -66,7 +66,7 @@
             || className.endsWith("/FeatureFlags")
             || className.endsWith("/FeatureFlagsImpl")
             || className.endsWith("/CustomFeatureFlags")
-            || className.endsWith("/FakeFeatureFlagsImpl");
+            || className.endsWith("/FakeFeatureFlagsImpl")
 }
 
 /**
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ConstantFilter.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ConstantFilter.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/DelegatingFilter.kt
similarity index 83%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/DelegatingFilter.kt
index b8b0d8a..7f36aca 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/DelegatingFilter.kt
@@ -19,9 +19,9 @@
  * Base class for an [OutputFilter] that uses another filter as a fallback.
  */
 abstract class DelegatingFilter(
-        // fallback shouldn't be used by subclasses directly, so make it private.
-        // They should instead be calling into `super` or `outermostFilter`.
-        private val fallback: OutputFilter
+    // fallback shouldn't be used by subclasses directly, so make it private.
+    // They should instead be calling into `super` or `outermostFilter`.
+    private val fallback: OutputFilter
 ) : OutputFilter() {
     init {
         fallback.outermostFilter = this
@@ -50,24 +50,24 @@
     }
 
     override fun getPolicyForField(
-            className: String,
-            fieldName: String
+        className: String,
+        fieldName: String
     ): FilterPolicyWithReason {
         return fallback.getPolicyForField(className, fieldName)
     }
 
     override fun getPolicyForMethod(
-            className: String,
-            methodName: String,
-            descriptor: String
+        className: String,
+        methodName: String,
+        descriptor: String
     ): FilterPolicyWithReason {
         return fallback.getPolicyForMethod(className, methodName, descriptor)
     }
 
     override fun getRenameTo(
-            className: String,
-            methodName: String,
-            descriptor: String
+        className: String,
+        methodName: String,
+        descriptor: String
     ): String? {
         return fallback.getRenameTo(className, methodName, descriptor)
     }
@@ -97,13 +97,12 @@
     }
 
     override fun getMethodCallReplaceTo(
-        callerClassName: String,
-        callerMethodName: String,
         className: String,
         methodName: String,
         descriptor: String,
     ): MethodReplaceTarget? {
         return fallback.getMethodCallReplaceTo(
-            callerClassName, callerMethodName, className, methodName, descriptor)
+            className, methodName, descriptor
+        )
     }
 }
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterRemapper.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterRemapper.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
similarity index 86%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 474da6d..d44d016 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -16,7 +16,6 @@
 package com.android.hoststubgen.filters
 
 import com.android.hoststubgen.HostStubGenErrors
-import com.android.hoststubgen.HostStubGenInternalException
 import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
 import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
 import com.android.hoststubgen.asm.ClassNodes
@@ -37,19 +36,15 @@
  * TODO: Do we need a way to make anonymous class methods and lambdas "throw"?
  */
 class ImplicitOutputFilter(
-        private val errors: HostStubGenErrors,
-        private val classes: ClassNodes,
-        fallback: OutputFilter
+    private val errors: HostStubGenErrors,
+    private val classes: ClassNodes,
+    fallback: OutputFilter
 ) : DelegatingFilter(fallback) {
-    private fun getClassImplicitPolicy(className: String, cn: ClassNode): FilterPolicyWithReason? {
+    private fun getClassImplicitPolicy(cn: ClassNode): FilterPolicyWithReason? {
         if (isAnonymousInnerClass(cn)) {
             log.forDebug {
 //                log.d("  anon-inner class: ${className} outer: ${cn.outerClass}  ")
             }
-            if (cn.outerClass == null) {
-                throw HostStubGenInternalException(
-                        "outerClass is null for anonymous inner class")
-            }
             // If the outer class needs to be in impl, it should be in impl too.
             val outerPolicy = outermostFilter.getPolicyForClass(cn.outerClass)
             if (outerPolicy.policy.needsInOutput) {
@@ -65,15 +60,15 @@
         val cn = classes.getClass(className)
 
         // Use the implicit policy, if any.
-        getClassImplicitPolicy(className, cn)?.let { return it }
+        getClassImplicitPolicy(cn)?.let { return it }
 
         return fallback
     }
 
     override fun getPolicyForMethod(
-            className: String,
-            methodName: String,
-            descriptor: String
+        className: String,
+        methodName: String,
+        descriptor: String
     ): FilterPolicyWithReason {
         val fallback = super.getPolicyForMethod(className, methodName, descriptor)
         val classPolicy = outermostFilter.getPolicyForClass(className)
@@ -84,12 +79,14 @@
         // "keep" instead.
         // Unless it's an enum -- in that case, the below code would handle it.
         if (!cn.isEnum() &&
-                fallback.policy == FilterPolicy.Throw &&
-                methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC) {
+            fallback.policy == FilterPolicy.Throw &&
+            methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC
+        ) {
             // TODO Maybe show a warning?? But that'd be too noisy with --default-throw.
             return FilterPolicy.Ignore.withReason(
                 "'throw' on static initializer is handled as 'ignore'" +
-                        " [original throw reason: ${fallback.reason}]")
+                        " [original throw reason: ${fallback.reason}]"
+            )
         }
 
         log.d("Class ${cn.name} Class policy: $classPolicy")
@@ -120,7 +117,8 @@
                     // For synthetic methods (such as lambdas), let's just inherit the class's
                     // policy.
                     return memberPolicy.withReason(classPolicy.reason).wrapReason(
-                            "is-synthetic-method")
+                        "is-synthetic-method"
+                    )
                 }
             }
         }
@@ -129,8 +127,8 @@
     }
 
     override fun getPolicyForField(
-            className: String,
-            fieldName: String
+        className: String,
+        fieldName: String
     ): FilterPolicyWithReason {
         val fallback = super.getPolicyForField(className, fieldName)
 
@@ -161,4 +159,4 @@
 
         return fallback
     }
-}
\ No newline at end of file
+}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
similarity index 68%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
index fc885d6..59da3da 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
@@ -28,10 +28,12 @@
     private val classes: ClassNodes,
     fallback: OutputFilter,
 ) : DelegatingFilter(fallback) {
-    private val mPolicies: MutableMap<String, FilterPolicyWithReason> = mutableMapOf()
-    private val mRenames: MutableMap<String, String> = mutableMapOf()
-    private val mRedirectionClasses: MutableMap<String, String> = mutableMapOf()
-    private val mClassLoadHooks: MutableMap<String, String> = mutableMapOf()
+    private val mPolicies = mutableMapOf<String, FilterPolicyWithReason>()
+    private val mRenames = mutableMapOf<String, String>()
+    private val mRedirectionClasses = mutableMapOf<String, String>()
+    private val mClassLoadHooks = mutableMapOf<String, String>()
+    private val mMethodCallReplaceSpecs = mutableListOf<MethodCallReplaceSpec>()
+    private val mTypeRenameSpecs = mutableListOf<TypeRenameSpec>()
 
     private fun getClassKey(className: String): String {
         return className.toHumanReadableClassName()
@@ -45,10 +47,6 @@
         return getClassKey(className) + "." + methodName + ";" + signature
     }
 
-    override fun getPolicyForClass(className: String): FilterPolicyWithReason {
-        return mPolicies[getClassKey(className)] ?: super.getPolicyForClass(className)
-    }
-
     private fun checkClass(className: String) {
         if (classes.findClass(className) == null) {
             log.w("Unknown class $className")
@@ -74,6 +72,10 @@
         }
     }
 
+    override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+        return mPolicies[getClassKey(className)] ?: super.getPolicyForClass(className)
+    }
+
     fun setPolicyForClass(className: String, policy: FilterPolicyWithReason) {
         checkClass(className)
         mPolicies[getClassKey(className)] = policy
@@ -81,7 +83,7 @@
 
     override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason {
         return mPolicies[getFieldKey(className, fieldName)]
-                ?: super.getPolicyForField(className, fieldName)
+            ?: super.getPolicyForField(className, fieldName)
     }
 
     fun setPolicyForField(className: String, fieldName: String, policy: FilterPolicyWithReason) {
@@ -90,21 +92,21 @@
     }
 
     override fun getPolicyForMethod(
-            className: String,
-            methodName: String,
-            descriptor: String,
-            ): FilterPolicyWithReason {
+        className: String,
+        methodName: String,
+        descriptor: String,
+    ): FilterPolicyWithReason {
         return mPolicies[getMethodKey(className, methodName, descriptor)]
             ?: mPolicies[getMethodKey(className, methodName, "*")]
             ?: super.getPolicyForMethod(className, methodName, descriptor)
     }
 
     fun setPolicyForMethod(
-            className: String,
-            methodName: String,
-            descriptor: String,
-            policy: FilterPolicyWithReason,
-            ) {
+        className: String,
+        methodName: String,
+        descriptor: String,
+        policy: FilterPolicyWithReason,
+    ) {
         checkMethod(className, methodName, descriptor)
         mPolicies[getMethodKey(className, methodName, descriptor)] = policy
     }
@@ -123,7 +125,7 @@
 
     override fun getRedirectionClass(className: String): String? {
         return mRedirectionClasses[getClassKey(className)]
-                ?: super.getRedirectionClass(className)
+            ?: super.getRedirectionClass(className)
     }
 
     fun setRedirectionClass(from: String, to: String) {
@@ -135,11 +137,52 @@
     }
 
     override fun getClassLoadHooks(className: String): List<String> {
-        return addNonNullElement(super.getClassLoadHooks(className),
-            mClassLoadHooks[getClassKey(className)])
+        return addNonNullElement(
+            super.getClassLoadHooks(className),
+            mClassLoadHooks[getClassKey(className)]
+        )
     }
 
     fun setClassLoadHook(className: String, methodName: String) {
         mClassLoadHooks[getClassKey(className)] = methodName.toHumanReadableMethodName()
     }
+
+    override fun hasAnyMethodCallReplace(): Boolean {
+        return mMethodCallReplaceSpecs.isNotEmpty() || super.hasAnyMethodCallReplace()
+    }
+
+    override fun getMethodCallReplaceTo(
+        className: String,
+        methodName: String,
+        descriptor: String,
+    ): MethodReplaceTarget? {
+        // Maybe use 'Tri' if we end up having too many replacements.
+        mMethodCallReplaceSpecs.forEach {
+            if (className == it.fromClass &&
+                methodName == it.fromMethod
+            ) {
+                if (it.fromDescriptor == "*" || descriptor == it.fromDescriptor) {
+                    return MethodReplaceTarget(it.toClass, it.toMethod)
+                }
+            }
+        }
+        return super.getMethodCallReplaceTo(className, methodName, descriptor)
+    }
+
+    fun setMethodCallReplaceSpec(spec: MethodCallReplaceSpec) {
+        mMethodCallReplaceSpecs.add(spec)
+    }
+
+    override fun remapType(className: String): String? {
+        mTypeRenameSpecs.forEach {
+            if (it.typeInternalNamePattern.matcher(className).matches()) {
+                return it.typeInternalNamePrefix + className
+            }
+        }
+        return super.remapType(className)
+    }
+
+    fun setRemapTypeSpec(spec: TypeRenameSpec) {
+        mTypeRenameSpecs.add(spec)
+    }
 }
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/OutputFilter.kt
similarity index 94%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/OutputFilter.kt
index f99ce90..c47bb30 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/OutputFilter.kt
@@ -41,10 +41,10 @@
     abstract fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason
 
     abstract fun getPolicyForMethod(
-            className: String,
-            methodName: String,
-            descriptor: String,
-            ): FilterPolicyWithReason
+        className: String,
+        methodName: String,
+        descriptor: String,
+    ): FilterPolicyWithReason
 
     /**
      * If a given method is a substitute-from method, return the substitute-to method name.
@@ -108,8 +108,6 @@
      * If a method call should be forwarded to another method, return the target's class / method.
      */
     open fun getMethodCallReplaceTo(
-        callerClassName: String,
-        callerMethodName: String,
         className: String,
         methodName: String,
         descriptor: String,
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/PackageFilter.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/PackageFilter.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/SanitizationFilter.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/SanitizationFilter.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/SubclassFilter.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/SubclassFilter.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
similarity index 93%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index dd353e9..97fc353 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -22,13 +22,13 @@
 import com.android.hoststubgen.asm.toJvmClassName
 import com.android.hoststubgen.log
 import com.android.hoststubgen.normalizeTextLine
+import com.android.hoststubgen.utils.FileOrResource
 import com.android.hoststubgen.whitespaceRegex
-import org.objectweb.asm.tree.ClassNode
 import java.io.BufferedReader
-import java.io.FileReader
 import java.io.PrintWriter
 import java.io.Reader
 import java.util.regex.Pattern
+import org.objectweb.asm.tree.ClassNode
 
 /**
  * Print a class node as a "keep" policy.
@@ -58,6 +58,23 @@
     RFile,
 }
 
+data class MethodCallReplaceSpec(
+    val fromClass: String,
+    val fromMethod: String,
+    val fromDescriptor: String,
+    val toClass: String,
+    val toMethod: String,
+)
+
+/**
+ * When a package name matches [typeInternalNamePattern], we prepend [typeInternalNamePrefix]
+ * to it.
+ */
+data class TypeRenameSpec(
+    val typeInternalNamePattern: Pattern,
+    val typeInternalNamePrefix: String,
+)
+
 /**
  * This receives [TextFileFilterPolicyBuilder] parsing result.
  */
@@ -99,7 +116,7 @@
         className: String,
         methodName: String,
         methodDesc: String,
-        replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
+        replaceSpec: MethodCallReplaceSpec,
     )
 }
 
@@ -116,9 +133,6 @@
     private var featureFlagsPolicy: FilterPolicyWithReason? = null
     private var syspropsPolicy: FilterPolicyWithReason? = null
     private var rFilePolicy: FilterPolicyWithReason? = null
-    private val typeRenameSpec = mutableListOf<TextFilePolicyRemapperFilter.TypeRenameSpec>()
-    private val methodReplaceSpec =
-        mutableListOf<TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec>()
 
     /**
      * Fields for a filter chain used for "partial allowlisting", which are used by
@@ -126,47 +140,34 @@
      */
     private val annotationAllowedInMemoryFilter: InMemoryOutputFilter
     val annotationAllowedMembersFilter: OutputFilter
+        get() = annotationAllowedInMemoryFilter
 
     private val annotationAllowedPolicy = FilterPolicy.AnnotationAllowed.withReason(FILTER_REASON)
 
     init {
         // Create a filter that checks "partial allowlisting".
-        var aaf: OutputFilter = ConstantFilter(FilterPolicy.Remove, "default disallowed")
-
-        aaf = InMemoryOutputFilter(classes, aaf)
-        annotationAllowedInMemoryFilter = aaf
-
-        annotationAllowedMembersFilter = annotationAllowedInMemoryFilter
+        val filter = ConstantFilter(FilterPolicy.Remove, "default disallowed")
+        annotationAllowedInMemoryFilter = InMemoryOutputFilter(classes, filter)
     }
 
     /**
      * Parse a given policy file. This method can be called multiple times to read from
      * multiple files. To get the resulting filter, use [createOutputFilter]
      */
-    fun parse(file: String) {
+    fun parse(file: FileOrResource) {
         // We may parse multiple files, but we reuse the same parser, because the parser
         // will make sure there'll be no dupplicating "special class" policies.
-        parser.parse(FileReader(file), file, Processor())
+        parser.parse(file.open(), file.path, Processor())
     }
 
     /**
      * Generate the resulting [OutputFilter].
      */
     fun createOutputFilter(): OutputFilter {
-        var ret: OutputFilter = imf
-        if (typeRenameSpec.isNotEmpty()) {
-            ret = TextFilePolicyRemapperFilter(typeRenameSpec, ret)
-        }
-        if (methodReplaceSpec.isNotEmpty()) {
-            ret = TextFilePolicyMethodReplaceFilter(methodReplaceSpec, classes, ret)
-        }
-
         // Wrap the in-memory-filter with AHF.
-        ret = AndroidHeuristicsFilter(
-            classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, ret
+        return AndroidHeuristicsFilter(
+            classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, imf
         )
-
-        return ret
     }
 
     private inner class Processor : PolicyFileProcessor {
@@ -180,9 +181,7 @@
         }
 
         override fun onRename(pattern: Pattern, prefix: String) {
-            typeRenameSpec += TextFilePolicyRemapperFilter.TypeRenameSpec(
-                pattern, prefix
-            )
+            imf.setRemapTypeSpec(TypeRenameSpec(pattern, prefix))
         }
 
         override fun onClassStart(className: String) {
@@ -284,12 +283,12 @@
             className: String,
             methodName: String,
             methodDesc: String,
-            replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
+            replaceSpec: MethodCallReplaceSpec,
         ) {
             // Keep the source method, because the target method may call it.
             imf.setPolicyForMethod(className, methodName, methodDesc,
                 FilterPolicy.Keep.withReason(FILTER_REASON))
-            methodReplaceSpec.add(replaceSpec)
+            imf.setMethodCallReplaceSpec(replaceSpec)
         }
     }
 }
@@ -630,13 +629,13 @@
             if (classAndMethod != null) {
                 // If the substitution target contains a ".", then
                 // it's a method call redirect.
-                val spec = TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec(
-                        currentClassName!!.toJvmClassName(),
-                        methodName,
-                        signature,
-                        classAndMethod.first.toJvmClassName(),
-                        classAndMethod.second,
-                    )
+                val spec = MethodCallReplaceSpec(
+                    className.toJvmClassName(),
+                    methodName,
+                    signature,
+                    classAndMethod.first.toJvmClassName(),
+                    classAndMethod.second,
+                )
                 processor.onMethodOutClassReplace(
                     className,
                     methodName,
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassPredicate.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/utils/ClassPredicate.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassPredicate.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/utils/ClassPredicate.kt
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/utils/OptionUtils.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/utils/OptionUtils.kt
new file mode 100644
index 0000000..d086992
--- /dev/null
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/utils/OptionUtils.kt
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen.utils
+
+import com.android.hoststubgen.ArgumentsException
+import com.android.hoststubgen.InputFileNotFoundException
+import com.android.hoststubgen.JarResourceNotFoundException
+import com.android.hoststubgen.log
+import com.android.hoststubgen.normalizeTextLine
+import java.io.File
+import java.io.FileReader
+import java.io.InputStreamReader
+import java.io.Reader
+
+const val JAR_RESOURCE_PREFIX = "jar:"
+
+/**
+ * Base class for parsing arguments from commandline.
+ */
+abstract class BaseOptions {
+    /**
+     * Parse all arguments.
+     *
+     * This method should remain final. For customization in subclasses, override [parseOption].
+     */
+    fun parseArgs(args: List<String>) {
+        val ai = ArgIterator.withAtFiles(args)
+        while (true) {
+            val arg = ai.nextArgOptional() ?: break
+
+            if (log.maybeHandleCommandLineArg(arg) { ai.nextArgRequired(arg) }) {
+                continue
+            }
+            try {
+                if (!parseOption(arg, ai)) {
+                    throw ArgumentsException("Unknown option: $arg")
+                }
+            } catch (e: SetOnce.SetMoreThanOnceException) {
+                throw ArgumentsException("Duplicate or conflicting argument found: $arg")
+            }
+        }
+
+        checkArgs()
+    }
+
+    /**
+     * Print out all fields in this class.
+     *
+     * This method should remain final. For customization in subclasses, override [dumpFields].
+     */
+    final override fun toString(): String {
+        val fields = dumpFields().prependIndent("  ")
+        return "${this::class.simpleName} {\n$fields\n}"
+    }
+
+    /**
+     * Check whether the parsed options are in a correct state.
+     *
+     * This method is called as the last step in [parseArgs].
+     */
+    open fun checkArgs() {}
+
+    /**
+     * Parse a single option. Return true if the option is accepted, otherwise return false.
+     *
+     * Subclasses override/extend this method to support more options.
+     */
+    abstract fun parseOption(option: String, args: ArgIterator): Boolean
+
+    abstract fun dumpFields(): String
+}
+
+class ArgIterator(
+    private val args: List<String>,
+    private var currentIndex: Int = -1
+) {
+    val current: String
+        get() = args[currentIndex]
+
+    /**
+     * Get the next argument, or [null] if there's no more arguments.
+     */
+    fun nextArgOptional(): String? {
+        if ((currentIndex + 1) >= args.size) {
+            return null
+        }
+        return args[++currentIndex]
+    }
+
+    /**
+     * Get the next argument, or throw if
+     */
+    fun nextArgRequired(argName: String): String {
+        nextArgOptional().let {
+            if (it == null) {
+                throw ArgumentsException("Missing parameter for option $argName")
+            }
+            if (it.isEmpty()) {
+                throw ArgumentsException("Parameter can't be empty for option $argName")
+            }
+            return it
+        }
+    }
+
+    companion object {
+        fun withAtFiles(args: List<String>): ArgIterator {
+            val expanded = mutableListOf<String>()
+            expandAtFiles(args.asSequence(), expanded)
+            return ArgIterator(expanded)
+        }
+
+        /**
+         * Scan the arguments, and if any of them starts with an `@`, then load from the file
+         * and use its content as arguments.
+         *
+         * In order to pass an argument that starts with an '@', use '@@' instead.
+         *
+         * In this file, each line is treated as a single argument.
+         *
+         * The file can contain '#' as comments.
+         */
+        private fun expandAtFiles(args: Sequence<String>, out: MutableList<String>) {
+            args.forEach { arg ->
+                if (arg.startsWith("@@")) {
+                    out.add(arg.substring(1))
+                    return@forEach
+                } else if (!arg.startsWith('@')) {
+                    out.add(arg)
+                    return@forEach
+                }
+
+                // Read from the file, and add each line to the result.
+                val file = FileOrResource(arg.substring(1))
+
+                log.v("Expanding options file ${file.path}")
+
+                val fileArgs = file
+                    .open()
+                    .buffered()
+                    .lineSequence()
+                    .map(::normalizeTextLine)
+                    .filter(CharSequence::isNotEmpty)
+
+                expandAtFiles(fileArgs, out)
+            }
+        }
+    }
+}
+
+/**
+ * A single value that can only set once.
+ */
+open class SetOnce<T>(private var value: T) {
+    class SetMoreThanOnceException : Exception()
+
+    private var set = false
+
+    fun set(v: T): T {
+        if (set) {
+            throw SetMoreThanOnceException()
+        }
+        if (v == null) {
+            throw NullPointerException("This shouldn't happen")
+        }
+        set = true
+        value = v
+        return v
+    }
+
+    val get: T
+        get() = this.value
+
+    val isSet: Boolean
+        get() = this.set
+
+    fun <R> ifSet(block: (T & Any) -> R): R? {
+        if (isSet) {
+            return block(value!!)
+        }
+        return null
+    }
+
+    override fun toString(): String {
+        return "$value"
+    }
+}
+
+class IntSetOnce(value: Int) : SetOnce<Int>(value) {
+    fun set(v: String): Int {
+        try {
+            return this.set(v.toInt())
+        } catch (e: NumberFormatException) {
+            throw ArgumentsException("Invalid integer $v")
+        }
+    }
+}
+
+/**
+ * A path either points to a file in filesystem, or an entry in the JAR.
+ */
+class FileOrResource(val path: String) {
+    init {
+        path.ensureFileExists()
+    }
+
+    /**
+     * Either read from filesystem, or read from JAR resources.
+     */
+    fun open(): Reader {
+        return if (path.startsWith(JAR_RESOURCE_PREFIX)) {
+            val path = path.removePrefix(JAR_RESOURCE_PREFIX)
+            InputStreamReader(this::class.java.classLoader.getResourceAsStream(path)!!)
+        } else {
+            FileReader(path)
+        }
+    }
+}
+
+fun String.ensureFileExists(): String {
+    if (this.startsWith(JAR_RESOURCE_PREFIX)) {
+        val cl = FileOrResource::class.java.classLoader
+        val path = this.removePrefix(JAR_RESOURCE_PREFIX)
+        if (cl.getResource(path) == null) {
+            throw JarResourceNotFoundException(path)
+        }
+    } else if (!File(this).exists()) {
+        throw InputFileNotFoundException(this)
+    }
+    return this
+}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/utils/Trie.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/utils/Trie.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/BaseAdapter.kt
similarity index 81%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/BaseAdapter.kt
index a08d1d6..769b769 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/BaseAdapter.kt
@@ -17,7 +17,6 @@
 
 import com.android.hoststubgen.HostStubGenErrors
 import com.android.hoststubgen.HostStubGenStats
-import com.android.hoststubgen.LogLevel
 import com.android.hoststubgen.asm.ClassNodes
 import com.android.hoststubgen.asm.UnifiedVisitor
 import com.android.hoststubgen.asm.getPackageNameFromFullClassName
@@ -26,13 +25,10 @@
 import com.android.hoststubgen.filters.OutputFilter
 import com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 import com.android.hoststubgen.log
-import java.io.PrintWriter
 import org.objectweb.asm.ClassVisitor
 import org.objectweb.asm.FieldVisitor
 import org.objectweb.asm.MethodVisitor
 import org.objectweb.asm.Opcodes
-import org.objectweb.asm.commons.ClassRemapper
-import org.objectweb.asm.util.TraceClassVisitor
 
 const val OPCODE_VERSION = Opcodes.ASM9
 
@@ -49,8 +45,6 @@
     data class Options(
         val errors: HostStubGenErrors,
         val stats: HostStubGenStats?,
-        val enablePreTrace: Boolean,
-        val enablePostTrace: Boolean,
         val deleteClassFinals: Boolean,
         val deleteMethodFinals: Boolean,
         // We don't remove finals from fields, because final fields have a stronger memory
@@ -253,50 +247,4 @@
         substituted: Boolean,
         superVisitor: MethodVisitor?,
     ): MethodVisitor?
-
-    companion object {
-        fun getVisitor(
-            classInternalName: String,
-            classes: ClassNodes,
-            nextVisitor: ClassVisitor,
-            filter: OutputFilter,
-            packageRedirector: PackageRedirectRemapper,
-            options: Options,
-        ): ClassVisitor {
-            var next = nextVisitor
-
-            val verbosePrinter = PrintWriter(log.getWriter(LogLevel.Verbose))
-
-            // Inject TraceClassVisitor for debugging.
-            if (options.enablePostTrace) {
-                next = TraceClassVisitor(next, verbosePrinter)
-            }
-
-            // Handle --package-redirect
-            if (!packageRedirector.isEmpty) {
-                // Don't apply the remapper on redirect-from classes.
-                // Otherwise, if the target jar actually contains the "from" classes (which
-                // may or may not be the case) they'd be renamed.
-                // But we update all references in other places, so, a method call to a "from" class
-                // would be replaced with the "to" class. All type references (e.g. variable types)
-                // will be updated too.
-                if (!packageRedirector.isTarget(classInternalName)) {
-                    next = ClassRemapper(next, packageRedirector)
-                } else {
-                    log.v(
-                        "Class $classInternalName is a redirect-from class, not applying" +
-                                " --package-redirect"
-                    )
-                }
-            }
-
-            next = ImplGeneratingAdapter(classes, next, filter, options)
-
-            // Inject TraceClassVisitor for debugging.
-            if (options.enablePreTrace) {
-                next = TraceClassVisitor(next, verbosePrinter)
-            }
-            return next
-        }
-    }
 }
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/Helper.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/Helper.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
similarity index 99%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index b8a3576..617385a 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -396,7 +396,7 @@
             }
 
             val to = filter.getMethodCallReplaceTo(
-                currentClassName, callerMethodName, owner, name, descriptor
+                owner, name, descriptor
             )
 
             if (to == null
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
similarity index 100%
rename from ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
rename to ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 7e294ed..3335405 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -17,36 +17,15 @@
 
 import com.android.hoststubgen.asm.ClassNodes
 import com.android.hoststubgen.dumper.ApiDumper
-import com.android.hoststubgen.filters.AnnotationBasedFilter
-import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter
-import com.android.hoststubgen.filters.ConstantFilter
-import com.android.hoststubgen.filters.DefaultHookInjectingFilter
 import com.android.hoststubgen.filters.FilterPolicy
-import com.android.hoststubgen.filters.FilterRemapper
-import com.android.hoststubgen.filters.ImplicitOutputFilter
-import com.android.hoststubgen.filters.KeepNativeFilter
-import com.android.hoststubgen.filters.OutputFilter
-import com.android.hoststubgen.filters.SanitizationFilter
-import com.android.hoststubgen.filters.TextFileFilterPolicyBuilder
 import com.android.hoststubgen.filters.printAsTextPolicy
-import com.android.hoststubgen.utils.ClassPredicate
-import com.android.hoststubgen.visitors.BaseAdapter
-import com.android.hoststubgen.visitors.PackageRedirectRemapper
 import java.io.BufferedInputStream
 import java.io.BufferedOutputStream
 import java.io.FileOutputStream
-import java.io.InputStream
-import java.io.OutputStream
 import java.io.PrintWriter
 import java.util.zip.ZipEntry
 import java.util.zip.ZipFile
 import java.util.zip.ZipOutputStream
-import org.objectweb.asm.ClassReader
-import org.objectweb.asm.ClassVisitor
-import org.objectweb.asm.ClassWriter
-import org.objectweb.asm.commons.ClassRemapper
-import org.objectweb.asm.commons.Remapper
-import org.objectweb.asm.util.CheckClassAdapter
 
 /**
  * Actual main class.
@@ -76,21 +55,15 @@
             }
         }
 
-        // Build the filters.
-        val filter = buildFilter(errors, allClasses, options)
-
-        val filterRemapper = FilterRemapper(filter)
+        // Build the class processor
+        val processor = HostStubGenClassProcessor(options, allClasses, errors, stats)
 
         // Transform the jar.
         convert(
             options.inJar.get,
             options.outJar.get,
-            filter,
+            processor,
             options.enableClassChecker.get,
-            allClasses,
-            errors,
-            stats,
-            filterRemapper,
             options.numShards.get,
             options.shard.get,
         )
@@ -106,109 +79,20 @@
                 PrintWriter(it).use { pw ->
                     // TODO, when dumping a jar that's not framework-minus-apex.jar, we need to feed
                     // framework-minus-apex.jar so that we can dump inherited methods from it.
-                    ApiDumper(pw, allClasses, null, filter).dump()
+                    ApiDumper(pw, allClasses, null, processor.filter).dump()
                 }
             }
         }
     }
 
     /**
-     * Build the filter, which decides what classes/methods/fields should be put in stub or impl
-     * jars, and "how". (e.g. with substitution?)
-     */
-    private fun buildFilter(
-        errors: HostStubGenErrors,
-        allClasses: ClassNodes,
-        options: HostStubGenOptions,
-    ): OutputFilter {
-        // We build a "chain" of multiple filters here.
-        //
-        // The filters are build in from "inside", meaning the first filter created here is
-        // the last filter used, so it has the least precedence.
-        //
-        // So, for example, the "remove" annotation, which is handled by AnnotationBasedFilter,
-        // can override a class-wide annotation, which is handled by
-        // ClassWidePolicyPropagatingFilter, and any annotations can be overridden by the
-        // text-file based filter, which is handled by parseTextFilterPolicyFile.
-
-        // The first filter is for the default policy from the command line options.
-        var filter: OutputFilter = ConstantFilter(options.defaultPolicy.get, "default-by-options")
-
-        // Next, we build a filter that preserves all native methods by default
-        filter = KeepNativeFilter(allClasses, filter)
-
-        // Next, we need a filter that resolves "class-wide" policies.
-        // This is used when a member (methods, fields, nested classes) don't get any polices
-        // from upper filters. e.g. when a method has no annotations, then this filter will apply
-        // the class-wide policy, if any. (if not, we'll fall back to the above filter.)
-        filter = ClassWidePolicyPropagatingFilter(allClasses, filter)
-
-        // Inject default hooks from options.
-        filter = DefaultHookInjectingFilter(
-            allClasses,
-            options.defaultClassLoadHook.get,
-            options.defaultMethodCallHook.get,
-            filter
-        )
-
-        val annotationAllowedPredicate = options.annotationAllowedClassesFile.get.let { file ->
-            if (file == null) {
-                ClassPredicate.newConstantPredicate(true) // Allow all classes
-            } else {
-                ClassPredicate.loadFromFile(file, false)
-            }
-        }
-
-        // Next, Java annotation based filter.
-        val annotFilter = AnnotationBasedFilter(
-            errors,
-            allClasses,
-            options.keepAnnotations,
-            options.keepClassAnnotations,
-            options.throwAnnotations,
-            options.removeAnnotations,
-            options.ignoreAnnotations,
-            options.substituteAnnotations,
-            options.redirectAnnotations,
-            options.redirectionClassAnnotations,
-            options.classLoadHookAnnotations,
-            options.partiallyAllowedAnnotations,
-            options.keepStaticInitializerAnnotations,
-            annotationAllowedPredicate,
-            filter
-        )
-        filter = annotFilter
-
-        // Next, "text based" filter, which allows to override polices without touching
-        // the target code.
-        if (options.policyOverrideFiles.isNotEmpty()) {
-            val builder = TextFileFilterPolicyBuilder(allClasses, filter)
-            options.policyOverrideFiles.forEach(builder::parse)
-            filter = builder.createOutputFilter()
-            annotFilter.annotationAllowedMembers = builder.annotationAllowedMembersFilter
-        }
-
-        // Apply the implicit filter.
-        filter = ImplicitOutputFilter(errors, allClasses, filter)
-
-        // Add a final sanitization step.
-        filter = SanitizationFilter(errors, allClasses, filter)
-
-        return filter
-    }
-
-    /**
      * Convert a JAR file into "stub" and "impl" JAR files.
      */
     private fun convert(
         inJar: String,
         outJar: String?,
-        filter: OutputFilter,
+        processor: HostStubGenClassProcessor,
         enableChecker: Boolean,
-        classes: ClassNodes,
-        errors: HostStubGenErrors,
-        stats: HostStubGenStats,
-        remapper: Remapper?,
         numShards: Int,
         shard: Int
     ) {
@@ -216,8 +100,6 @@
         log.i("ASM CheckClassAdapter is %s", if (enableChecker) "enabled" else "disabled")
 
         log.iTime("Transforming jar") {
-            val packageRedirector = PackageRedirectRemapper(options.packageRedirects)
-
             var itemIndex = 0
             var numItemsProcessed = 0
             var numItems = -1 // == Unknown
@@ -240,11 +122,7 @@
                             if (!inShard) {
                                 continue
                             }
-                            convertSingleEntry(
-                                inZip, entry, outStream, filter,
-                                packageRedirector, remapper, enableChecker,
-                                classes, errors, stats
-                            )
+                            convertSingleEntry(inZip, entry, outStream, processor)
                             numItemsProcessed++
                         }
                         log.i("Converted all entries.")
@@ -270,13 +148,7 @@
         inZip: ZipFile,
         entry: ZipEntry,
         outStream: ZipOutputStream?,
-        filter: OutputFilter,
-        packageRedirector: PackageRedirectRemapper,
-        remapper: Remapper?,
-        enableChecker: Boolean,
-        classes: ClassNodes,
-        errors: HostStubGenErrors,
-        stats: HostStubGenStats
+        processor: HostStubGenClassProcessor
     ) {
         log.d("Entry: %s", entry.name)
         log.withIndent {
@@ -289,10 +161,7 @@
 
             // If it's a class, convert it.
             if (name.endsWith(".class")) {
-                processSingleClass(
-                    inZip, entry, outStream, filter, packageRedirector,
-                    remapper, enableChecker, classes, errors, stats
-                )
+                processSingleClass(inZip, entry, outStream, processor)
                 return
             }
 
@@ -332,29 +201,23 @@
     }
 
     /**
-     * Convert a single class to "stub" and "impl".
+     * Convert a single class.
      */
     private fun processSingleClass(
         inZip: ZipFile,
         entry: ZipEntry,
         outStream: ZipOutputStream?,
-        filter: OutputFilter,
-        packageRedirector: PackageRedirectRemapper,
-        remapper: Remapper?,
-        enableChecker: Boolean,
-        classes: ClassNodes,
-        errors: HostStubGenErrors,
-        stats: HostStubGenStats
+        processor: HostStubGenClassProcessor
     ) {
         val classInternalName = entry.name.replaceFirst("\\.class$".toRegex(), "")
-        val classPolicy = filter.getPolicyForClass(classInternalName)
+        val classPolicy = processor.filter.getPolicyForClass(classInternalName)
         if (classPolicy.policy == FilterPolicy.Remove) {
             log.d("Removing class: %s %s", classInternalName, classPolicy)
             return
         }
         // If we're applying a remapper, we need to rename the file too.
         var newName = entry.name
-        remapper?.mapType(classInternalName)?.let { remappedName ->
+        processor.remapper.mapType(classInternalName)?.let { remappedName ->
             if (remappedName != classInternalName) {
                 log.d("Renaming class file: %s -> %s", classInternalName, remappedName)
                 newName = "$remappedName.class"
@@ -367,64 +230,11 @@
                 BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
                     val newEntry = ZipEntry(newName)
                     outStream.putNextEntry(newEntry)
-                    convertClass(
-                        classInternalName, bis,
-                        outStream, filter, packageRedirector, remapper,
-                        enableChecker, classes, errors, stats
-                    )
+                    val classBytecode = bis.readAllBytes()
+                    outStream.write(processor.processClassBytecode(classBytecode))
                     outStream.closeEntry()
                 }
             }
         }
     }
-
-    /**
-     * Convert a single class to either "stub" or "impl".
-     */
-    private fun convertClass(
-        classInternalName: String,
-        input: InputStream,
-        out: OutputStream,
-        filter: OutputFilter,
-        packageRedirector: PackageRedirectRemapper,
-        remapper: Remapper?,
-        enableChecker: Boolean,
-        classes: ClassNodes,
-        errors: HostStubGenErrors,
-        stats: HostStubGenStats?
-    ) {
-        val cr = ClassReader(input)
-
-        // COMPUTE_FRAMES wouldn't be happy if code uses
-        val flags = ClassWriter.COMPUTE_MAXS // or ClassWriter.COMPUTE_FRAMES
-        val cw = ClassWriter(flags)
-
-        // Connect to the class writer
-        var outVisitor: ClassVisitor = cw
-        if (enableChecker) {
-            outVisitor = CheckClassAdapter(outVisitor)
-        }
-
-        // Remapping should happen at the end.
-        remapper?.let {
-            outVisitor = ClassRemapper(outVisitor, remapper)
-        }
-
-        val visitorOptions = BaseAdapter.Options(
-            errors = errors,
-            stats = stats,
-            enablePreTrace = options.enablePreTrace.get,
-            enablePostTrace = options.enablePostTrace.get,
-            deleteClassFinals = options.deleteFinals.get,
-            deleteMethodFinals = options.deleteFinals.get,
-        )
-        outVisitor = BaseAdapter.getVisitor(
-            classInternalName, classes, outVisitor, filter,
-            packageRedirector, visitorOptions
-        )
-
-        cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
-        val data = cw.toByteArray()
-        out.write(data)
-    }
 }
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
index 8506466..4ba8c5c 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
@@ -17,8 +17,6 @@
 
 package com.android.hoststubgen
 
-import java.io.PrintWriter
-
 /**
  * Entry point.
  */
@@ -26,10 +24,10 @@
     executableName = "HostStubGen"
     runMainWithBoilerplate {
         // Parse the command line arguments.
-        var clanupOnError = false
+        var cleanupOnError = false
         try {
-            val options = HostStubGenOptions.parseArgs(args)
-            clanupOnError = options.cleanUpOnError.get
+            val options = HostStubGenOptions().apply { parseArgs(args.asList()) }
+            cleanupOnError = options.cleanUpOnError.get
 
             log.v("$executableName started")
             log.v("Options: $options")
@@ -37,30 +35,10 @@
             // Run.
             HostStubGen(options).run()
         } catch (e: Throwable) {
-            if (clanupOnError) {
+            if (cleanupOnError) {
                 TODO("Remove output jars here")
             }
             throw e
         }
     }
 }
-
-inline fun runMainWithBoilerplate(realMain: () -> Unit) {
-    var success = false
-
-    try {
-        realMain()
-
-        success = true
-    } catch (e: Throwable) {
-        log.e("$executableName: Error: ${e.message}")
-        if (e !is UserErrorException) {
-            e.printStackTrace(PrintWriter(log.getWriter(LogLevel.Error)))
-        }
-    } finally {
-        log.i("$executableName finished")
-        log.flush()
-    }
-
-    System.exit(if (success) 0 else 1 )
-}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 1ab88d2..d9cc54a 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -15,385 +15,103 @@
  */
 package com.android.hoststubgen
 
-import com.android.hoststubgen.filters.FilterPolicy
-import java.io.BufferedReader
-import java.io.FileReader
-
-/**
- * A single value that can only set once.
- */
-open class SetOnce<T>(private var value: T) {
-    class SetMoreThanOnceException : Exception()
-
-    private var set = false
-
-    fun set(v: T): T {
-        if (set) {
-            throw SetMoreThanOnceException()
-        }
-        if (v == null) {
-            throw NullPointerException("This shouldn't happen")
-        }
-        set = true
-        value = v
-        return v
-    }
-
-    val get: T
-        get() = this.value
-
-    val isSet: Boolean
-        get() = this.set
-
-    fun <R> ifSet(block: (T & Any) -> R): R? {
-        if (isSet) {
-            return block(value!!)
-        }
-        return null
-    }
-
-    override fun toString(): String {
-        return "$value"
-    }
-}
-
-class IntSetOnce(value: Int) : SetOnce<Int>(value) {
-    fun set(v: String): Int {
-        try {
-            return this.set(v.toInt())
-        } catch (e: NumberFormatException) {
-            throw ArgumentsException("Invalid integer $v")
-        }
-    }
-}
+import com.android.hoststubgen.utils.ArgIterator
+import com.android.hoststubgen.utils.IntSetOnce
+import com.android.hoststubgen.utils.SetOnce
+import com.android.hoststubgen.utils.ensureFileExists
 
 /**
  * Options that can be set from command line arguments.
  */
 class HostStubGenOptions(
-        /** Input jar file*/
-        var inJar: SetOnce<String> = SetOnce(""),
+    /** Input jar file*/
+    var inJar: SetOnce<String> = SetOnce(""),
 
-        /** Output jar file */
-        var outJar: SetOnce<String?> = SetOnce(null),
+    /** Output jar file */
+    var outJar: SetOnce<String?> = SetOnce(null),
 
-        var inputJarDumpFile: SetOnce<String?> = SetOnce(null),
+    var inputJarDumpFile: SetOnce<String?> = SetOnce(null),
 
-        var inputJarAsKeepAllFile: SetOnce<String?> = SetOnce(null),
+    var inputJarAsKeepAllFile: SetOnce<String?> = SetOnce(null),
 
-        var keepAnnotations: MutableSet<String> = mutableSetOf(),
-        var throwAnnotations: MutableSet<String> = mutableSetOf(),
-        var removeAnnotations: MutableSet<String> = mutableSetOf(),
-        var ignoreAnnotations: MutableSet<String> = mutableSetOf(),
-        var keepClassAnnotations: MutableSet<String> = mutableSetOf(),
-        var partiallyAllowedAnnotations: MutableSet<String> = mutableSetOf(),
-        var redirectAnnotations: MutableSet<String> = mutableSetOf(),
+    var cleanUpOnError: SetOnce<Boolean> = SetOnce(false),
 
-        var substituteAnnotations: MutableSet<String> = mutableSetOf(),
-        var redirectionClassAnnotations: MutableSet<String> = mutableSetOf(),
-        var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),
-        var keepStaticInitializerAnnotations: MutableSet<String> = mutableSetOf(),
+    var statsFile: SetOnce<String?> = SetOnce(null),
 
-        var packageRedirects: MutableList<Pair<String, String>> = mutableListOf(),
+    var apiListFile: SetOnce<String?> = SetOnce(null),
 
-        var annotationAllowedClassesFile: SetOnce<String?> = SetOnce(null),
+    var numShards: IntSetOnce = IntSetOnce(1),
+    var shard: IntSetOnce = IntSetOnce(0),
+) : HostStubGenClassProcessorOptions() {
 
-        var defaultClassLoadHook: SetOnce<String?> = SetOnce(null),
-        var defaultMethodCallHook: SetOnce<String?> = SetOnce(null),
-
-        var policyOverrideFiles: MutableList<String> = mutableListOf(),
-
-        var defaultPolicy: SetOnce<FilterPolicy> = SetOnce(FilterPolicy.Remove),
-
-        var cleanUpOnError: SetOnce<Boolean> = SetOnce(false),
-
-        var deleteFinals: SetOnce<Boolean> = SetOnce(false),
-
-        var enableClassChecker: SetOnce<Boolean> = SetOnce(false),
-        var enablePreTrace: SetOnce<Boolean> = SetOnce(false),
-        var enablePostTrace: SetOnce<Boolean> = SetOnce(false),
-
-        var statsFile: SetOnce<String?> = SetOnce(null),
-
-        var apiListFile: SetOnce<String?> = SetOnce(null),
-
-        var numShards: IntSetOnce = IntSetOnce(1),
-        var shard: IntSetOnce = IntSetOnce(0),
-) {
-    companion object {
-
-        private fun parsePackageRedirect(fromColonTo: String): Pair<String, String> {
-            val colon = fromColonTo.indexOf(':')
-            if ((colon < 1) || (colon + 1 >= fromColonTo.length)) {
-                throw ArgumentsException("--package-redirect must be a colon-separated string")
-            }
-            // TODO check for duplicates
-            return Pair(fromColonTo.substring(0, colon), fromColonTo.substring(colon + 1))
+    override fun checkArgs() {
+        if (!inJar.isSet) {
+            throw ArgumentsException("Required option missing: --in-jar")
+        }
+        if (!outJar.isSet) {
+            log.w("--out-jar is not set. $executableName will not generate jar files.")
+        }
+        if (numShards.isSet != shard.isSet) {
+            throw ArgumentsException("--num-shards and --shard-index must be used together")
         }
 
-        fun parseArgs(args: Array<String>): HostStubGenOptions {
-            val ret = HostStubGenOptions()
-
-            val ai = ArgIterator.withAtFiles(args)
-
-            var allAnnotations = mutableSetOf<String>()
-
-            fun ensureUniqueAnnotation(name: String): String {
-                if (!allAnnotations.add(name)) {
-                    throw DuplicateAnnotationException(ai.current)
-                }
-                return name
+        if (numShards.isSet) {
+            if (shard.get >= numShards.get) {
+                throw ArgumentsException("--shard-index must be smaller than --num-shards")
             }
-
-            while (true) {
-                val arg = ai.nextArgOptional() ?: break
-
-                // Define some shorthands...
-                fun nextArg(): String = ai.nextArgRequired(arg)
-                fun MutableSet<String>.addUniqueAnnotationArg(): String =
-                        nextArg().also { this += ensureUniqueAnnotation(it) }
-
-                if (log.maybeHandleCommandLineArg(arg) { nextArg() }) {
-                    continue
-                }
-                try {
-                    when (arg) {
-                        // TODO: Write help
-                        "-h", "--help" -> TODO("Help is not implemented yet")
-
-                        "--in-jar" -> ret.inJar.set(nextArg()).ensureFileExists()
-                        // We support both arguments because some AOSP dependencies
-                        // still use the old argument
-                        "--out-jar", "--out-impl-jar" -> ret.outJar.set(nextArg())
-
-                        "--policy-override-file" ->
-                            ret.policyOverrideFiles.add(nextArg().ensureFileExists())
-
-                        "--clean-up-on-error" -> ret.cleanUpOnError.set(true)
-                        "--no-clean-up-on-error" -> ret.cleanUpOnError.set(false)
-
-                        "--default-remove" -> ret.defaultPolicy.set(FilterPolicy.Remove)
-                        "--default-throw" -> ret.defaultPolicy.set(FilterPolicy.Throw)
-                        "--default-keep" -> ret.defaultPolicy.set(FilterPolicy.Keep)
-
-                        "--keep-annotation" ->
-                            ret.keepAnnotations.addUniqueAnnotationArg()
-
-                        "--keep-class-annotation" ->
-                            ret.keepClassAnnotations.addUniqueAnnotationArg()
-
-                        "--partially-allowed-annotation" ->
-                            ret.partiallyAllowedAnnotations.addUniqueAnnotationArg()
-
-                        "--throw-annotation" ->
-                            ret.throwAnnotations.addUniqueAnnotationArg()
-
-                        "--remove-annotation" ->
-                            ret.removeAnnotations.addUniqueAnnotationArg()
-
-                        "--ignore-annotation" ->
-                            ret.ignoreAnnotations.addUniqueAnnotationArg()
-
-                        "--substitute-annotation" ->
-                            ret.substituteAnnotations.addUniqueAnnotationArg()
-
-                        "--redirect-annotation" ->
-                            ret.redirectAnnotations.addUniqueAnnotationArg()
-
-                        "--redirection-class-annotation" ->
-                            ret.redirectionClassAnnotations.addUniqueAnnotationArg()
-
-                        "--class-load-hook-annotation" ->
-                            ret.classLoadHookAnnotations.addUniqueAnnotationArg()
-
-                        "--keep-static-initializer-annotation" ->
-                            ret.keepStaticInitializerAnnotations.addUniqueAnnotationArg()
-
-                        "--package-redirect" ->
-                            ret.packageRedirects += parsePackageRedirect(nextArg())
-
-                        "--annotation-allowed-classes-file" ->
-                            ret.annotationAllowedClassesFile.set(nextArg())
-
-                        "--default-class-load-hook" ->
-                            ret.defaultClassLoadHook.set(nextArg())
-
-                        "--default-method-call-hook" ->
-                            ret.defaultMethodCallHook.set(nextArg())
-
-                        "--gen-keep-all-file" ->
-                            ret.inputJarAsKeepAllFile.set(nextArg())
-
-                        "--delete-finals" -> ret.deleteFinals.set(true)
-
-                        // Following options are for debugging.
-                        "--enable-class-checker" -> ret.enableClassChecker.set(true)
-                        "--no-class-checker" -> ret.enableClassChecker.set(false)
-
-                        "--enable-pre-trace" -> ret.enablePreTrace.set(true)
-                        "--no-pre-trace" -> ret.enablePreTrace.set(false)
-
-                        "--enable-post-trace" -> ret.enablePostTrace.set(true)
-                        "--no-post-trace" -> ret.enablePostTrace.set(false)
-
-                        "--gen-input-dump-file" -> ret.inputJarDumpFile.set(nextArg())
-
-                        "--stats-file" -> ret.statsFile.set(nextArg())
-                        "--supported-api-list-file" -> ret.apiListFile.set(nextArg())
-
-                        "--num-shards" -> ret.numShards.set(nextArg()).also {
-                            if (it < 1) {
-                                throw ArgumentsException("$arg must be positive integer")
-                            }
-                        }
-                        "--shard-index" -> ret.shard.set(nextArg()).also {
-                            if (it < 0) {
-                                throw ArgumentsException("$arg must be positive integer or zero")
-                            }
-                        }
-
-                        else -> throw ArgumentsException("Unknown option: $arg")
-                    }
-                } catch (e: SetOnce.SetMoreThanOnceException) {
-                    throw ArgumentsException("Duplicate or conflicting argument found: $arg")
-                }
-            }
-
-            if (!ret.inJar.isSet) {
-                throw ArgumentsException("Required option missing: --in-jar")
-            }
-            if (!ret.outJar.isSet) {
-                log.w("--out-jar is not set. $executableName will not generate jar files.")
-            }
-            if (ret.numShards.isSet != ret.shard.isSet) {
-                throw ArgumentsException("--num-shards and --shard-index must be used together")
-            }
-
-            if (ret.numShards.isSet) {
-                if (ret.shard.get >= ret.numShards.get) {
-                    throw ArgumentsException("--shard-index must be smaller than --num-shards")
-                }
-            }
-
-            return ret
         }
     }
 
-    override fun toString(): String {
+    override fun parseOption(option: String, args: ArgIterator): Boolean {
+        // Define some shorthands...
+        fun nextArg(): String = args.nextArgRequired(option)
+
+        when (option) {
+            // TODO: Write help
+            "-h", "--help" -> TODO("Help is not implemented yet")
+
+            "--in-jar" -> inJar.set(nextArg()).ensureFileExists()
+            // We support both arguments because some AOSP dependencies
+            // still use the old argument
+            "--out-jar", "--out-impl-jar" -> outJar.set(nextArg())
+
+            "--clean-up-on-error" -> cleanUpOnError.set(true)
+            "--no-clean-up-on-error" -> cleanUpOnError.set(false)
+
+            "--gen-input-dump-file" -> inputJarDumpFile.set(nextArg())
+            "--gen-keep-all-file" -> inputJarAsKeepAllFile.set(nextArg())
+
+            "--stats-file" -> statsFile.set(nextArg())
+            "--supported-api-list-file" -> apiListFile.set(nextArg())
+
+            "--num-shards" -> numShards.set(nextArg()).also {
+                if (it < 1) {
+                    throw ArgumentsException("$option must be positive integer")
+                }
+            }
+            "--shard-index" -> shard.set(nextArg()).also {
+                if (it < 0) {
+                    throw ArgumentsException("$option must be positive integer or zero")
+                }
+            }
+
+            else -> return super.parseOption(option, args)
+        }
+
+        return true
+    }
+
+    override fun dumpFields(): String {
         return """
-            HostStubGenOptions{
-              inJar='$inJar',
-              outJar='$outJar',
-              inputJarDumpFile=$inputJarDumpFile,
-              inputJarAsKeepAllFile=$inputJarAsKeepAllFile,
-              keepAnnotations=$keepAnnotations,
-              throwAnnotations=$throwAnnotations,
-              removeAnnotations=$removeAnnotations,
-              ignoreAnnotations=$ignoreAnnotations,
-              keepClassAnnotations=$keepClassAnnotations,
-              partiallyAllowedAnnotations=$partiallyAllowedAnnotations,
-              substituteAnnotations=$substituteAnnotations,
-              nativeSubstituteAnnotations=$redirectionClassAnnotations,
-              classLoadHookAnnotations=$classLoadHookAnnotations,
-              keepStaticInitializerAnnotations=$keepStaticInitializerAnnotations,
-              packageRedirects=$packageRedirects,
-              annotationAllowedClassesFile=$annotationAllowedClassesFile,
-              defaultClassLoadHook=$defaultClassLoadHook,
-              defaultMethodCallHook=$defaultMethodCallHook,
-              policyOverrideFiles=${policyOverrideFiles.toTypedArray().contentToString()},
-              defaultPolicy=$defaultPolicy,
-              deleteFinals=$deleteFinals,
-              cleanUpOnError=$cleanUpOnError,
-              enableClassChecker=$enableClassChecker,
-              enablePreTrace=$enablePreTrace,
-              enablePostTrace=$enablePostTrace,
-              statsFile=$statsFile,
-              apiListFile=$apiListFile,
-              numShards=$numShards,
-              shard=$shard,
-            }
-            """.trimIndent()
+            inJar=$inJar,
+            outJar=$outJar,
+            inputJarDumpFile=$inputJarDumpFile,
+            inputJarAsKeepAllFile=$inputJarAsKeepAllFile,
+            cleanUpOnError=$cleanUpOnError,
+            statsFile=$statsFile,
+            apiListFile=$apiListFile,
+            numShards=$numShards,
+            shard=$shard,
+        """.trimIndent() + '\n' + super.dumpFields()
     }
 }
-
-class ArgIterator(
-    private val args: List<String>,
-    private var currentIndex: Int = -1
-) {
-    val current: String
-        get() = args.get(currentIndex)
-
-    /**
-     * Get the next argument, or [null] if there's no more arguments.
-     */
-    fun nextArgOptional(): String? {
-        if ((currentIndex + 1) >= args.size) {
-            return null
-        }
-        return args.get(++currentIndex)
-    }
-
-    /**
-     * Get the next argument, or throw if
-     */
-    fun nextArgRequired(argName: String): String {
-        nextArgOptional().let {
-            if (it == null) {
-                throw ArgumentsException("Missing parameter for option $argName")
-            }
-            if (it.isEmpty()) {
-                throw ArgumentsException("Parameter can't be empty for option $argName")
-            }
-            return it
-        }
-    }
-
-    companion object {
-        fun withAtFiles(args: Array<String>): ArgIterator {
-            return ArgIterator(expandAtFiles(args))
-        }
-    }
-}
-
-/**
- * Scan the arguments, and if any of them starts with an `@`, then load from the file
- * and use its content as arguments.
- *
- * In order to pass an argument that starts with an '@', use '@@' instead.
- *
- * In this file, each line is treated as a single argument.
- *
- * The file can contain '#' as comments.
- */
-private fun expandAtFiles(args: Array<String>): List<String> {
-    val ret = mutableListOf<String>()
-
-    args.forEach { arg ->
-        if (arg.startsWith("@@")) {
-            ret += arg.substring(1)
-            return@forEach
-        } else if (!arg.startsWith('@')) {
-            ret += arg
-            return@forEach
-        }
-        // Read from the file, and add each line to the result.
-        val filename = arg.substring(1).ensureFileExists()
-
-        log.v("Expanding options file $filename")
-
-        BufferedReader(FileReader(filename)).use { reader ->
-            while (true) {
-                var line = reader.readLine()
-                if (line == null) {
-                    break // EOF
-                }
-
-                line = normalizeTextLine(line)
-                if (line.isNotEmpty()) {
-                    ret += line
-                }
-            }
-        }
-    }
-    return ret
-}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
deleted file mode 100644
index a3f934c..0000000
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
+++ /dev/null
@@ -1,60 +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.hoststubgen.filters
-
-import com.android.hoststubgen.asm.ClassNodes
-
-/**
- * Filter used by TextFileFilterPolicyParser for "method call relacement".
- */
-class TextFilePolicyMethodReplaceFilter(
-    val spec: List<MethodCallReplaceSpec>,
-    val classes: ClassNodes,
-    val fallback: OutputFilter,
-) : DelegatingFilter(fallback) {
-
-    data class MethodCallReplaceSpec(
-        val fromClass: String,
-        val fromMethod: String,
-        val fromDescriptor: String,
-        val toClass: String,
-        val toMethod: String,
-    )
-
-    override fun hasAnyMethodCallReplace(): Boolean {
-        return true
-    }
-
-    override fun getMethodCallReplaceTo(
-        callerClassName: String,
-        callerMethodName: String,
-        className: String,
-        methodName: String,
-        descriptor: String,
-    ): MethodReplaceTarget? {
-        // Maybe use 'Tri' if we end up having too many replacements.
-        spec.forEach {
-            if (className == it.fromClass &&
-                methodName == it.fromMethod
-                ) {
-                if (it.fromDescriptor == "*" || descriptor == it.fromDescriptor) {
-                    return MethodReplaceTarget(it.toClass, it.toMethod)
-                }
-            }
-        }
-        return null
-    }
-}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
deleted file mode 100644
index bc90d12..0000000
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
+++ /dev/null
@@ -1,44 +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.hoststubgen.filters
-
-import java.util.regex.Pattern
-
-/**
- * A filter that provides a simple "jarjar" functionality via [mapType]
- */
-class TextFilePolicyRemapperFilter(
-    val typeRenameSpecs: List<TypeRenameSpec>,
-    fallback: OutputFilter,
-) : DelegatingFilter(fallback) {
-    /**
-     * When a package name matches [typeInternalNamePattern], we prepend [typeInternalNamePrefix]
-     * to it.
-     */
-    data class TypeRenameSpec(
-        val typeInternalNamePattern: Pattern,
-        val typeInternalNamePrefix: String,
-    )
-
-    override fun remapType(className: String): String? {
-        typeRenameSpecs.forEach {
-            if (it.typeInternalNamePattern.matcher(className).matches()) {
-                return it.typeInternalNamePrefix + className
-            }
-        }
-        return null
-    }
-}
diff --git a/ravenwood/tools/ravenhelper/Android.bp b/ravenwood/tools/ravenhelper/Android.bp
index 3da6dd8..b279147 100644
--- a/ravenwood/tools/ravenhelper/Android.bp
+++ b/ravenwood/tools/ravenhelper/Android.bp
@@ -14,13 +14,7 @@
     static_libs: [
         "guava",
         "hoststubgen-lib",
-        "junit",
         "metalava-gradle-plugin-deps", // Get lint/PSI related classes from here.
-        "ow2-asm",
-        "ow2-asm-analysis",
-        "ow2-asm-commons",
-        "ow2-asm-tree",
-        "ow2-asm-util",
     ],
     visibility: ["//visibility:public"],
 }
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaOptions.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaOptions.kt
index 08bd95f..58bd9e9 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaOptions.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaOptions.kt
@@ -15,11 +15,11 @@
  */
 package com.android.platform.test.ravenwood.ravenhelper.policytoannot
 
-import com.android.hoststubgen.ArgIterator
 import com.android.hoststubgen.ArgumentsException
-import com.android.hoststubgen.SetOnce
-import com.android.hoststubgen.ensureFileExists
-import com.android.hoststubgen.log
+import com.android.hoststubgen.utils.ArgIterator
+import com.android.hoststubgen.utils.BaseOptions
+import com.android.hoststubgen.utils.SetOnce
+import com.android.hoststubgen.utils.ensureFileExists
 
 /**
  * Options for the "ravenhelper pta" subcommand.
@@ -39,68 +39,48 @@
 
     /** Dump the operations (for debugging) */
     var dumpOperations: SetOnce<Boolean> = SetOnce(false),
-) {
-    companion object {
-        fun parseArgs(args: List<String>): PtaOptions {
-            val ret = PtaOptions()
-            val ai = ArgIterator.withAtFiles(args.toTypedArray())
+) : BaseOptions() {
 
-            while (true) {
-                val arg = ai.nextArgOptional() ?: break
+    override fun parseOption(option: String, args: ArgIterator): Boolean {
+        fun nextArg(): String = args.nextArgRequired(option)
 
-                fun nextArg(): String = ai.nextArgRequired(arg)
+        when (option) {
+            // TODO: Write help
+            "-h", "--help" -> TODO("Help is not implemented yet")
 
-                if (log.maybeHandleCommandLineArg(arg) { nextArg() }) {
-                    continue
-                }
-                try {
-                    when (arg) {
-                        // TODO: Write help
-                        "-h", "--help" -> TODO("Help is not implemented yet")
+            "-p", "--policy-override-file" ->
+                policyOverrideFiles.add(nextArg().ensureFileExists())
 
-                        "-p", "--policy-override-file" ->
-                            ret.policyOverrideFiles.add(nextArg().ensureFileExists())
+            "-a", "--annotation-allowed-classes-file" ->
+                annotationAllowedClassesFile.set(nextArg().ensureFileExists())
 
-                        "-a", "--annotation-allowed-classes-file" ->
-                            ret.annotationAllowedClassesFile.set(nextArg().ensureFileExists())
+            "-s", "--src" -> sourceFilesOrDirectories.add(nextArg().ensureFileExists())
+            "--dump" -> dumpOperations.set(true)
+            "-o", "--output-script" -> outputScriptFile.set(nextArg())
 
-                        "-s", "--src" ->
-                            ret.sourceFilesOrDirectories.add(nextArg().ensureFileExists())
+            else -> return false
+        }
 
-                        "--dump" ->
-                            ret.dumpOperations.set(true)
+        return true
+    }
 
-                        "-o", "--output-script" ->
-                            ret.outputScriptFile.set(nextArg())
+    override fun checkArgs() {
+        if (policyOverrideFiles.size == 0) {
+            throw ArgumentsException("Must specify at least one policy file")
+        }
 
-                        else -> throw ArgumentsException("Unknown option: $arg")
-                    }
-                } catch (e: SetOnce.SetMoreThanOnceException) {
-                    throw ArgumentsException("Duplicate or conflicting argument found: $arg")
-                }
-            }
-
-            if (ret.policyOverrideFiles.size == 0) {
-                throw ArgumentsException("Must specify at least one policy file")
-            }
-
-            if (ret.sourceFilesOrDirectories.size == 0) {
-                throw ArgumentsException("Must specify at least one source path")
-            }
-
-            return ret
+        if (sourceFilesOrDirectories.size == 0) {
+            throw ArgumentsException("Must specify at least one source path")
         }
     }
 
-    override fun toString(): String {
+    override fun dumpFields(): String {
         return """
-            PtaOptions{
-              policyOverrideFiles=$policyOverrideFiles
-              annotationAllowedClassesFile=$annotationAllowedClassesFile
-              sourceFilesOrDirectories=$sourceFilesOrDirectories
-              outputScriptFile=$outputScriptFile
-              dumpOperations=$dumpOperations
-            }
-            """.trimIndent()
+            policyOverrideFiles=$policyOverrideFiles
+            annotationAllowedClassesFile=$annotationAllowedClassesFile
+            sourceFilesOrDirectories=$sourceFilesOrDirectories
+            outputScriptFile=$outputScriptFile
+            dumpOperations=$dumpOperations
+        """.trimIndent()
     }
-}
\ No newline at end of file
+}
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
index a7f481a..5ce9a23 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
@@ -19,10 +19,10 @@
 import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
 import com.android.hoststubgen.asm.toJvmClassName
 import com.android.hoststubgen.filters.FilterPolicyWithReason
+import com.android.hoststubgen.filters.MethodCallReplaceSpec
 import com.android.hoststubgen.filters.PolicyFileProcessor
 import com.android.hoststubgen.filters.SpecialClass
 import com.android.hoststubgen.filters.TextFileFilterPolicyParser
-import com.android.hoststubgen.filters.TextFilePolicyMethodReplaceFilter
 import com.android.hoststubgen.log
 import com.android.hoststubgen.utils.ClassPredicate
 import com.android.platform.test.ravenwood.ravenhelper.SubcommandHandler
@@ -39,7 +39,7 @@
  */
 class PtaProcessor : SubcommandHandler {
     override fun handle(args: List<String>) {
-        val options = PtaOptions.parseArgs(args)
+        val options = PtaOptions().apply { parseArgs(args) }
 
         log.v("Options: $options")
 
@@ -448,7 +448,7 @@
             className: String,
             methodName: String,
             methodDesc: String,
-            replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
+            replaceSpec: MethodCallReplaceSpec,
         ) {
             // This can't be converted to an annotation.
             classHasMember = true
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MapOptions.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MapOptions.kt
index ee200bb..6e0b7b8 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MapOptions.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MapOptions.kt
@@ -15,11 +15,11 @@
  */
 package com.android.platform.test.ravenwood.ravenhelper.sourcemap
 
-import com.android.hoststubgen.ArgIterator
 import com.android.hoststubgen.ArgumentsException
-import com.android.hoststubgen.SetOnce
-import com.android.hoststubgen.ensureFileExists
-import com.android.hoststubgen.log
+import com.android.hoststubgen.utils.ArgIterator
+import com.android.hoststubgen.utils.BaseOptions
+import com.android.hoststubgen.utils.SetOnce
+import com.android.hoststubgen.utils.ensureFileExists
 
 /**
  * Options for the "ravenhelper map" subcommand.
@@ -36,60 +36,36 @@
 
     /** Text to insert. */
     var text: SetOnce<String?> = SetOnce(null),
-) {
-    companion object {
-        fun parseArgs(args: List<String>): MapOptions {
-            val ret = MapOptions()
-            val ai = ArgIterator.withAtFiles(args.toTypedArray())
+) : BaseOptions() {
 
-            while (true) {
-                val arg = ai.nextArgOptional() ?: break
+    override fun parseOption(option: String, args: ArgIterator): Boolean {
+        fun nextArg(): String = args.nextArgRequired(option)
 
-                fun nextArg(): String = ai.nextArgRequired(arg)
+        when (option) {
+            // TODO: Write help
+            "-h", "--help" -> TODO("Help is not implemented yet")
+            "-s", "--src" -> sourceFilesOrDirectories.add(nextArg().ensureFileExists())
+            "-i", "--input" -> targetMethodFiles.add(nextArg().ensureFileExists())
+            "-o", "--output-script" -> outputScriptFile.set(nextArg())
+            "-t", "--text" -> text.set(nextArg())
+            else -> return false
+        }
 
-                if (log.maybeHandleCommandLineArg(arg) { nextArg() }) {
-                    continue
-                }
-                try {
-                    when (arg) {
-                        // TODO: Write help
-                        "-h", "--help" -> TODO("Help is not implemented yet")
+        return true
+    }
 
-                        "-s", "--src" ->
-                            ret.sourceFilesOrDirectories.add(nextArg().ensureFileExists())
-
-                        "-i", "--input" ->
-                            ret.targetMethodFiles.add(nextArg().ensureFileExists())
-
-                        "-o", "--output-script" ->
-                            ret.outputScriptFile.set(nextArg())
-
-                        "-t", "--text" ->
-                            ret.text.set(nextArg())
-
-                        else -> throw ArgumentsException("Unknown option: $arg")
-                    }
-                } catch (e: SetOnce.SetMoreThanOnceException) {
-                    throw ArgumentsException("Duplicate or conflicting argument found: $arg")
-                }
-            }
-
-            if (ret.sourceFilesOrDirectories.size == 0) {
-                throw ArgumentsException("Must specify at least one source path")
-            }
-
-            return ret
+    override fun checkArgs() {
+        if (sourceFilesOrDirectories.size == 0) {
+            throw ArgumentsException("Must specify at least one source path")
         }
     }
 
-    override fun toString(): String {
+    override fun dumpFields(): String {
         return """
-            PtaOptions{
-              sourceFilesOrDirectories=$sourceFilesOrDirectories
-              targetMethods=$targetMethodFiles
-              outputScriptFile=$outputScriptFile
-              text=$text
-            }
-            """.trimIndent()
+            sourceFilesOrDirectories=$sourceFilesOrDirectories
+            targetMethods=$targetMethodFiles
+            outputScriptFile=$outputScriptFile
+            text=$text
+        """.trimIndent()
     }
-}
\ No newline at end of file
+}
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MarkMethodHandler.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MarkMethodHandler.kt
index 8085253..f1c1398 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MarkMethodHandler.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/MarkMethodHandler.kt
@@ -35,7 +35,7 @@
  */
 class MarkMethodHandler : SubcommandHandler {
     override fun handle(args: List<String>) {
-        val options = MapOptions.parseArgs(args)
+        val options = MapOptions().apply { parseArgs(args) }
 
         log.i("Options: $options")
 
diff --git a/ravenwood/tools/ravenizer/Android.bp b/ravenwood/tools/ravenizer/Android.bp
index a52a04b..93cda4e 100644
--- a/ravenwood/tools/ravenizer/Android.bp
+++ b/ravenwood/tools/ravenizer/Android.bp
@@ -13,13 +13,12 @@
     srcs: ["src/**/*.kt"],
     static_libs: [
         "hoststubgen-lib",
-        "ow2-asm",
-        "ow2-asm-analysis",
-        "ow2-asm-commons",
-        "ow2-asm-tree",
-        "ow2-asm-util",
-        "junit",
         "ravenwood-junit-for-ravenizer",
     ],
+    java_resources: [
+        ":ravenizer-standard-options",
+        ":ravenwood-standard-annotations",
+        ":ravenwood-common-policies",
+    ],
     visibility: ["//visibility:public"],
 }
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
index e67c730..04e3bda2 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
@@ -16,23 +16,22 @@
 package com.android.platform.test.ravenwood.ravenizer
 
 import com.android.hoststubgen.GeneralUserErrorException
+import com.android.hoststubgen.HostStubGenClassProcessor
 import com.android.hoststubgen.asm.ClassNodes
 import com.android.hoststubgen.asm.zipEntryNameToClassName
 import com.android.hoststubgen.executableName
 import com.android.hoststubgen.log
 import com.android.platform.test.ravenwood.ravenizer.adapter.RunnerRewritingAdapter
+import java.io.BufferedInputStream
+import java.io.BufferedOutputStream
+import java.io.FileOutputStream
+import java.util.zip.ZipEntry
+import java.util.zip.ZipFile
+import java.util.zip.ZipOutputStream
 import org.objectweb.asm.ClassReader
 import org.objectweb.asm.ClassVisitor
 import org.objectweb.asm.ClassWriter
 import org.objectweb.asm.util.CheckClassAdapter
-import java.io.BufferedInputStream
-import java.io.BufferedOutputStream
-import java.io.FileOutputStream
-import java.io.InputStream
-import java.io.OutputStream
-import java.util.zip.ZipEntry
-import java.util.zip.ZipFile
-import java.util.zip.ZipOutputStream
 
 /**
  * Various stats on Ravenizer.
@@ -41,7 +40,7 @@
     /** Total end-to-end time. */
     var totalTime: Double = .0,
 
-    /** Time took to build [ClasNodes] */
+    /** Time took to build [ClassNodes] */
     var loadStructureTime: Double = .0,
 
     /** Time took to validate the classes */
@@ -50,14 +49,17 @@
     /** Total real time spent for converting the jar file */
     var totalProcessTime: Double = .0,
 
-    /** Total real time spent for converting class files (except for I/O time). */
-    var totalConversionTime: Double = .0,
+    /** Total real time spent for ravenizing class files (excluding I/O time). */
+    var totalRavenizeTime: Double = .0,
+
+    /** Total real time spent for processing class files HSG style (excluding I/O time). */
+    var totalHostStubGenTime: Double = .0,
 
     /** Total real time spent for copying class files without modification. */
     var totalCopyTime: Double = .0,
 
     /** # of entries in the input jar file */
-    var totalEntiries: Int = 0,
+    var totalEntries: Int = 0,
 
     /** # of *.class files in the input jar file */
     var totalClasses: Int = 0,
@@ -67,14 +69,15 @@
 ) {
     override fun toString(): String {
         return """
-            RavenizerStats{
+            RavenizerStats {
               totalTime=$totalTime,
               loadStructureTime=$loadStructureTime,
               validationTime=$validationTime,
               totalProcessTime=$totalProcessTime,
-              totalConversionTime=$totalConversionTime,
+              totalRavenizeTime=$totalRavenizeTime,
+              totalHostStubGenTime=$totalHostStubGenTime,
               totalCopyTime=$totalCopyTime,
-              totalEntiries=$totalEntiries,
+              totalEntries=$totalEntries,
               totalClasses=$totalClasses,
               processedClasses=$processedClasses,
             }
@@ -90,12 +93,18 @@
         val stats = RavenizerStats()
 
         stats.totalTime = log.nTime {
+            val allClasses = ClassNodes.loadClassStructures(options.inJar.get) {
+                stats.loadStructureTime = it
+            }
+            val processor = HostStubGenClassProcessor(options, allClasses)
+
             process(
                 options.inJar.get,
                 options.outJar.get,
                 options.enableValidation.get,
                 options.fatalValidation.get,
                 options.stripMockito.get,
+                processor,
                 stats,
             )
         }
@@ -108,15 +117,13 @@
         enableValidation: Boolean,
         fatalValidation: Boolean,
         stripMockito: Boolean,
+        processor: HostStubGenClassProcessor,
         stats: RavenizerStats,
     ) {
-        var allClasses = ClassNodes.loadClassStructures(inJar) {
-            time -> stats.loadStructureTime = time
-        }
         if (enableValidation) {
             stats.validationTime = log.iTime("Validating classes") {
-                if (!validateClasses(allClasses)) {
-                    var message = "Invalid test class(es) detected." +
+                if (!validateClasses(processor.allClasses)) {
+                    val message = "Invalid test class(es) detected." +
                             " See error log for details."
                     if (fatalValidation) {
                         throw RavenizerInvalidTestException(message)
@@ -126,7 +133,7 @@
                 }
             }
         }
-        if (includeUnsupportedMockito(allClasses)) {
+        if (includeUnsupportedMockito(processor.allClasses)) {
             log.w("Unsupported Mockito detected in $inJar!")
         }
 
@@ -134,7 +141,7 @@
             ZipFile(inJar).use { inZip ->
                 val inEntries = inZip.entries()
 
-                stats.totalEntiries = inZip.size()
+                stats.totalEntries = inZip.size()
 
                 ZipOutputStream(BufferedOutputStream(FileOutputStream(outJar))).use { outZip ->
                     while (inEntries.hasMoreElements()) {
@@ -159,9 +166,9 @@
                             stats.totalClasses += 1
                         }
 
-                        if (className != null && shouldProcessClass(allClasses, className)) {
-                            stats.processedClasses += 1
-                            processSingleClass(inZip, entry, outZip, allClasses, stats)
+                        if (className != null &&
+                            shouldProcessClass(processor.allClasses, className)) {
+                            processSingleClass(inZip, entry, outZip, processor, stats)
                         } else {
                             // Too slow, let's use merge_zips to bring back the original classes.
                             copyZipEntry(inZip, entry, outZip, stats)
@@ -201,14 +208,22 @@
         inZip: ZipFile,
         entry: ZipEntry,
         outZip: ZipOutputStream,
-        allClasses: ClassNodes,
+        processor: HostStubGenClassProcessor,
         stats: RavenizerStats,
     ) {
+        stats.processedClasses += 1
         val newEntry = ZipEntry(entry.name)
         outZip.putNextEntry(newEntry)
 
         BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
-            processSingleClass(entry, bis, outZip, allClasses, stats)
+            var classBytes = bis.readBytes()
+            stats.totalRavenizeTime += log.vTime("Ravenize ${entry.name}") {
+                classBytes = ravenizeSingleClass(entry, classBytes, processor.allClasses)
+            }
+            stats.totalHostStubGenTime += log.vTime("HostStubGen ${entry.name}") {
+                classBytes = processor.processClassBytecode(classBytes)
+            }
+            outZip.write(classBytes)
         }
         outZip.closeEntry()
     }
@@ -217,41 +232,34 @@
      * Whether a class needs to be processed. This must be kept in sync with [processSingleClass].
      */
     private fun shouldProcessClass(classes: ClassNodes, classInternalName: String): Boolean {
-        return !classInternalName.shouldByBypassed()
+        return !classInternalName.shouldBypass()
                 && RunnerRewritingAdapter.shouldProcess(classes, classInternalName)
     }
 
-    private fun processSingleClass(
+    private fun ravenizeSingleClass(
         entry: ZipEntry,
-        input: InputStream,
-        output: OutputStream,
+        input: ByteArray,
         allClasses: ClassNodes,
-        stats: RavenizerStats,
-    ) {
-        val cr = ClassReader(input)
+    ): ByteArray {
+        val classInternalName = zipEntryNameToClassName(entry.name)
+            ?: throw RavenizerInternalException("Unexpected zip entry name: ${entry.name}")
 
-        lateinit var data: ByteArray
-        stats.totalConversionTime += log.vTime("Modify ${entry.name}") {
+        val flags = ClassWriter.COMPUTE_MAXS
+        val cw = ClassWriter(flags)
+        var outVisitor: ClassVisitor = cw
 
-            val classInternalName = zipEntryNameToClassName(entry.name)
-                ?: throw RavenizerInternalException("Unexpected zip entry name: ${entry.name}")
-            val flags = ClassWriter.COMPUTE_MAXS
-            val cw = ClassWriter(flags)
-            var outVisitor: ClassVisitor = cw
-
-            val enableChecker = false
-            if (enableChecker) {
-                outVisitor = CheckClassAdapter(outVisitor)
-            }
-
-            // This must be kept in sync with shouldProcessClass.
-            outVisitor = RunnerRewritingAdapter.maybeApply(
-                classInternalName, allClasses, outVisitor)
-
-            cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
-
-            data = cw.toByteArray()
+        val enableChecker = false
+        if (enableChecker) {
+            outVisitor = CheckClassAdapter(outVisitor)
         }
-        output.write(data)
+
+        // This must be kept in sync with shouldProcessClass.
+        outVisitor = RunnerRewritingAdapter.maybeApply(
+            classInternalName, allClasses, outVisitor)
+
+        val cr = ClassReader(input)
+        cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
+
+        return cw.toByteArray()
     }
 }
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerMain.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerMain.kt
index aee4530..8a09e6d 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerMain.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerMain.kt
@@ -21,6 +21,26 @@
 import com.android.hoststubgen.executableName
 import com.android.hoststubgen.log
 import com.android.hoststubgen.runMainWithBoilerplate
+import com.android.hoststubgen.utils.JAR_RESOURCE_PREFIX
+import java.nio.file.Paths
+import kotlin.io.path.exists
+
+/**
+ * If this file exits, we also read options from it. This is "unsafe" because it could break
+ * incremental builds, if it sets any flag that affects the output file.
+ * (however, for now, there's no such options.)
+ *
+ * For example, to enable verbose logging, do `echo '-v' > ~/.raveniezr-unsafe`
+ *
+ * (but even the content of this file changes, soong won't rerun the command, so you need to
+ * remove the output first and then do a build again.)
+ */
+private val RAVENIZER_DOTFILE = System.getenv("HOME") + "/.ravenizer-unsafe"
+
+/**
+ * This is the name of the standard option text file embedded inside ravenizer.jar.
+ */
+private const val RAVENIZER_STANDARD_OPTIONS = "texts/ravenizer-standard-options.txt"
 
 /**
  * Entry point.
@@ -30,7 +50,15 @@
     log.setConsoleLogLevel(LogLevel.Info)
 
     runMainWithBoilerplate {
-        val options = RavenizerOptions.parseArgs(args)
+        val newArgs = args.toMutableList()
+        newArgs.add(0, "@$JAR_RESOURCE_PREFIX$RAVENIZER_STANDARD_OPTIONS")
+
+        if (Paths.get(RAVENIZER_DOTFILE).exists()) {
+            log.i("Reading options from $RAVENIZER_DOTFILE")
+            newArgs.add(0, "@$RAVENIZER_DOTFILE")
+        }
+
+        val options = RavenizerOptions().apply { parseArgs(newArgs) }
 
         log.i("$executableName started")
         log.v("Options: $options")
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
index a0e5599..5d278bb 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
@@ -15,25 +15,11 @@
  */
 package com.android.platform.test.ravenwood.ravenizer
 
-import com.android.hoststubgen.ArgIterator
 import com.android.hoststubgen.ArgumentsException
-import com.android.hoststubgen.SetOnce
-import com.android.hoststubgen.ensureFileExists
-import com.android.hoststubgen.log
-import java.nio.file.Paths
-import kotlin.io.path.exists
-
-/**
- * If this file exits, we also read options from it. This is "unsafe" because it could break
- * incremental builds, if it sets any flag that affects the output file.
- * (however, for now, there's no such options.)
- *
- * For example, to enable verbose logging, do `echo '-v' > ~/.raveniezr-unsafe`
- *
- * (but even the content of this file changes, soong won't rerun the command, so you need to
- * remove the output first and then do a build again.)
- */
-private val RAVENIZER_DOTFILE = System.getenv("HOME") + "/.raveniezr-unsafe"
+import com.android.hoststubgen.HostStubGenClassProcessorOptions
+import com.android.hoststubgen.utils.ArgIterator
+import com.android.hoststubgen.utils.SetOnce
+import com.android.hoststubgen.utils.ensureFileExists
 
 class RavenizerOptions(
     /** Input jar file*/
@@ -50,72 +36,49 @@
 
     /** Whether to remove mockito and dexmaker classes. */
     var stripMockito: SetOnce<Boolean> = SetOnce(false),
-) {
-    companion object {
+) : HostStubGenClassProcessorOptions() {
 
-        fun parseArgs(origArgs: Array<String>): RavenizerOptions {
-            val args = origArgs.toMutableList()
-            if (Paths.get(RAVENIZER_DOTFILE).exists()) {
-                log.i("Reading options from $RAVENIZER_DOTFILE")
-                args.add(0, "@$RAVENIZER_DOTFILE")
-            }
+    override fun parseOption(option: String, args: ArgIterator): Boolean {
+        fun nextArg(): String = args.nextArgRequired(option)
 
-            val ret = RavenizerOptions()
-            val ai = ArgIterator.withAtFiles(args.toTypedArray())
+        when (option) {
+            // TODO: Write help
+            "-h", "--help" -> TODO("Help is not implemented yet")
 
-            while (true) {
-                val arg = ai.nextArgOptional()
-                if (arg == null) {
-                    break
-                }
+            "--in-jar" -> inJar.set(nextArg()).ensureFileExists()
+            "--out-jar" -> outJar.set(nextArg())
 
-                fun nextArg(): String = ai.nextArgRequired(arg)
+            "--enable-validation" -> enableValidation.set(true)
+            "--disable-validation" -> enableValidation.set(false)
 
-                if (log.maybeHandleCommandLineArg(arg) { nextArg() }) {
-                    continue
-                }
-                try {
-                    when (arg) {
-                        // TODO: Write help
-                        "-h", "--help" -> TODO("Help is not implemented yet")
+            "--fatal-validation" -> fatalValidation.set(true)
+            "--no-fatal-validation" -> fatalValidation.set(false)
 
-                        "--in-jar" -> ret.inJar.set(nextArg()).ensureFileExists()
-                        "--out-jar" -> ret.outJar.set(nextArg())
+            "--strip-mockito" -> stripMockito.set(true)
+            "--no-strip-mockito" -> stripMockito.set(false)
 
-                        "--enable-validation" -> ret.enableValidation.set(true)
-                        "--disable-validation" -> ret.enableValidation.set(false)
+            else -> return super.parseOption(option, args)
+        }
 
-                        "--fatal-validation" -> ret.fatalValidation.set(true)
-                        "--no-fatal-validation" -> ret.fatalValidation.set(false)
+        return true
+    }
 
-                        "--strip-mockito" -> ret.stripMockito.set(true)
-                        "--no-strip-mockito" -> ret.stripMockito.set(false)
-
-                        else -> throw ArgumentsException("Unknown option: $arg")
-                    }
-                } catch (e: SetOnce.SetMoreThanOnceException) {
-                    throw ArgumentsException("Duplicate or conflicting argument found: $arg")
-                }
-            }
-
-            if (!ret.inJar.isSet) {
-                throw ArgumentsException("Required option missing: --in-jar")
-            }
-            if (!ret.outJar.isSet) {
-                throw ArgumentsException("Required option missing: --out-jar")
-            }
-           return ret
+    override fun checkArgs() {
+        if (!inJar.isSet) {
+            throw ArgumentsException("Required option missing: --in-jar")
+        }
+        if (!outJar.isSet) {
+            throw ArgumentsException("Required option missing: --out-jar")
         }
     }
 
-    override fun toString(): String {
+    override fun dumpFields(): String {
         return """
-            RavenizerOptions{
-              inJar=$inJar,
-              outJar=$outJar,
-              enableValidation=$enableValidation,
-              fatalValidation=$fatalValidation,
-            }
-            """.trimIndent()
+            inJar=$inJar,
+            outJar=$outJar,
+            enableValidation=$enableValidation,
+            fatalValidation=$fatalValidation,
+            stripMockito=$stripMockito,
+        """.trimIndent() + '\n' + super.dumpFields()
     }
 }
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
index 6092fcc..b394a76 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
@@ -15,8 +15,8 @@
  */
 package com.android.platform.test.ravenwood.ravenizer
 
-import android.platform.test.annotations.internal.InnerRunner
 import android.platform.test.annotations.NoRavenizer
+import android.platform.test.annotations.internal.InnerRunner
 import android.platform.test.ravenwood.RavenwoodAwareTestRunner
 import com.android.hoststubgen.asm.ClassNodes
 import com.android.hoststubgen.asm.findAnyAnnotation
@@ -85,7 +85,7 @@
 /**
  * Classes that should never be modified.
  */
-fun String.shouldByBypassed(): Boolean {
+fun String.shouldBypass(): Boolean {
     if (this.isRavenwoodClass()) {
         return true
     }
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
index 61e254b..d252b4d 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
@@ -20,8 +20,8 @@
 import com.android.hoststubgen.asm.startsWithAny
 import com.android.hoststubgen.asm.toHumanReadableClassName
 import com.android.hoststubgen.log
-import org.objectweb.asm.tree.ClassNode
 import java.util.regex.Pattern
+import org.objectweb.asm.tree.ClassNode
 
 fun validateClasses(classes: ClassNodes): Boolean {
     var allOk = true
@@ -37,7 +37,7 @@
  *
  */
 fun checkClass(cn: ClassNode, classes: ClassNodes): Boolean {
-    if (cn.name.shouldByBypassed()) {
+    if (cn.name.shouldBypass()) {
         // Class doesn't need to be checked.
         return true
     }
@@ -145,4 +145,4 @@
 
 private fun isAllowListedLegacyTest(targetClass: ClassNode): Boolean {
     return allowListedLegacyTests.contains(targetClass.name)
-}
\ No newline at end of file
+}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index b52b3dab..35db3c6 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -260,6 +260,16 @@
 }
 
 flag {
+    name: "pointer_up_motion_event_in_touch_exploration"
+    namespace: "accessibility"
+    description: "Allows POINTER_UP motionEvents to trigger during touch exploration."
+    bug: "374930391"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "proxy_use_apps_on_virtual_device_listener"
     namespace: "accessibility"
     description: "Fixes race condition described in b/286587811"
@@ -336,3 +346,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "hearing_input_change_when_comm_device"
+    namespace: "accessibility"
+    description: "Listen to the CommunicationDeviceChanged to show hearing device input notification."
+    bug: "394070235"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c49151d..703e37f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -521,15 +521,6 @@
                         @Nullable IBinder focusedToken) {
                     return AccessibilityManagerService.this.handleKeyGestureEvent(event);
                 }
-
-                @Override
-                public boolean isKeyGestureSupported(int gestureType) {
-                    return switch (gestureType) {
-                        case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
-                             KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK -> true;
-                        default -> false;
-                    };
-                }
             };
 
     @VisibleForTesting
@@ -540,7 +531,7 @@
             AccessibilitySecurityPolicy securityPolicy,
             SystemActionPerformer systemActionPerformer,
             AccessibilityWindowManager a11yWindowManager,
-            AccessibilityDisplayListener a11yDisplayListener,
+            AccessibilityDisplayListener.DisplayManagerWrapper displayManagerWrapper,
             MagnificationController magnificationController,
             @Nullable AccessibilityInputFilter inputFilter,
             ProxyManager proxyManager,
@@ -559,7 +550,8 @@
         mSecurityPolicy = securityPolicy;
         mSystemActionPerformer = systemActionPerformer;
         mA11yWindowManager = a11yWindowManager;
-        mA11yDisplayListener = a11yDisplayListener;
+        mA11yDisplayListener = new AccessibilityDisplayListener(displayManagerWrapper,
+                new MainHandler(Looper.getMainLooper()));
         mMagnificationController = magnificationController;
         mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
         mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
@@ -605,7 +597,8 @@
                 this, LocalServices.getService(PackageManagerInternal.class));
         mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
                 mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
-        mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
+        mA11yDisplayListener = new AccessibilityDisplayListener(
+                new AccessibilityDisplayListener.DisplayManagerWrapper(mContext), mMainHandler);
         mMagnificationController = new MagnificationController(
                 this,
                 mLock,
@@ -5466,11 +5459,11 @@
      * A Utility class to handle display state.
      */
     public class AccessibilityDisplayListener implements DisplayManager.DisplayListener {
-        private final DisplayManager mDisplayManager;
+        private final DisplayManagerWrapper mDisplayManager;
         private final ArrayList<Display> mDisplaysList = new ArrayList<>();
         private int mSystemUiUid = 0;
 
-        AccessibilityDisplayListener(Context context, Handler handler) {
+        AccessibilityDisplayListener(DisplayManagerWrapper displayManager, Handler handler) {
             // Avoid concerns about one thread adding displays while another thread removes
             // them by ensuring the looper is the main looper and the DisplayListener
             // callbacks are always executed on the one main thread.
@@ -5483,7 +5476,7 @@
                 Slog.e(LOG_TAG, errorMessage);
             }
 
-            mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+            mDisplayManager = displayManager;
             mDisplayManager.registerDisplayListener(this, handler);
             initializeDisplayList();
 
@@ -5619,7 +5612,7 @@
         }
 
         private boolean isValidDisplay(@Nullable Display display) {
-            if (display == null || display.getType() == Display.TYPE_OVERLAY) {
+            if (display == null) {
                 return false;
             }
             // Private virtual displays are created by the ap and is not allowed to access by other
@@ -5635,6 +5628,34 @@
             }
             return true;
         }
+
+        /** Wrapper of DisplayManager for testing. */
+        @VisibleForTesting
+        static class DisplayManagerWrapper {
+            private final DisplayManager mDm;
+
+            DisplayManagerWrapper(Context context) {
+                mDm = context.getSystemService(DisplayManager.class);
+            }
+
+            /**
+             * @see DisplayManager#registerDisplayListener(DisplayManager.DisplayListener, Handler)
+             */
+            public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+                    @Nullable Handler handler) {
+                mDm.registerDisplayListener(listener, handler);
+            }
+
+            /** @see DisplayManager#getDisplays() */
+            public Display[] getDisplays() {
+                return mDm.getDisplays();
+            }
+
+            /** @see DisplayManager#getDisplay(int) */
+            public Display getDisplay(int displayId) {
+                return mDm.getDisplay(displayId);
+            }
+        }
     }
 
     /** Represents an {@link AccessibilityManager} */
diff --git a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
index 10dffb5..94cef41 100644
--- a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
@@ -65,9 +65,9 @@
     private final Executor mCallbackExecutor;
 
     public HearingDevicePhoneCallNotificationController(@NonNull Context context) {
-        mTelephonyListener = new CallStateListener(context);
         mTelephonyManager = context.getSystemService(TelephonyManager.class);
         mCallbackExecutor = Executors.newSingleThreadExecutor();
+        mTelephonyListener = new CallStateListener(context, mCallbackExecutor);
     }
 
     @VisibleForTesting
@@ -109,14 +109,29 @@
                 AudioDeviceAttributes.ROLE_INPUT, AudioDeviceInfo.TYPE_BUILTIN_MIC, "");
 
         private final Context mContext;
+        private final Executor mCommDeviceChangedExecutor;
+        private final AudioManager.OnCommunicationDeviceChangedListener mCommDeviceChangedListener;
         private NotificationManager mNotificationManager;
         private AudioManager mAudioManager;
         private BroadcastReceiver mHearingDeviceActionReceiver;
         private BluetoothDevice mHearingDevice;
+        private boolean mIsCommDeviceChangedRegistered = false;
         private boolean mIsNotificationShown = false;
 
-        CallStateListener(@NonNull Context context) {
+        CallStateListener(@NonNull Context context, @NonNull Executor executor) {
             mContext = context;
+            mCommDeviceChangedExecutor = executor;
+            mCommDeviceChangedListener = device -> {
+                if (device == null) {
+                    return;
+                }
+                mHearingDevice = getSupportedInputHearingDeviceInfo(List.of(device));
+                if (mHearingDevice != null) {
+                    showNotificationIfNeeded();
+                } else {
+                    dismissNotificationIfNeeded();
+                }
+            };
         }
 
         @Override
@@ -134,6 +149,11 @@
             }
 
             if (state == TelephonyManager.CALL_STATE_IDLE) {
+                if (mIsCommDeviceChangedRegistered) {
+                    mIsCommDeviceChangedRegistered = false;
+                    mAudioManager.removeOnCommunicationDeviceChangedListener(
+                            mCommDeviceChangedListener);
+                }
                 dismissNotificationIfNeeded();
 
                 if (mHearingDevice != null) {
@@ -143,10 +163,26 @@
                 mHearingDevice = null;
             }
             if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
-                mHearingDevice = getSupportedInputHearingDeviceInfo(
-                        mAudioManager.getAvailableCommunicationDevices());
-                if (mHearingDevice != null) {
-                    showNotificationIfNeeded();
+                if (com.android.server.accessibility.Flags.hearingInputChangeWhenCommDevice()) {
+                    AudioDeviceInfo commDevice = mAudioManager.getCommunicationDevice();
+                    if (commDevice == null) {
+                        return;
+                    }
+                    mHearingDevice = getSupportedInputHearingDeviceInfo(List.of(commDevice));
+                    if (mHearingDevice != null) {
+                        showNotificationIfNeeded();
+                    } else {
+                        mAudioManager.addOnCommunicationDeviceChangedListener(
+                                mCommDeviceChangedExecutor,
+                                mCommDeviceChangedListener);
+                        mIsCommDeviceChangedRegistered = true;
+                    }
+                } else {
+                    mHearingDevice = getSupportedInputHearingDeviceInfo(
+                            mAudioManager.getAvailableCommunicationDevices());
+                    if (mHearingDevice != null) {
+                        showNotificationIfNeeded();
+                    }
                 }
             }
         }
@@ -264,6 +300,10 @@
                             PendingIntent.FLAG_IMMUTABLE);
                 }
                 case ACTION_BLUETOOTH_DEVICE_DETAILS -> {
+                    if (mHearingDevice == null) {
+                        return null;
+                    }
+
                     Bundle bundle = new Bundle();
                     bundle.putString(KEY_BLUETOOTH_ADDRESS, mHearingDevice.getAddress());
                     intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 0f6f86b..0b9c45d 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -21,10 +21,14 @@
 import static android.view.accessibility.AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
 import static android.view.accessibility.AccessibilityManager.AUTOCLICK_DELAY_DEFAULT;
 import static android.view.accessibility.AccessibilityManager.AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT;
+import static android.view.accessibility.AccessibilityManager.AUTOCLICK_REVERT_TO_LEFT_CLICK_DEFAULT;
 
 import static com.android.server.accessibility.autoclick.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_DOUBLE_CLICK;
+import static com.android.server.accessibility.autoclick.AutoclickScrollPanel.DIRECTION_NONE;
 import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK;
 import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL;
 import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AutoclickType;
 import static com.android.server.accessibility.autoclick.AutoclickTypePanel.ClickPanelControllerInterface;
 
@@ -43,6 +47,7 @@
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
+import android.view.ViewConfiguration;
 import android.view.WindowManager;
 
 import androidx.annotation.VisibleForTesting;
@@ -87,17 +92,26 @@
     @VisibleForTesting AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
     @VisibleForTesting AutoclickIndicatorView mAutoclickIndicatorView;
     @VisibleForTesting AutoclickTypePanel mAutoclickTypePanel;
+    @VisibleForTesting AutoclickScrollPanel mAutoclickScrollPanel;
     private WindowManager mWindowManager;
 
     // Default click type is left-click.
     private @AutoclickType int mActiveClickType = AUTOCLICK_TYPE_LEFT_CLICK;
 
+    // Default scroll direction is DIRECTION_NONE.
+    private @AutoclickScrollPanel.ScrollDirection int mHoveredDirection = DIRECTION_NONE;
+
     @VisibleForTesting
     final ClickPanelControllerInterface clickPanelController =
             new ClickPanelControllerInterface() {
                 @Override
                 public void handleAutoclickTypeChange(@AutoclickType int clickType) {
                     mActiveClickType = clickType;
+
+                    // Hide scroll panel when type is not scroll.
+                    if (clickType != AUTOCLICK_TYPE_SCROLL && mAutoclickScrollPanel != null) {
+                        mAutoclickScrollPanel.hide();
+                    }
                 }
 
                 @Override
@@ -117,6 +131,34 @@
                 }
             };
 
+    @VisibleForTesting
+    final AutoclickScrollPanel.ScrollPanelControllerInterface mScrollPanelController =
+            new AutoclickScrollPanel.ScrollPanelControllerInterface() {
+                @Override
+                public void onHoverButtonChange(
+                        @AutoclickScrollPanel.ScrollDirection int direction,
+                        boolean hovered) {
+                    // Update the hover direction.
+                    if (hovered) {
+                        mHoveredDirection = direction;
+                    } else if (mHoveredDirection == direction) {
+                        // Safety check: Only clear hover tracking if this is the same button
+                        // we're currently tracking.
+                        mHoveredDirection = AutoclickScrollPanel.DIRECTION_NONE;
+                    }
+
+                    // For exit button, we only trigger hover state changes, the autoclick system
+                    // will handle the countdown.
+                    if (direction == AutoclickScrollPanel.DIRECTION_EXIT) {
+                        return;
+                    }
+                    // For direction buttons, perform scroll action immediately.
+                    if (hovered && direction != AutoclickScrollPanel.DIRECTION_NONE) {
+                        handleScroll(direction);
+                    }
+                }
+            };
+
     public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
         mTrace = trace;
         mContext = context;
@@ -136,7 +178,8 @@
                     initiateAutoclickIndicator(handler);
                 }
 
-                mClickScheduler = new ClickScheduler(handler, AUTOCLICK_DELAY_DEFAULT);
+                mClickScheduler = new ClickScheduler(
+                            handler, AUTOCLICK_DELAY_DEFAULT);
                 mAutoclickSettingsObserver = new AutoclickSettingsObserver(mUserId, handler);
                 mAutoclickSettingsObserver.start(
                         mContext.getContentResolver(),
@@ -161,6 +204,8 @@
         mWindowManager = mContext.getSystemService(WindowManager.class);
         mAutoclickTypePanel =
                 new AutoclickTypePanel(mContext, mWindowManager, mUserId, clickPanelController);
+        mAutoclickScrollPanel = new AutoclickScrollPanel(mContext, mWindowManager,
+                mScrollPanelController);
 
         mAutoclickTypePanel.show();
         mWindowManager.addView(mAutoclickIndicatorView, mAutoclickIndicatorView.getLayoutParams());
@@ -189,6 +234,10 @@
             mClickScheduler.cancel();
         }
 
+        if (mAutoclickScrollPanel != null) {
+            mAutoclickScrollPanel.hide();
+        }
+
         super.clearEvents(inputSource);
     }
 
@@ -209,6 +258,11 @@
             mWindowManager.removeView(mAutoclickIndicatorView);
             mAutoclickTypePanel.hide();
         }
+
+        if (mAutoclickScrollPanel != null) {
+            mAutoclickScrollPanel.hide();
+            mAutoclickScrollPanel = null;
+        }
     }
 
     private void handleMouseMotion(MotionEvent event, int policyFlags) {
@@ -231,7 +285,11 @@
 
     private boolean isPaused() {
         return Flags.enableAutoclickIndicator() && mAutoclickTypePanel.isPaused()
-                && !mAutoclickTypePanel.isHovered();
+                && !isHovered();
+    }
+
+    private boolean isHovered() {
+        return Flags.enableAutoclickIndicator() && mAutoclickTypePanel.isHovered();
     }
 
     private void cancelPendingClick() {
@@ -243,6 +301,22 @@
         }
     }
 
+    /**
+     * Handles scroll operations in the specified direction.
+     */
+    public void handleScroll(@AutoclickScrollPanel.ScrollDirection int direction) {
+        // TODO(b/388845721): Perform actual scroll.
+    }
+
+    /**
+     * Exits scroll mode and hides the scroll panel UI.
+     */
+    public void exitScrollMode() {
+        if (mAutoclickScrollPanel != null) {
+            mAutoclickScrollPanel.hide();
+        }
+    }
+
     @VisibleForTesting
     void onChangeForTesting(boolean selfChange, Uri uri) {
         mAutoclickSettingsObserver.onChange(selfChange, uri);
@@ -266,6 +340,10 @@
                 Settings.Secure.getUriFor(
                         Settings.Secure.ACCESSIBILITY_AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT);
 
+        private final Uri mAutoclickRevertToLeftClickSettingUri =
+                Settings.Secure.getUriFor(
+                        Settings.Secure.ACCESSIBILITY_AUTOCLICK_REVERT_TO_LEFT_CLICK);
+
         private ContentResolver mContentResolver;
         private ClickScheduler mClickScheduler;
         private AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
@@ -330,6 +408,13 @@
                         /* observer= */ this,
                         mUserId);
                 onChange(/* selfChange= */ true, mAutoclickIgnoreMinorCursorMovementSettingUri);
+
+                mContentResolver.registerContentObserver(
+                        mAutoclickRevertToLeftClickSettingUri,
+                        /* notifyForDescendants= */ false,
+                        /* observer= */ this,
+                        mUserId);
+                onChange(/* selfChange= */ true, mAutoclickRevertToLeftClickSettingUri);
             }
         }
 
@@ -386,6 +471,20 @@
                             == AccessibilityUtils.State.ON;
                     mClickScheduler.setIgnoreMinorCursorMovement(ignoreMinorCursorMovement);
                 }
+
+                if (mAutoclickRevertToLeftClickSettingUri.equals(uri)) {
+                    boolean revertToLeftClick =
+                            Settings.Secure.getIntForUser(
+                                    mContentResolver,
+                                    Settings.Secure
+                                            .ACCESSIBILITY_AUTOCLICK_REVERT_TO_LEFT_CLICK,
+                                    AUTOCLICK_REVERT_TO_LEFT_CLICK_DEFAULT
+                                            ? AccessibilityUtils.State.ON
+                                            : AccessibilityUtils.State.OFF,
+                                    mUserId)
+                            == AccessibilityUtils.State.ON;
+                    mClickScheduler.setRevertToLeftClick(revertToLeftClick);
+                }
             }
         }
     }
@@ -467,6 +566,9 @@
         /** Whether the minor cursor movement should be ignored. */
         private boolean mIgnoreMinorCursorMovement = AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT;
 
+        /** Whether the autoclick type reverts to left click once performing an action. */
+        private boolean mRevertToLeftClick = AUTOCLICK_REVERT_TO_LEFT_CLICK_DEFAULT;
+
         /** Whether there is pending click. */
         private boolean mActive;
         /** If active, time at which pending click is scheduled. */
@@ -478,6 +580,8 @@
         private int mEventPolicyFlags;
         /** Current meta state. This value will be used as meta state for click event sequence. */
         private int mMetaState;
+        /** Last observed panel hovered state when click was scheduled. */
+        private boolean mHoveredState;
 
         /**
          * The current anchor's coordinates. Should be ignored if #mLastMotionEvent is null.
@@ -515,6 +619,7 @@
 
             sendClick();
             resetInternalState();
+            resetSelectedClickTypeIfNecessary();
         }
 
         /**
@@ -593,6 +698,11 @@
             return mDelay;
         }
 
+        @VisibleForTesting
+        boolean getRevertToLeftClickForTesting() {
+            return mRevertToLeftClick;
+        }
+
         /**
          * Updates the time at which click sequence should occur.
          *
@@ -631,6 +741,7 @@
             }
             mLastMotionEvent = MotionEvent.obtain(event);
             mEventPolicyFlags = policyFlags;
+            mHoveredState = isHovered();
 
             if (useAsAnchor) {
                 final int pointerIndex = mLastMotionEvent.getActionIndex();
@@ -651,6 +762,12 @@
             }
         }
 
+        private void resetSelectedClickTypeIfNecessary() {
+            if (mRevertToLeftClick && mActiveClickType != AUTOCLICK_TYPE_LEFT_CLICK) {
+                mAutoclickTypePanel.resetSelectedClickType();
+            }
+        }
+
         /**
          * @param event Observed motion event.
          * @return Whether the event coords are far enough from the anchor for the event not to be
@@ -675,6 +792,10 @@
             mIgnoreMinorCursorMovement = ignoreMinorCursorMovement;
         }
 
+        public void setRevertToLeftClick(boolean revertToLeftClick) {
+            mRevertToLeftClick = revertToLeftClick;
+        }
+
         private void updateMovementSlop(double slop) {
             mMovementSlop = slop;
         }
@@ -687,6 +808,22 @@
                 return;
             }
 
+            if (mAutoclickScrollPanel != null && mAutoclickScrollPanel.isVisible()) {
+                // If exit button is hovered, exit scroll mode after countdown and return early.
+                if (mHoveredDirection == AutoclickScrollPanel.DIRECTION_EXIT) {
+                    exitScrollMode();
+                }
+                return;
+            }
+
+            // Handle scroll type specially, show scroll panel instead of sending click events.
+            if (mActiveClickType == AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL) {
+                if (mAutoclickScrollPanel != null) {
+                    mAutoclickScrollPanel.show();
+                }
+                return;
+            }
+
             final int pointerIndex = mLastMotionEvent.getActionIndex();
 
             if (mTempPointerProperties == null) {
@@ -704,19 +841,40 @@
 
             final long now = SystemClock.uptimeMillis();
 
-            // TODO(b/395094903): always triggers left-click when the cursor hovers over the
-            // autoclick type panel, to always allow users to change a different click type.
-            // Otherwise, if one chooses the right-click, this user won't be able to rely on
-            // autoclick to select other click types.
-            final int actionButton =
-                    mActiveClickType == AUTOCLICK_TYPE_RIGHT_CLICK
-                            ? BUTTON_SECONDARY
-                            : BUTTON_PRIMARY;
+            int actionButton = BUTTON_PRIMARY;
+            if (mHoveredState) {
+                // Always triggers left-click when the cursor hovers over the autoclick type
+                // panel, to always allow users to change a different click type. Otherwise, if
+                // one chooses the right-click, this user won't be able to rely on autoclick to
+                // select other click types.
+                actionButton = BUTTON_PRIMARY;
+            } else {
+                switch (mActiveClickType) {
+                    case AUTOCLICK_TYPE_LEFT_CLICK:
+                        actionButton = BUTTON_PRIMARY;
+                        break;
+                    case AUTOCLICK_TYPE_RIGHT_CLICK:
+                        actionButton = BUTTON_SECONDARY;
+                        break;
+                    case AUTOCLICK_TYPE_DOUBLE_CLICK:
+                        actionButton = BUTTON_PRIMARY;
+                        long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+                        sendMotionEvent(actionButton, now);
+                        sendMotionEvent(actionButton, now + doubleTapMinimumTimeout);
+                        return;
+                    default:
+                        break;
+                }
+            }
 
+            sendMotionEvent(actionButton, now);
+        }
+
+        private void sendMotionEvent(int actionButton, long eventTime) {
             MotionEvent downEvent =
                     MotionEvent.obtain(
-                            /* downTime= */ now,
-                            /* eventTime= */ now,
+                            /* downTime= */ eventTime,
+                            /* eventTime= */ eventTime,
                             MotionEvent.ACTION_DOWN,
                             /* pointerCount= */ 1,
                             mTempPointerProperties,
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java
new file mode 100644
index 0000000..c714431
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickScrollPanel.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility.autoclick;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.ImageButton;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public class AutoclickScrollPanel {
+    public static final int DIRECTION_UP = 0;
+    public static final int DIRECTION_DOWN = 1;
+    public static final int DIRECTION_LEFT = 2;
+    public static final int DIRECTION_RIGHT = 3;
+    public static final int DIRECTION_EXIT = 4;
+    public static final int DIRECTION_NONE = 5;
+
+    @IntDef({
+            DIRECTION_UP,
+            DIRECTION_DOWN,
+            DIRECTION_LEFT,
+            DIRECTION_RIGHT,
+            DIRECTION_EXIT,
+            DIRECTION_NONE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScrollDirection {}
+
+    private final Context mContext;
+    private final View mContentView;
+    private final WindowManager mWindowManager;
+    private ScrollPanelControllerInterface mScrollPanelController;
+
+    // Scroll panel buttons.
+    private final ImageButton mUpButton;
+    private final ImageButton mDownButton;
+    private final ImageButton mLeftButton;
+    private final ImageButton mRightButton;
+    private final ImageButton mExitButton;
+
+    private boolean mInScrollMode = false;
+
+    /**
+     * Interface for handling scroll operations.
+     */
+    public interface ScrollPanelControllerInterface {
+        /**
+         * Called when a button hover state changes.
+         *
+         * @param direction The direction associated with the button.
+         * @param hovered Whether the button is being hovered or not.
+         */
+        void onHoverButtonChange(@ScrollDirection int direction, boolean hovered);
+    }
+
+    public AutoclickScrollPanel(Context context, WindowManager windowManager,
+            ScrollPanelControllerInterface controller) {
+        mContext = context;
+        mWindowManager = windowManager;
+        mScrollPanelController = controller;
+        mContentView = LayoutInflater.from(context).inflate(
+                R.layout.accessibility_autoclick_scroll_panel, null);
+
+        // Initialize buttons.
+        mUpButton = mContentView.findViewById(R.id.scroll_up);
+        mLeftButton = mContentView.findViewById(R.id.scroll_left);
+        mRightButton = mContentView.findViewById(R.id.scroll_right);
+        mDownButton = mContentView.findViewById(R.id.scroll_down);
+        mExitButton = mContentView.findViewById(R.id.scroll_exit);
+
+        initializeButtonState();
+    }
+
+    /**
+     * Sets up hover listeners for scroll panel buttons.
+     */
+    private void initializeButtonState() {
+        // Set up hover listeners for all buttons.
+        setupHoverListenerForButton(mUpButton, DIRECTION_UP);
+        setupHoverListenerForButton(mLeftButton, DIRECTION_LEFT);
+        setupHoverListenerForButton(mRightButton, DIRECTION_RIGHT);
+        setupHoverListenerForButton(mDownButton, DIRECTION_DOWN);
+        setupHoverListenerForButton(mExitButton, DIRECTION_EXIT);
+    }
+
+    /**
+     * Shows the autoclick scroll panel.
+     */
+    public void show() {
+        if (mInScrollMode) {
+            return;
+        }
+        mWindowManager.addView(mContentView, getLayoutParams());
+        mInScrollMode = true;
+    }
+
+    /**
+     * Hides the autoclick scroll panel.
+     */
+    public void hide() {
+        if (!mInScrollMode) {
+            return;
+        }
+        mWindowManager.removeView(mContentView);
+        mInScrollMode = false;
+    }
+
+    /**
+     * Sets up a hover listener for a button.
+     */
+    private void setupHoverListenerForButton(ImageButton button, @ScrollDirection int direction) {
+        button.setOnHoverListener((v, event) -> {
+            if (mScrollPanelController == null) {
+                return true;
+            }
+
+            boolean hovered;
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_HOVER_ENTER:
+                    hovered = true;
+                    break;
+                case MotionEvent.ACTION_HOVER_MOVE:
+                    // For direction buttons, continuously trigger scroll on hover move.
+                    if (direction != DIRECTION_EXIT) {
+                        hovered = true;
+                    } else {
+                        // Ignore hover move events for exit button.
+                        return true;
+                    }
+                    break;
+                case MotionEvent.ACTION_HOVER_EXIT:
+                    hovered = false;
+                    break;
+                default:
+                    return true;
+            }
+
+            // Notify the controller about the hover change.
+            mScrollPanelController.onHoverButtonChange(direction, hovered);
+            return true;
+        });
+    }
+
+    /**
+     * Retrieves the layout params for AutoclickScrollPanel, used when it's added to the Window
+     * Manager.
+     */
+    @NonNull
+    private WindowManager.LayoutParams getLayoutParams() {
+        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
+        layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+        layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+        layoutParams.setFitInsetsTypes(WindowInsets.Type.statusBars());
+        layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        layoutParams.format = PixelFormat.TRANSLUCENT;
+        layoutParams.setTitle(AutoclickScrollPanel.class.getSimpleName());
+        layoutParams.accessibilityTitle =
+                mContext.getString(R.string.accessibility_autoclick_scroll_panel_title);
+        layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
+        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        layoutParams.gravity = Gravity.CENTER;
+        return layoutParams;
+    }
+
+    @VisibleForTesting
+    public boolean isVisible() {
+        return mInScrollMode;
+    }
+
+    @VisibleForTesting
+    public View getContentViewForTesting() {
+        return mContentView;
+    }
+
+    @VisibleForTesting
+    public WindowManager.LayoutParams getLayoutParamsForTesting() {
+        return getLayoutParams();
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
index 90ddc43..c29829f 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
@@ -266,16 +266,28 @@
     }
 
     private void initializeButtonState() {
-        mLeftClickButton.setOnClickListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_LEFT_CLICK));
-        mRightClickButton.setOnClickListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_RIGHT_CLICK));
+        // Use `createButtonListener()` to append extra pause logic to each button's click.
+        mLeftClickButton.setOnClickListener(
+                wrapWithTogglePauseListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_LEFT_CLICK)));
+        mRightClickButton.setOnClickListener(
+                wrapWithTogglePauseListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_RIGHT_CLICK)));
         mDoubleClickButton.setOnClickListener(
-                v -> togglePanelExpansion(AUTOCLICK_TYPE_DOUBLE_CLICK));
-        mScrollButton.setOnClickListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_SCROLL));
-        mDragButton.setOnClickListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_DRAG));
-        mPositionButton.setOnClickListener(v -> moveToNextCorner());
+                wrapWithTogglePauseListener(
+                        v -> togglePanelExpansion(AUTOCLICK_TYPE_DOUBLE_CLICK)));
+        mScrollButton.setOnClickListener(
+                wrapWithTogglePauseListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_SCROLL)));
+        mDragButton.setOnClickListener(
+                wrapWithTogglePauseListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_DRAG)));
+        mPositionButton.setOnClickListener(wrapWithTogglePauseListener(v -> moveToNextCorner()));
+
+        // The pause button calls `togglePause()` directly so it does not need extra logic.
         mPauseButton.setOnClickListener(v -> togglePause());
 
-        // Initializes panel as collapsed state and only displays the left click button.
+        resetSelectedClickType();
+    }
+
+    /** Reset panel as collapsed state and only displays the left click button. */
+    public void resetSelectedClickType() {
         hideAllClickTypeButtons();
         mLeftClickButton.setVisibility(View.VISIBLE);
         setSelectedClickType(AUTOCLICK_TYPE_LEFT_CLICK);
@@ -517,6 +529,18 @@
         return true;
     }
 
+    /* Appends a check of the pause state to the button's listener. */
+    private View.OnClickListener wrapWithTogglePauseListener(View.OnClickListener listener) {
+        return v -> {
+            listener.onClick(v);
+
+            // Resumes autoclick if the button is clicked while in a paused state.
+            if (mPaused) {
+                togglePause();
+            }
+        };
+    }
+
     @VisibleForTesting
     boolean getExpansionStateForTesting() {
         return mExpanded;
@@ -552,7 +576,7 @@
     private WindowManager.LayoutParams getDefaultLayoutParams() {
         final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
         layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
         layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         layoutParams.setFitInsetsTypes(WindowInsets.Type.statusBars());
         layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index fb32943..b02fe27 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -653,6 +653,14 @@
             case ACTION_UP:
                 handleActionUp(event, rawEvent, policyFlags);
                 break;
+            case ACTION_POINTER_UP:
+                if (com.android.server.accessibility.Flags
+                        .pointerUpMotionEventInTouchExploration()) {
+                    if (mState.isServiceDetectingGestures()) {
+                        mAms.sendMotionEventToListeningServices(rawEvent);
+                    }
+                }
+                break;
             default:
                 break;
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index cd46b38..568abd1 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -26,6 +26,8 @@
 import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityEvent;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.server.accessibility.AccessibilityManagerService;
 
 /**
@@ -73,7 +75,8 @@
     private int mState = STATE_CLEAR;
     // Helper class to track received pointers.
     // Todo: collapse or hide this class so multiple classes don't modify it.
-    private final ReceivedPointerTracker mReceivedPointerTracker;
+    @VisibleForTesting
+    public final ReceivedPointerTracker mReceivedPointerTracker;
     // The most recently received motion event.
     private MotionEvent mLastReceivedEvent;
     // The accompanying raw event without any transformations.
@@ -219,8 +222,19 @@
                 startTouchInteracting();
                 break;
             case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
-                setState(STATE_CLEAR);
-                // We will clear when we actually handle the next ACTION_DOWN.
+                // When interaction ends, check if there are still down pointers.
+                // If there are any down pointers, go directly to TouchExploring instead.
+                if (com.android.server.accessibility.Flags
+                        .pointerUpMotionEventInTouchExploration()) {
+                    if (mReceivedPointerTracker.mReceivedPointersDown > 0) {
+                        startTouchExploring();
+                    } else {
+                        setState(STATE_CLEAR);
+                        // We will clear when we actually handle the next ACTION_DOWN.
+                    }
+                } else {
+                    setState(STATE_CLEAR);
+                }
                 break;
             case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
                 startTouchExploring();
@@ -419,7 +433,8 @@
         private final PointerDownInfo[] mReceivedPointers = new PointerDownInfo[MAX_POINTER_COUNT];
 
         // Which pointers are down.
-        private int mReceivedPointersDown;
+        @VisibleForTesting
+        public int mReceivedPointersDown;
 
         // The edge flags of the last received down event.
         private int mLastReceivedDownEdgeFlags;
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 6ccf5e4..5956667 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -39,6 +39,14 @@
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_DELAY_AFTER_ANIMATION_END;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_FILL_DIALOG_DISABLED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_LAST_TRIGGERED_ID_CHANGED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_SCREEN_HAS_CREDMAN_FIELD;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_TIMEOUT_AFTER_DELAY;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_TIMEOUT_SINCE_IME_ANIMATED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_WAIT_FOR_IME_ANIMATION;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_FILL_REQUEST_FAILED;
@@ -157,8 +165,24 @@
             DETECTION_PREFER_PCC
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface DetectionPreference {
-    }
+    public @interface DetectionPreference {}
+
+    /**
+     * The fill dialog not shown reason. These are wrappers around
+     * {@link com.android.os.AtomsProto.AutofillPresentationEventReported.FillDialogNotShownReason}.
+     */
+    @IntDef(prefix = {"FILL_DIALOG_NOT_SHOWN_REASON"}, value = {
+        FILL_DIALOG_NOT_SHOWN_REASON_UNKNOWN,
+        FILL_DIALOG_NOT_SHOWN_REASON_FILL_DIALOG_DISABLED,
+        FILL_DIALOG_NOT_SHOWN_REASON_SCREEN_HAS_CREDMAN_FIELD,
+        FILL_DIALOG_NOT_SHOWN_REASON_LAST_TRIGGERED_ID_CHANGED,
+        FILL_DIALOG_NOT_SHOWN_REASON_WAIT_FOR_IME_ANIMATION,
+        FILL_DIALOG_NOT_SHOWN_REASON_TIMEOUT_SINCE_IME_ANIMATED,
+        FILL_DIALOG_NOT_SHOWN_REASON_DELAY_AFTER_ANIMATION_END,
+        FILL_DIALOG_NOT_SHOWN_REASON_TIMEOUT_AFTER_DELAY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FillDialogNotShownReason {}
 
     public static final int NOT_SHOWN_REASON_ANY_SHOWN =
             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
@@ -219,6 +243,25 @@
     public static final int DETECTION_PREFER_PCC =
             AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
 
+    // Values for AutofillFillResponseReported.fill_dialog_not_shown_reason
+    public static final int FILL_DIALOG_NOT_SHOWN_REASON_UNKNOWN =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_UNKNOWN;
+    public static final int FILL_DIALOG_NOT_SHOWN_REASON_FILL_DIALOG_DISABLED =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_FILL_DIALOG_DISABLED;
+    public static final int FILL_DIALOG_NOT_SHOWN_REASON_SCREEN_HAS_CREDMAN_FIELD =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_SCREEN_HAS_CREDMAN_FIELD;
+    public static final int FILL_DIALOG_NOT_SHOWN_REASON_LAST_TRIGGERED_ID_CHANGED =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_LAST_TRIGGERED_ID_CHANGED;
+    public static final int FILL_DIALOG_NOT_SHOWN_REASON_WAIT_FOR_IME_ANIMATION =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_WAIT_FOR_IME_ANIMATION;
+    public static final int FILL_DIALOG_NOT_SHOWN_REASON_TIMEOUT_SINCE_IME_ANIMATED =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_TIMEOUT_SINCE_IME_ANIMATED;
+    public static final int FILL_DIALOG_NOT_SHOWN_REASON_DELAY_AFTER_ANIMATION_END =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_DELAY_AFTER_ANIMATION_END;
+    public static final int FILL_DIALOG_NOT_SHOWN_REASON_TIMEOUT_AFTER_DELAY =
+            AUTOFILL_PRESENTATION_EVENT_REPORTED__FILL_DIALOG_NOT_SHOWN_REASON__REASON_TIMEOUT_AFTER_DELAY;
+
+
     private static final int DEFAULT_VALUE_INT = -1;
 
     private final int mSessionId;
@@ -871,6 +914,43 @@
     }
 
     /**
+     * Set fill_dialog_not_shown_reason
+     * @param reason
+     */
+    public void maybeSetFillDialogNotShownReason(@FillDialogNotShownReason int reason) {
+        mEventInternal.ifPresent(event -> {
+            if ((event.mFillDialogNotShownReason
+                    == FILL_DIALOG_NOT_SHOWN_REASON_DELAY_AFTER_ANIMATION_END
+                    || event.mFillDialogNotShownReason
+                    == FILL_DIALOG_NOT_SHOWN_REASON_WAIT_FOR_IME_ANIMATION) && reason
+                    == FILL_DIALOG_NOT_SHOWN_REASON_TIMEOUT_SINCE_IME_ANIMATED) {
+                event.mFillDialogNotShownReason = FILL_DIALOG_NOT_SHOWN_REASON_TIMEOUT_AFTER_DELAY;
+            } else {
+                event.mFillDialogNotShownReason = reason;
+            }
+        });
+    }
+
+    /**
+     * Set fill_dialog_ready_to_show_ms
+     * @param val
+     */
+    public void maybeSetFillDialogReadyToShowMs(long val) {
+        mEventInternal.ifPresent(event -> {
+            event.mFillDialogReadyToShowMs = (int) (val - mSessionStartTimestamp);
+        });
+    }
+
+    /**
+     * Set ime_animation_finish_ms
+     * @param val
+     */
+    public void maybeSetImeAnimationFinishMs(long val) {
+        mEventInternal.ifPresent(event -> {
+            event.mImeAnimationFinishMs = (int) (val - mSessionStartTimestamp);
+        });
+    }
+    /**
      * Set the log contains relayout metrics.
      * This is being added as a temporary measure to add logging.
      * In future, when we map Session's old view states to the new autofill id's as part of fixing
@@ -959,7 +1039,13 @@
                     + " event.notExpiringResponseDuringAuthCount="
                     + event.mFixExpireResponseDuringAuthCount
                     + " event.notifyViewEnteredIgnoredDuringAuthCount="
-                    + event.mNotifyViewEnteredIgnoredDuringAuthCount);
+                    + event.mNotifyViewEnteredIgnoredDuringAuthCount
+                    + " event.fillDialogNotShownReason="
+                    + event.mFillDialogNotShownReason
+                    + " event.fillDialogReadyToShowMs="
+                    + event.mFillDialogReadyToShowMs
+                    + " event.imeAnimationFinishMs="
+                    + event.mImeAnimationFinishMs);
         }
 
         // TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -1020,7 +1106,10 @@
                 event.mViewFilledSuccessfullyOnRefillCount,
                 event.mViewFailedOnRefillCount,
                 event.mFixExpireResponseDuringAuthCount,
-                event.mNotifyViewEnteredIgnoredDuringAuthCount);
+                event.mNotifyViewEnteredIgnoredDuringAuthCount,
+                event.mFillDialogNotShownReason,
+                event.mFillDialogReadyToShowMs,
+                event.mImeAnimationFinishMs);
         mEventInternal = Optional.empty();
     }
 
@@ -1087,6 +1176,9 @@
         // Following are not logged and used only for internal logic
         boolean shouldResetShownCount = false;
         boolean mHasRelayoutLog = false;
+        @FillDialogNotShownReason int mFillDialogNotShownReason = FILL_DIALOG_NOT_SHOWN_REASON_UNKNOWN;
+        int mFillDialogReadyToShowMs = DEFAULT_VALUE_INT;
+        int mImeAnimationFinishMs = DEFAULT_VALUE_INT;
         PresentationStatsEventInternal() {}
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 6fdb2b6..ff3bf2a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -64,6 +64,7 @@
 import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_PCC;
 import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_UNKNOWN;
 import static com.android.server.autofill.FillResponseEventLogger.HAVE_SAVE_TRIGGER_ID;
+import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_CANCELLED;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_FAILURE;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SESSION_DESTROYED;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SUCCESS;
@@ -80,6 +81,13 @@
 import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_RESULT_SUCCESS;
 import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_TYPE_DATASET_AUTHENTICATION;
 import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_TYPE_FULL_AUTHENTICATION;
+import static com.android.server.autofill.PresentationStatsEventLogger.FILL_DIALOG_NOT_SHOWN_REASON_DELAY_AFTER_ANIMATION_END;
+import static com.android.server.autofill.PresentationStatsEventLogger.FILL_DIALOG_NOT_SHOWN_REASON_FILL_DIALOG_DISABLED;
+import static com.android.server.autofill.PresentationStatsEventLogger.FILL_DIALOG_NOT_SHOWN_REASON_LAST_TRIGGERED_ID_CHANGED;
+import static com.android.server.autofill.PresentationStatsEventLogger.FILL_DIALOG_NOT_SHOWN_REASON_SCREEN_HAS_CREDMAN_FIELD;
+import static com.android.server.autofill.PresentationStatsEventLogger.FILL_DIALOG_NOT_SHOWN_REASON_TIMEOUT_SINCE_IME_ANIMATED;
+import static com.android.server.autofill.PresentationStatsEventLogger.FILL_DIALOG_NOT_SHOWN_REASON_UNKNOWN;
+import static com.android.server.autofill.PresentationStatsEventLogger.FILL_DIALOG_NOT_SHOWN_REASON_WAIT_FOR_IME_ANIMATION;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_ANY_SHOWN;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_NO_FOCUS;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_FAILED;
@@ -1416,6 +1424,15 @@
 
         // Remove the FillContext as there will never be a response for the service
         if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
+            // Start a new FillResponse logger for the cancellation case.
+            mFillResponseEventLogger.startLogForNewResponse();
+            mFillResponseEventLogger.maybeSetRequestId(canceledRequest);
+            mFillResponseEventLogger.maybeSetAppPackageUid(uid);
+            mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_CANCELLED);
+            mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
+                    (int) (SystemClock.elapsedRealtime() - mLatencyBaseTime));
+            mFillResponseEventLogger.logAndEndEvent();
+
             final int numContexts = mContexts.size();
 
             // It is most likely the last context, hence search backwards
@@ -5612,6 +5629,10 @@
                 synchronized (mLock) {
                     final ViewState currentView = mViewStates.get(mCurrentViewId);
                     currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
+                    // Set fill_dialog_not_shown_reason to unknown (a.k.a shown). It is needed due
+                    // to possible SHOW_FILL_DIALOG_WAIT.
+                    mPresentationStatsEventLogger.maybeSetFillDialogNotShownReason(
+                            FILL_DIALOG_NOT_SHOWN_REASON_UNKNOWN);
                 }
                 // Just show fill dialog once per fill request, so disabled after shown.
                 // Note: Cannot disable before requestShowFillDialog() because the method
@@ -5715,6 +5736,15 @@
 
     private boolean isFillDialogUiEnabled() {
         synchronized (mLock) {
+            if (mSessionFlags.mFillDialogDisabled) {
+                mPresentationStatsEventLogger.maybeSetFillDialogNotShownReason(
+                        FILL_DIALOG_NOT_SHOWN_REASON_FILL_DIALOG_DISABLED);
+            }
+            if (mSessionFlags.mScreenHasCredmanField) {
+                // Prefer to log "HAS_CREDMAN_FIELD" over "FILL_DIALOG_DISABLED".
+                mPresentationStatsEventLogger.maybeSetFillDialogNotShownReason(
+                        FILL_DIALOG_NOT_SHOWN_REASON_SCREEN_HAS_CREDMAN_FIELD);
+            }
             return !mSessionFlags.mFillDialogDisabled && !mSessionFlags.mScreenHasCredmanField;
         }
     }
@@ -5779,6 +5809,8 @@
                     || !ArrayUtils.contains(mLastFillDialogTriggerIds, filledId)) {
                 // Last fill dialog triggered ids are changed.
                 if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed.");
+                mPresentationStatsEventLogger.maybeSetFillDialogNotShownReason(
+                        FILL_DIALOG_NOT_SHOWN_REASON_LAST_TRIGGERED_ID_CHANGED);
                 return SHOW_FILL_DIALOG_NO;
             }
 
@@ -5805,6 +5837,8 @@
                     // we need to wait for animation to happen. We can't return from here yet.
                     // This is the situation #2 described above.
                     Log.d(TAG, "Waiting for ime animation to complete before showing fill dialog");
+                    mPresentationStatsEventLogger.maybeSetFillDialogNotShownReason(
+                            FILL_DIALOG_NOT_SHOWN_REASON_WAIT_FOR_IME_ANIMATION);
                     mFillDialogRunnable = createFillDialogEvalRunnable(
                             response, filledId, filterText, flags);
                     return SHOW_FILL_DIALOG_WAIT;
@@ -5814,9 +5848,15 @@
                 // max of start input time or the ime finish time
                 long effectiveDuration = currentTimestampMs
                         - Math.max(mLastInputStartTime, mImeAnimationFinishTimeMs);
+                mPresentationStatsEventLogger.maybeSetFillDialogReadyToShowMs(
+                        currentTimestampMs);
+                mPresentationStatsEventLogger.maybeSetImeAnimationFinishMs(
+                        Math.max(mLastInputStartTime, mImeAnimationFinishTimeMs));
                 if (effectiveDuration >= mFillDialogTimeoutMs) {
                     Log.d(TAG, "Fill dialog not shown since IME has been up for more time than "
                             + mFillDialogTimeoutMs + "ms");
+                    mPresentationStatsEventLogger.maybeSetFillDialogNotShownReason(
+                            FILL_DIALOG_NOT_SHOWN_REASON_TIMEOUT_SINCE_IME_ANIMATED);
                     return SHOW_FILL_DIALOG_NO;
                 } else if (effectiveDuration < mFillDialogMinWaitAfterImeAnimationMs) {
                     // we need to wait for some time after animation ends
@@ -5824,6 +5864,8 @@
                             response, filledId, filterText, flags);
                     mHandler.postDelayed(runnable,
                             mFillDialogMinWaitAfterImeAnimationMs - effectiveDuration);
+                    mPresentationStatsEventLogger.maybeSetFillDialogNotShownReason(
+                            FILL_DIALOG_NOT_SHOWN_REASON_DELAY_AFTER_ANIMATION_END);
                     return SHOW_FILL_DIALOG_WAIT;
                 }
             }
diff --git a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
index 9a353fb..867cd51 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
@@ -44,6 +44,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupRestoreTask.CancellationReason;
 import com.android.server.backup.internal.LifecycleOperationStorage;
 
 import java.util.Set;
@@ -298,20 +299,22 @@
             // Offload operation cancellation off the main thread as the cancellation callbacks
             // might call out to BackupTransport. Other operations started on the same package
             // before the cancellation callback has executed will also be cancelled by the callback.
-            Runnable cancellationRunnable = () -> {
-                // handleCancel() causes the PerformFullTransportBackupTask to go on to
-                // tearDownAgentAndKill: that will unbindBackupAgent in the Activity Manager, so
-                // that the package being backed up doesn't get stuck in restricted mode until the
-                // backup time-out elapses.
-                for (int token : mOperationStorage.operationTokensForPackage(packageName)) {
-                    if (DEBUG) {
-                        Slog.d(TAG,
-                                mUserIdMsg + "agentDisconnected: will handleCancel(all) for token:"
-                                        + Integer.toHexString(token));
-                    }
-                    mUserBackupManagerService.handleCancel(token, true /* cancelAll */);
-                }
-            };
+            Runnable cancellationRunnable =
+                    () -> {
+                        // On handleCancel(), the operation will call unbindAgent() which will make
+                        // sure the app doesn't get stuck in restricted mode.
+                        for (int token : mOperationStorage.operationTokensForPackage(packageName)) {
+                            if (DEBUG) {
+                                Slog.d(
+                                        TAG,
+                                        mUserIdMsg
+                                                + "agentDisconnected: cancelling for token:"
+                                                + Integer.toHexString(token));
+                            }
+                            mUserBackupManagerService.handleCancel(
+                                    token, CancellationReason.AGENT_DISCONNECTED);
+                        }
+                    };
             getThreadForCancellation(cancellationRunnable).start();
 
             mAgentConnectLock.notifyAll();
diff --git a/services/backup/java/com/android/server/backup/BackupRestoreTask.java b/services/backup/java/com/android/server/backup/BackupRestoreTask.java
index acaab0c..7ec5f0d 100644
--- a/services/backup/java/com/android/server/backup/BackupRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/BackupRestoreTask.java
@@ -16,9 +16,12 @@
 
 package com.android.server.backup;
 
-/**
- * Interface and methods used by the asynchronous-with-timeout backup/restore operations.
- */
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Interface and methods used by the asynchronous-with-timeout backup/restore operations. */
 public interface BackupRestoreTask {
 
     // Execute one tick of whatever state machine the task implements
@@ -27,6 +30,24 @@
     // An operation that wanted a callback has completed
     void operationComplete(long result);
 
-    // An operation that wanted a callback has timed out
-    void handleCancel(boolean cancelAll);
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        CancellationReason.TIMEOUT,
+        CancellationReason.AGENT_DISCONNECTED,
+        CancellationReason.EXTERNAL,
+        CancellationReason.SCHEDULED_JOB_STOPPED,
+    })
+    @interface CancellationReason {
+        // The task timed out.
+        int TIMEOUT = 0;
+        // The agent went away before the task was able to finish (e.g. due to an app crash).
+        int AGENT_DISCONNECTED = 1;
+        // An external caller cancelled the operation (e.g. via BackupManager#cancelBackups).
+        int EXTERNAL = 2;
+        // The job scheduler has stopped an ongoing scheduled backup pass.
+        int SCHEDULED_JOB_STOPPED = 3;
+    }
+
+    /** The task is cancelled for the given {@link CancellationReason}. */
+    void handleCancel(@CancellationReason int cancellationReason);
 }
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 2143aaa..b3af444 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -102,6 +102,7 @@
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupRestoreTask.CancellationReason;
 import com.android.server.backup.OperationStorage.OpState;
 import com.android.server.backup.OperationStorage.OpType;
 import com.android.server.backup.fullbackup.FullBackupEntry;
@@ -168,6 +169,7 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.IntConsumer;
 
 /** System service that performs backup/restore operations. */
 public class UserBackupManagerService {
@@ -1816,11 +1818,9 @@
 
             for (Integer token : operationsToCancel) {
                 mOperationStorage.cancelOperation(
-                        token, /* cancelAll */
-                        true,
-                        operationType -> {
-                            /* no callback needed here */
-                        });
+                        token,
+                        operationType -> {}, // no callback needed here
+                        CancellationReason.EXTERNAL);
             }
             // We don't want the backup jobs to kick in any time soon.
             // Reschedules them to run in the distant future.
@@ -1897,19 +1897,17 @@
     }
 
     /** Cancel the operation associated with {@code token}. */
-    public void handleCancel(int token, boolean cancelAll) {
+    public void handleCancel(int token, @CancellationReason int cancellationReason) {
         // Remove all pending timeout messages of types OpType.BACKUP_WAIT and
         // OpType.RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
         // doesn't require cancellation.
-        mOperationStorage.cancelOperation(
-                token,
-                cancelAll,
-                operationType -> {
-                    if (operationType == OpType.BACKUP_WAIT
-                            || operationType == OpType.RESTORE_WAIT) {
-                        mBackupHandler.removeMessages(getMessageIdForOperationType(operationType));
+        IntConsumer timeoutCallback =
+                opType -> {
+                    if (opType == OpType.BACKUP_WAIT || opType == OpType.RESTORE_WAIT) {
+                        mBackupHandler.removeMessages(getMessageIdForOperationType(opType));
                     }
-                });
+                };
+        mOperationStorage.cancelOperation(token, timeoutCallback, cancellationReason);
     }
 
     /** Returns {@code true} if a backup is currently running, else returns {@code false}. */
@@ -2219,19 +2217,16 @@
         // offload the mRunningFullBackupTask.handleCancel() call to another thread,
         // as we might have to wait for mCancelLock
         Runnable endFullBackupRunnable =
-                new Runnable() {
-                    @Override
-                    public void run() {
-                        PerformFullTransportBackupTask pftbt = null;
-                        synchronized (mQueueLock) {
-                            if (mRunningFullBackupTask != null) {
-                                pftbt = mRunningFullBackupTask;
-                            }
+                () -> {
+                    PerformFullTransportBackupTask pftbt = null;
+                    synchronized (mQueueLock) {
+                        if (mRunningFullBackupTask != null) {
+                            pftbt = mRunningFullBackupTask;
                         }
-                        if (pftbt != null) {
-                            Slog.i(TAG, mLogIdMsg + "Telling running backup to stop");
-                            pftbt.handleCancel(true);
-                        }
+                    }
+                    if (pftbt != null) {
+                        Slog.i(TAG, mLogIdMsg + "Telling running backup to stop");
+                        pftbt.handleCancel(CancellationReason.SCHEDULED_JOB_STOPPED);
                     }
                 };
         new Thread(endFullBackupRunnable, "end-full-backup").start();
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index ebb1194..b173f76 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -25,6 +25,7 @@
 import android.annotation.UserIdInt;
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
+import android.app.backup.BackupManagerMonitor;
 import android.app.backup.BackupTransport;
 import android.app.backup.FullBackupDataOutput;
 import android.content.pm.ApplicationInfo;
@@ -268,6 +269,12 @@
                 mBackupManagerMonitorEventSender.monitorAgentLoggingResults(mPkg, mAgent);
             } catch (IOException e) {
                 Slog.e(TAG, "Error backing up " + mPkg.packageName + ": " + e.getMessage());
+                // This is likely due to the app process dying.
+                mBackupManagerMonitorEventSender.monitorEvent(
+                        BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_AGENT_PIPE_BROKEN,
+                        mPkg,
+                        BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+                        /* extras= */ null);
                 result = BackupTransport.AGENT_ERROR;
             } finally {
                 try {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 0d4364e..7fc9ed3 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -484,7 +484,7 @@
     }
 
     @Override
-    public void handleCancel(boolean cancelAll) {
+    public void handleCancel(@CancellationReason int cancellationReason) {
         final PackageInfo target = mCurrentTarget;
         Slog.w(TAG, "adb backup cancel of " + target);
         if (target != null) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index c182c26..fa67ef5 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -162,7 +162,7 @@
 
     // This is true when a backup operation for some package is in progress.
     private volatile boolean mIsDoingBackup;
-    private volatile boolean mCancelAll;
+    private volatile boolean mCancelled;
     private final int mCurrentOpToken;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private final BackupEligibilityRules mBackupEligibilityRules;
@@ -199,7 +199,7 @@
 
         if (backupManagerService.isBackupOperationInProgress()) {
             Slog.d(TAG, "Skipping full backup. A backup is already in progress.");
-            mCancelAll = true;
+            mCancelled = true;
             return;
         }
 
@@ -287,25 +287,31 @@
     }
 
     @Override
-    public void handleCancel(boolean cancelAll) {
+    public void handleCancel(@CancellationReason int cancellationReason) {
         synchronized (mCancelLock) {
-            // We only support 'cancelAll = true' case for this task. Cancelling of a single package
-
-            // due to timeout is handled by SinglePackageBackupRunner and
+            // This callback is only used for cancelling the entire backup operation. Cancelling of
+            // a single package due to timeout is handled by SinglePackageBackupRunner and
             // SinglePackageBackupPreflight.
-
-            if (!cancelAll) {
-                Slog.wtf(TAG, "Expected cancelAll to be true.");
+            if (cancellationReason == CancellationReason.TIMEOUT) {
+                Slog.wtf(TAG, "This task cannot time out");
+                return;
             }
 
-            if (mCancelAll) {
+            // We don't cancel the entire operation if a single agent is disconnected unexpectedly.
+            // SinglePackageBackupRunner and SinglePackageBackupPreflight will receive the same
+            // callback and fail gracefully. The operation should then continue to the next package.
+            if (cancellationReason == CancellationReason.AGENT_DISCONNECTED) {
+                return;
+            }
+
+            if (mCancelled) {
                 Slog.d(TAG, "Ignoring duplicate cancel call.");
                 return;
             }
 
-            mCancelAll = true;
+            mCancelled = true;
             if (mIsDoingBackup) {
-                mUserBackupManagerService.handleCancel(mBackupRunnerOpToken, cancelAll);
+                mUserBackupManagerService.handleCancel(mBackupRunnerOpToken, cancellationReason);
                 try {
                     // If we're running a backup we should be connected to a transport
                     BackupTransportClient transport =
@@ -410,7 +416,7 @@
                 int backupPackageStatus;
                 long quota = Long.MAX_VALUE;
                 synchronized (mCancelLock) {
-                    if (mCancelAll) {
+                    if (mCancelled) {
                         break;
                     }
                     backupPackageStatus = transport.performFullBackup(currentPackage,
@@ -478,7 +484,7 @@
                             if (nRead > 0) {
                                 out.write(buffer, 0, nRead);
                                 synchronized (mCancelLock) {
-                                    if (!mCancelAll) {
+                                    if (!mCancelled) {
                                         backupPackageStatus = transport.sendBackupData(nRead);
                                     }
                                 }
@@ -509,7 +515,7 @@
                     synchronized (mCancelLock) {
                         mIsDoingBackup = false;
                         // If mCancelCurrent is true, we have already called cancelFullBackup().
-                        if (!mCancelAll) {
+                        if (!mCancelled) {
                             if (backupRunnerResult == BackupTransport.TRANSPORT_OK) {
                                 // If we were otherwise in a good state, now interpret the final
                                 // result based on what finishBackup() returns.  If we're in a
@@ -607,7 +613,7 @@
                             .sendBackupOnPackageResult(mBackupObserver, packageName,
                                     BackupManager.ERROR_BACKUP_CANCELLED);
                     Slog.w(TAG, "Backup cancelled. package=" + packageName +
-                            ", cancelAll=" + mCancelAll);
+                            ", entire session cancelled=" + mCancelled);
                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_CANCELLED, packageName);
                     mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
                             currentPackage.applicationInfo, /* allowKill= */ true);
@@ -654,7 +660,7 @@
 
         } finally {
 
-            if (mCancelAll) {
+            if (mCancelled) {
                 backupRunStatus = BackupManager.ERROR_BACKUP_CANCELLED;
             }
 
@@ -820,7 +826,7 @@
         }
 
         @Override
-        public void handleCancel(boolean cancelAll) {
+        public void handleCancel(@CancellationReason int cancellationReason) {
             if (DEBUG) {
                 Slog.i(TAG, "Preflight cancelled; failing");
             }
@@ -974,17 +980,22 @@
         public void operationComplete(long result) { /* intentionally empty */ }
 
         @Override
-        public void handleCancel(boolean cancelAll) {
-            Slog.w(TAG, "Full backup cancel of " + mTarget.packageName);
+        public void handleCancel(@CancellationReason int cancellationReason) {
+            Slog.w(
+                    TAG,
+                    "Cancelled backup: " + mTarget.packageName + " reason:" + cancellationReason);
 
             mBackupManagerMonitorEventSender.monitorEvent(
                     BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL,
                     mTarget,
                     BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
-                    /* extras= */ null);
+                    BackupManagerMonitorEventSender.putMonitoringExtra(
+                            /* extras= */ null,
+                            BackupManagerMonitor.EXTRA_LOG_CANCELLATION_REASON,
+                            cancellationReason));
             mIsCancelled = true;
             // Cancel tasks spun off by this task.
-            mUserBackupManagerService.handleCancel(mEphemeralToken, cancelAll);
+            mUserBackupManagerService.handleCancel(mEphemeralToken, cancellationReason);
             mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
                     mTarget.applicationInfo, /* allowKill= */ true);
             // Free up everyone waiting on this task and its children.
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 87cf8a3..464dc2d 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -34,6 +34,7 @@
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.BackupRestoreTask.CancellationReason;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.OperationStorage;
 import com.android.server.backup.TransportManager;
@@ -410,8 +411,8 @@
 
             case MSG_BACKUP_OPERATION_TIMEOUT:
             case MSG_RESTORE_OPERATION_TIMEOUT: {
-                Slog.d(TAG, "Timeout message received for token=" + Integer.toHexString(msg.arg1));
-                backupManagerService.handleCancel(msg.arg1, false);
+                Slog.d(TAG, "Timeout for token=" + Integer.toHexString(msg.arg1));
+                backupManagerService.handleCancel(msg.arg1, CancellationReason.TIMEOUT);
                 break;
             }
 
diff --git a/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java b/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java
index 0b974e2..5aacb2f 100644
--- a/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java
+++ b/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java
@@ -24,6 +24,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.BackupRestoreTask.CancellationReason;
 import com.android.server.backup.OperationStorage;
 
 import com.google.android.collect.Sets;
@@ -296,20 +297,18 @@
     }
 
     /**
-     * Cancel the operation associated with {@code token}.  Cancellation may be
-     * propagated to the operation's callback (a {@link BackupRestoreTask}) if
-     * the operation has one, and the cancellation is due to the operation
-     * timing out.
+     * Cancel the operation associated with {@code token}. Cancellation may be propagated to the
+     * operation's callback (a {@link BackupRestoreTask}) if the operation has one, and the
+     * cancellation is due to the operation timing out.
      *
      * @param token the operation token specified when registering the operation
-     * @param cancelAll this is passed on when propagating the cancellation
-     * @param operationTimedOutCallback a lambda that is invoked with the
-     *                                  operation type where the operation is
-     *                                  cancelled due to timeout, allowing the
-     *                                  caller to do type-specific clean-ups.
+     * @param operationTimedOutCallback a lambda that is invoked with the operation type where the
+     *     operation is cancelled due to timeout, allowing the caller to do type-specific clean-ups.
      */
     public void cancelOperation(
-            int token, boolean cancelAll, IntConsumer operationTimedOutCallback) {
+            int token,
+            IntConsumer operationTimedOutCallback,
+            @CancellationReason int cancellationReason) {
         // Notify any synchronous waiters
         Operation op = null;
         synchronized (mOperationsLock) {
@@ -343,7 +342,7 @@
             if (DEBUG) {
                 Slog.v(TAG, "[UserID:" + mUserId + "   Invoking cancel on " + op.callback);
             }
-            op.callback.handleCancel(cancelAll);
+            op.callback.handleCancel(cancellationReason);
         }
     }
 }
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 494b9d5..8e7a23c 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -171,7 +171,7 @@
  * complete backup should be performed.
  *
  * <p>This task is designed to run on a dedicated thread, with the exception of the {@link
- * #handleCancel(boolean)} method, which can be called from any thread.
+ * BackupRestoreTask#handleCancel(int)} method, which can be called from any thread.
  */
 // TODO: Stop poking into BMS state and doing things for it (e.g. synchronizing on public locks)
 // TODO: Consider having the caller responsible for some clean-up (like resetting state)
@@ -1208,13 +1208,13 @@
      *
      * <p>Note: This method is inherently racy since there are no guarantees about how much of the
      * task will be executed after you made the call.
-     *
-     * @param cancelAll MUST be {@code true}. Will be removed.
      */
     @Override
-    public void handleCancel(boolean cancelAll) {
+    public void handleCancel(@CancellationReason int cancellationReason) {
         // This is called in a thread different from the one that executes method run().
-        Preconditions.checkArgument(cancelAll, "Can't partially cancel a key-value backup task");
+        Preconditions.checkArgument(
+                cancellationReason != CancellationReason.TIMEOUT,
+                "Key-value backup task cannot time out");
         markCancel();
         waitCancel();
     }
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index cb491c6..f1829b6 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -79,7 +79,7 @@
     }
 
     @Override
-    public void handleCancel(boolean cancelAll) {
+    public void handleCancel(@CancellationReason int cancellationReason) {
         Slog.w(TAG, "adb onRestoreFinished() timed out");
         mLatch.countDown();
         mOperationStorage.removeOperation(mCurrentOpToken);
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 707ae03..20f103c 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -589,6 +589,7 @@
                         monitoringExtras);
                 Slog.e(TAG, "Failure getting next package name");
                 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
+                mStatus = BackupTransport.TRANSPORT_ERROR;
                 nextState = UnifiedRestoreState.FINAL;
                 return;
             } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) {
@@ -1307,7 +1308,7 @@
 
         // The app has timed out handling a restoring file
         @Override
-        public void handleCancel(boolean cancelAll) {
+        public void handleCancel(@CancellationReason int cancellationReason) {
             mOperationStorage.removeOperation(mEphemeralOpToken);
             Slog.w(TAG, "Full-data restore target timed out; shutting down");
             Bundle monitoringExtras = addRestoreOperationTypeToEvent(/* extras= */ null);
@@ -1555,7 +1556,7 @@
 
     // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
     @Override
-    public void handleCancel(boolean cancelAll) {
+    public void handleCancel(@CancellationReason int cancellationReason) {
         mOperationStorage.removeOperation(mEphemeralOpToken);
         Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
         Bundle monitoringExtras = addRestoreOperationTypeToEvent(/* extras= */ null);
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
index fad59d2..855c72a 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
@@ -389,6 +389,8 @@
                     "Agent failure during restore";
             case BackupManagerMonitor.LOG_EVENT_ID_FAILED_TO_READ_DATA_FROM_TRANSPORT ->
                     "Failed to read data from Transport";
+            case BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_AGENT_PIPE_BROKEN ->
+                "LOG_EVENT_ID_FULL_BACKUP_AGENT_PIPE_BROKEN";
             default -> "Unknown log event ID: " + code;
         };
         return id;
diff --git a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
index cd9285c..cbee839 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -58,13 +58,16 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.server.companion.CompanionDeviceManagerService;
 import com.android.server.companion.utils.PackageUtils;
 
+import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Class responsible for handling incoming {@link AssociationRequest}s.
@@ -130,6 +133,12 @@
     private static final int ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW = 5;
     private static final long ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS = 60 * 60 * 1000; // 60 min;
 
+    // Set of profiles for which the association dialog cannot be skipped.
+    private static final Set<String> DEVICE_PROFILES_WITH_REQUIRED_CONFIRMATION = new ArraySet<>(
+            Arrays.asList(
+                    AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
+                    AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING));
+
     private final @NonNull Context mContext;
     private final @NonNull PackageManagerInternal mPackageManagerInternal;
     private final @NonNull AssociationStore mAssociationStore;
@@ -174,6 +183,7 @@
         // 2a. Check if association can be created without launching UI (i.e. CDM needs NEITHER
         // to perform discovery NOR to collect user consent).
         if (request.isSelfManaged() && !request.isForceConfirmation()
+                && !DEVICE_PROFILES_WITH_REQUIRED_CONFIRMATION.contains(request.getDeviceProfile())
                 && !willAddRoleHolder(request, packageName, userId)) {
             // 2a.1. Create association right away.
             createAssociationAndNotifyApplication(request, packageName, userId,
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 88b7910..6e098d01 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -21,15 +21,18 @@
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtual.sensor.IVirtualSensorCallback;
 import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.content.AttributionSource;
+import android.hardware.SensorAdditionalInfo;
 import android.hardware.SensorDirectChannel;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SharedMemory;
+import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -140,7 +143,7 @@
         final IBinder sensorToken =
                 new Binder("android.hardware.sensor.VirtualSensor:" + config.getName());
         VirtualSensor sensor = new VirtualSensor(handle, config.getType(), config.getName(),
-                virtualDevice, sensorToken);
+                config.getFlags(), virtualDevice, sensorToken);
         synchronized (mLock) {
             mSensorDescriptors.put(sensorToken, sensorDescriptor);
             mVirtualSensors.put(handle, sensor);
@@ -164,6 +167,37 @@
         }
     }
 
+    boolean sendSensorAdditionalInfo(@NonNull IBinder token,
+            @NonNull VirtualSensorAdditionalInfo info) {
+        Objects.requireNonNull(token);
+        Objects.requireNonNull(info);
+        synchronized (mLock) {
+            final SensorDescriptor sensorDescriptor = mSensorDescriptors.get(token);
+            long timestamp = SystemClock.elapsedRealtimeNanos();
+            if (sensorDescriptor == null) {
+                throw new IllegalArgumentException("Could not send sensor event for given token");
+            }
+            if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+                    sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_BEGIN,
+                    /* serial= */ 0, timestamp++, /* values= */ null)) {
+                return false;
+            }
+            for (int i = 0; i < info.getValues().size(); ++i) {
+                if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+                        sensorDescriptor.getHandle(), info.getType(), /* serial= */ i,
+                        timestamp++, info.getValues().get(i))) {
+                    return false;
+                }
+            }
+            if (!mSensorManagerInternal.sendSensorAdditionalInfo(
+                    sensorDescriptor.getHandle(), SensorAdditionalInfo.TYPE_FRAME_END,
+                    /* serial= */ 0, timestamp, /* values= */ null)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     @Nullable
     VirtualSensor getSensorByHandle(int handle) {
         synchronized (mLock) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 28efdfc..0023b6d 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -55,6 +55,7 @@
 import android.companion.virtual.audio.IAudioRoutingCallback;
 import android.companion.virtual.camera.VirtualCameraConfig;
 import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.companion.virtualdevice.flags.Flags;
 import android.compat.annotation.ChangeId;
@@ -1294,6 +1295,18 @@
     }
 
     @Override // Binder call
+    public boolean sendSensorAdditionalInfo(@NonNull IBinder token,
+            @NonNull VirtualSensorAdditionalInfo info) {
+        checkCallerIsDeviceOwner();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return mSensorController.sendSensorAdditionalInfo(token, info);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override // Binder call
     public void registerIntentInterceptor(IVirtualDeviceIntentInterceptor intentInterceptor,
             IntentFilter filter) {
         checkCallerIsDeviceOwner();
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 93b4de8..caf535c 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -142,14 +142,6 @@
     @GuardedBy("mVirtualDeviceManagerLock")
     private ArrayMap<String, AssociationInfo> mActiveAssociations = new ArrayMap<>();
 
-    private final CompanionDeviceManager.OnAssociationsChangedListener mCdmAssociationListener =
-            new CompanionDeviceManager.OnAssociationsChangedListener() {
-                @Override
-                public void onAssociationsChanged(@NonNull List<AssociationInfo> associations) {
-                    syncVirtualDevicesToCdmAssociations(associations);
-                }
-            };
-
     private class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
         final Set<Integer> mUsersInLockdown = new ArraySet<>();
 
@@ -348,33 +340,6 @@
         return true;
     }
 
-    private void syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations) {
-        Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>();
-        synchronized (mVirtualDeviceManagerLock) {
-            if (mVirtualDevices.size() == 0) {
-                return;
-            }
-
-            Set<Integer> activeAssociationIds = new HashSet<>(associations.size());
-            for (AssociationInfo association : associations) {
-                activeAssociationIds.add(association.getId());
-            }
-
-            for (int i = 0; i < mVirtualDevices.size(); i++) {
-                VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
-                int deviceAssociationId = virtualDevice.getAssociationId();
-                if (deviceAssociationId != CDM_ASSOCIATION_ID_NONE
-                        && !activeAssociationIds.contains(deviceAssociationId)) {
-                    virtualDevicesToRemove.add(virtualDevice);
-                }
-            }
-        }
-
-        for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) {
-            virtualDevice.close();
-        }
-    }
-
     void onCdmAssociationsChanged(List<AssociationInfo> associations) {
         ArrayMap<String, AssociationInfo> vdmAssociations = new ArrayMap<>();
         for (int i = 0; i < associations.size(); ++i) {
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index 7eb7072..a1d39fa 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -313,7 +313,8 @@
             android.Manifest.permission.CREATE_USERS,
             android.Manifest.permission.QUERY_USERS
     })
-    private Intent getContextualSearchIntent(int entrypoint, int userId, CallbackToken mToken) {
+    private Intent getContextualSearchIntent(int entrypoint, int userId, String callingPackage,
+            CallbackToken mToken) {
         final Intent launchIntent = getResolvedLaunchIntent(userId);
         if (launchIntent == null) {
             if (DEBUG) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null");
@@ -332,6 +333,9 @@
             launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_AUDIO_PLAYING,
                     mAudioManager.isMusicActive());
         }
+        if (Flags.selfInvocation()) {
+            launchIntent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
+        }
         boolean isAssistDataAllowed = mAtmInternal.isAssistDataAllowed();
         final List<ActivityAssistInfo> records = mAtmInternal.getTopVisibleActivities();
         final List<IBinder> activityTokens = new ArrayList<>(records.size());
@@ -500,6 +504,7 @@
         }
 
         private void startContextualSearchInternal(int entrypoint) {
+            final String callingPackage = mPackageManager.getNameForUid(Binder.getCallingUid());
             final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
             mAssistDataRequester.cancel();
             // Creates a new CallbackToken at mToken and an expiration handler.
@@ -508,7 +513,8 @@
             // server has READ_FRAME_BUFFER permission to get the screenshot and because only
             // the system server can invoke non-exported activities.
             Binder.withCleanCallingIdentity(() -> {
-                Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId, mToken);
+                Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId,
+                            callingPackage, mToken);
                 if (launchIntent != null) {
                     int result = invokeContextualSearchIntent(launchIntent, callingUserId);
                     if (DEBUG) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 14d9d3f..decac40 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -122,10 +122,10 @@
 }
 
 genrule {
-  name: "statslog-mediarouter-java-gen",
-  tools: ["stats-log-api-gen"],
-  cmd: "$(location stats-log-api-gen) --java $(out) --module mediarouter --javaPackage com.android.server.media --javaClass MediaRouterStatsLog",
-  out: ["com/android/server/media/MediaRouterStatsLog.java"],
+    name: "statslog-mediarouter-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module mediarouter --javaPackage com.android.server.media --javaClass MediaRouterStatsLog",
+    out: ["com/android/server/media/MediaRouterStatsLog.java"],
 }
 
 java_library_static {
@@ -138,6 +138,7 @@
         "ondeviceintelligence_conditionally",
     ],
     srcs: [
+        ":android.hardware.audio.effect-V1-java-source",
         ":android.hardware.tv.hdmi.connection-V1-java-source",
         ":android.hardware.tv.hdmi.earc-V1-java-source",
         ":android.hardware.tv.mediaquality-V1-java-source",
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b75b7ddf..76dd6a5 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2816,10 +2816,11 @@
         if (!checkNotifyPermission("notifyEmergencyNumberList()")) {
             return;
         }
-        if (!mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_TELEPHONY_CALLING)) {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+                && !mContext.getPackageManager()
+                        .hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
             // TelephonyManager.getEmergencyNumberList() throws an exception if
-            // FEATURE_TELEPHONY_CALLING is not defined.
+            // FEATURE_TELEPHONY_CALLING or FEATURE_TELEPHONY_MESSAGING is not defined.
             return;
         }
 
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 96b30d4..d60c6c5 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -165,7 +165,6 @@
             "android.hardware.sensors@1.0::ISensors",
             "android.hardware.sensors@2.0::ISensors",
             "android.hardware.sensors@2.1::ISensors",
-            "android.hardware.vibrator@1.0::IVibrator",
             "android.hardware.vr@1.0::IVr",
             "android.system.suspend@1.0::ISystemSuspend"
     );
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index a73a991..658ea4c 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -130,8 +130,6 @@
  */
 public class AdbDebuggingManager {
     private static final String TAG = AdbDebuggingManager.class.getSimpleName();
-    private static final boolean DEBUG = false;
-    private static final boolean MDNS_DEBUG = false;
 
     private static final String ADBD_SOCKET = "adbd";
     private static final String ADB_DIRECTORY = "misc/adb";
@@ -156,8 +154,6 @@
     @Nullable private final File mUserKeyFile;
     @Nullable private final File mTempKeysFile;
 
-    private static final String WIFI_PERSISTENT_CONFIG_PROPERTY =
-            "persist.adb.tls_server.enable";
     private static final String WIFI_PERSISTENT_GUID =
             "persist.adb.wifi.guid";
     private static final int PAIRING_CODE_LENGTH = 6;
@@ -261,12 +257,10 @@
             mHandler.sendMessage(msg);
 
             boolean paired = native_pairing_wait();
-            if (DEBUG) {
-                if (mPublicKey != null) {
-                    Slog.i(TAG, "Pairing succeeded key=" + mPublicKey);
-                } else {
-                    Slog.i(TAG, "Pairing failed");
-                }
+            if (mPublicKey != null) {
+                Slog.i(TAG, "Pairing succeeded key=" + mPublicKey);
+            } else {
+                Slog.i(TAG, "Pairing failed");
             }
 
             mNsdManager.unregisterService(this);
@@ -307,7 +301,7 @@
 
         @Override
         public void onServiceRegistered(NsdServiceInfo serviceInfo) {
-            if (MDNS_DEBUG) Slog.i(TAG, "Registered pairing service: " + serviceInfo);
+            Slog.i(TAG, "Registered pairing service: " + serviceInfo);
         }
 
         @Override
@@ -319,7 +313,7 @@
 
         @Override
         public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
-            if (MDNS_DEBUG) Slog.i(TAG, "Unregistered pairing service: " + serviceInfo);
+            Slog.i(TAG, "Unregistered pairing service: " + serviceInfo);
         }
 
         @Override
@@ -354,7 +348,7 @@
 
         @Override
         public void run() {
-            if (DEBUG) Slog.d(TAG, "Starting adb port property poller");
+            Slog.d(TAG, "Starting adb port property poller");
             // Once adbwifi is enabled, we poll the service.adb.tls.port
             // system property until we get the port, or -1 on failure.
             // Let's also limit the polling to 10 seconds, just in case
@@ -390,7 +384,7 @@
 
     class PortListenerImpl implements AdbConnectionPortListener {
         public void onPortReceived(int port) {
-            if (DEBUG) Slog.d(TAG, "Received tls port=" + port);
+            Slog.d(TAG, "Received tls port=" + port);
             Message msg = mHandler.obtainMessage(port > 0
                      ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
                      : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
@@ -419,11 +413,11 @@
 
         @Override
         public void run() {
-            if (DEBUG) Slog.d(TAG, "Entering thread");
+            Slog.d(TAG, "Entering thread");
             while (true) {
                 synchronized (this) {
                     if (mStopped) {
-                        if (DEBUG) Slog.d(TAG, "Exiting thread");
+                        Slog.d(TAG, "Exiting thread");
                         return;
                     }
                     try {
@@ -448,7 +442,7 @@
                         LocalSocketAddress.Namespace.RESERVED);
                 mInputStream = null;
 
-                if (DEBUG) Slog.d(TAG, "Creating socket");
+                Slog.d(TAG, "Creating socket");
                 mSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
                 mSocket.connect(address);
 
@@ -549,7 +543,7 @@
         }
 
         private void closeSocketLocked() {
-            if (DEBUG) Slog.d(TAG, "Closing socket");
+            Slog.d(TAG, "Closing socket");
             try {
                 if (mOutputStream != null) {
                     mOutputStream.close();
@@ -859,7 +853,7 @@
 
         private void startAdbDebuggingThread() {
             ++mAdbEnabledRefCount;
-            if (DEBUG) Slog.i(TAG, "startAdbDebuggingThread ref=" + mAdbEnabledRefCount);
+            Slog.i(TAG, "startAdbDebuggingThread ref=" + mAdbEnabledRefCount);
             if (mAdbEnabledRefCount > 1) {
                 return;
             }
@@ -875,7 +869,7 @@
 
         private void stopAdbDebuggingThread() {
             --mAdbEnabledRefCount;
-            if (DEBUG) Slog.i(TAG, "stopAdbDebuggingThread ref=" + mAdbEnabledRefCount);
+            Slog.i(TAG, "stopAdbDebuggingThread ref=" + mAdbEnabledRefCount);
             if (mAdbEnabledRefCount > 0) {
                 return;
             }
@@ -1093,7 +1087,7 @@
                     intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
                     mContext.registerReceiver(mBroadcastReceiver, intentFilter);
 
-                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    SystemProperties.set(AdbService.WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
                     mConnectionPortPoller =
                             new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
                     mConnectionPortPoller.start();
@@ -1101,7 +1095,7 @@
                     startAdbDebuggingThread();
                     mAdbWifiEnabled = true;
 
-                    if (DEBUG) Slog.i(TAG, "adb start wireless adb");
+                    Slog.i(TAG, "adb start wireless adb");
                     break;
                 }
                 case MSG_ADBDWIFI_DISABLE:
@@ -1143,7 +1137,7 @@
                     intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
                     mContext.registerReceiver(mBroadcastReceiver, intentFilter);
 
-                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    SystemProperties.set(AdbService.WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
                     mConnectionPortPoller =
                             new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
                     mConnectionPortPoller.start();
@@ -1151,7 +1145,7 @@
                     startAdbDebuggingThread();
                     mAdbWifiEnabled = true;
 
-                    if (DEBUG) Slog.i(TAG, "adb start wireless adb");
+                    Slog.i(TAG, "adb start wireless adb");
                     break;
                 case MSG_ADBWIFI_DENY:
                     Settings.Global.putInt(mContentResolver,
@@ -1259,7 +1253,7 @@
                     break;
                 }
                 case MSG_ADBD_SOCKET_CONNECTED: {
-                    if (DEBUG) Slog.d(TAG, "adbd socket connected");
+                    Slog.d(TAG, "adbd socket connected");
                     if (mAdbWifiEnabled) {
                         // In scenarios where adbd is restarted, the tls port may change.
                         mConnectionPortPoller =
@@ -1269,7 +1263,7 @@
                     break;
                 }
                 case MSG_ADBD_SOCKET_DISCONNECTED: {
-                    if (DEBUG) Slog.d(TAG, "adbd socket disconnected");
+                    Slog.d(TAG, "adbd socket disconnected");
                     if (mConnectionPortPoller != null) {
                         mConnectionPortPoller.cancelAndWait();
                         mConnectionPortPoller = null;
@@ -1477,7 +1471,7 @@
         }
 
         private void updateUIPairCode(String code) {
-            if (DEBUG) Slog.i(TAG, "updateUIPairCode: " + code);
+            Slog.i(TAG, "updateUIPairCode: " + code);
 
             Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
             intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code);
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 55d8dba..40f7c87 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -143,19 +143,16 @@
 
         @Override
         public File getAdbKeysFile() {
-            return mDebuggingManager == null ? null : mDebuggingManager.getUserKeyFile();
+            return mDebuggingManager.getUserKeyFile();
         }
 
         @Override
         public File getAdbTempKeysFile() {
-            return mDebuggingManager == null ? null : mDebuggingManager.getAdbTempKeysFile();
+            return mDebuggingManager.getAdbTempKeysFile();
         }
 
         @Override
         public void notifyKeyFilesUpdated() {
-            if (mDebuggingManager == null) {
-                return;
-            }
             mDebuggingManager.notifyKeyFilesUpdated();
         }
 
@@ -222,15 +219,14 @@
         }
     }
 
-    private static final String TAG = "AdbService";
-    private static final boolean DEBUG = false;
+    private static final String TAG = AdbService.class.getSimpleName();
 
     /**
      * The persistent property which stores whether adb is enabled or not.
      * May also contain vendor-specific default functions for testing purposes.
      */
     private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
-    private static final String WIFI_PERSISTENT_CONFIG_PROPERTY = "persist.adb.tls_server.enable";
+    static final String WIFI_PERSISTENT_CONFIG_PROPERTY = "persist.adb.tls_server.enable";
 
     private final Context mContext;
     private final ContentResolver mContentResolver;
@@ -238,7 +234,7 @@
 
     private boolean mIsAdbUsbEnabled;
     private boolean mIsAdbWifiEnabled;
-    private AdbDebuggingManager mDebuggingManager;
+    private final AdbDebuggingManager mDebuggingManager;
 
     private ContentObserver mObserver;
 
@@ -256,7 +252,7 @@
      * SystemServer}.
      */
     public void systemReady() {
-        if (DEBUG) Slog.d(TAG, "systemReady");
+        Slog.d(TAG, "systemReady");
 
         /*
          * Use the normal bootmode persistent prop to maintain state of adb across
@@ -287,39 +283,28 @@
      * Called in response to {@code SystemService.PHASE_BOOT_COMPLETED} from {@code SystemServer}.
      */
     public void bootCompleted() {
-        if (DEBUG) Slog.d(TAG, "boot completed");
-        if (mDebuggingManager != null) {
-            mDebuggingManager.setAdbEnabled(mIsAdbUsbEnabled, AdbTransportType.USB);
-            mDebuggingManager.setAdbEnabled(mIsAdbWifiEnabled, AdbTransportType.WIFI);
-        }
+        Slog.d(TAG, "boot completed");
+        mDebuggingManager.setAdbEnabled(mIsAdbUsbEnabled, AdbTransportType.USB);
+        mDebuggingManager.setAdbEnabled(mIsAdbWifiEnabled, AdbTransportType.WIFI);
     }
 
     @Override
     public void allowDebugging(boolean alwaysAllow, @NonNull String publicKey) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
         Preconditions.checkStringNotEmpty(publicKey);
-        if (mDebuggingManager != null) {
-            mDebuggingManager.allowDebugging(alwaysAllow, publicKey);
-        }
+        mDebuggingManager.allowDebugging(alwaysAllow, publicKey);
     }
 
     @Override
     public void denyDebugging() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        if (mDebuggingManager != null) {
-            mDebuggingManager.denyDebugging();
-        }
+        mDebuggingManager.denyDebugging();
     }
 
     @Override
     public void clearDebuggingKeys() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        if (mDebuggingManager != null) {
-            mDebuggingManager.clearDebuggingKeys();
-        } else {
-            throw new RuntimeException("Cannot clear ADB debugging keys, "
-                    + "AdbDebuggingManager not enabled");
-        }
+        mDebuggingManager.clearDebuggingKeys();
     }
 
     /**
@@ -351,25 +336,18 @@
     public void allowWirelessDebugging(boolean alwaysAllow, @NonNull String bssid) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
         Preconditions.checkStringNotEmpty(bssid);
-        if (mDebuggingManager != null) {
-            mDebuggingManager.allowWirelessDebugging(alwaysAllow, bssid);
-        }
+        mDebuggingManager.allowWirelessDebugging(alwaysAllow, bssid);
     }
 
     @Override
     public void denyWirelessDebugging() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        if (mDebuggingManager != null) {
-            mDebuggingManager.denyWirelessDebugging();
-        }
+        mDebuggingManager.denyWirelessDebugging();
     }
 
     @Override
     public FingerprintAndPairDevice[] getPairedDevices() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        if (mDebuggingManager == null) {
-            return null;
-        }
         Map<String, PairDevice> map = mDebuggingManager.getPairedDevices();
         FingerprintAndPairDevice[] ret = new FingerprintAndPairDevice[map.size()];
         int i = 0;
@@ -386,17 +364,13 @@
     public void unpairDevice(@NonNull String fingerprint) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
         Preconditions.checkStringNotEmpty(fingerprint);
-        if (mDebuggingManager != null) {
-            mDebuggingManager.unpairDevice(fingerprint);
-        }
+        mDebuggingManager.unpairDevice(fingerprint);
     }
 
     @Override
     public void enablePairingByPairingCode() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        if (mDebuggingManager != null) {
-            mDebuggingManager.enablePairingByPairingCode();
-        }
+        mDebuggingManager.enablePairingByPairingCode();
     }
 
     @Override
@@ -404,42 +378,30 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
         Preconditions.checkStringNotEmpty(serviceName);
         Preconditions.checkStringNotEmpty(password);
-        if (mDebuggingManager != null) {
-            mDebuggingManager.enablePairingByQrCode(serviceName, password);
-        }
+        mDebuggingManager.enablePairingByQrCode(serviceName, password);
     }
 
     @Override
     public void disablePairing() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        if (mDebuggingManager != null) {
-            mDebuggingManager.disablePairing();
-        }
+        mDebuggingManager.disablePairing();
     }
 
     @Override
     public int getAdbWirelessPort() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        if (mDebuggingManager != null) {
-            return mDebuggingManager.getAdbWirelessPort();
-        }
-        // If ro.adb.secure=0
-        return mConnectionPort.get();
+        return mDebuggingManager.getAdbWirelessPort();
     }
 
     @Override
     public void registerCallback(IAdbCallback callback) throws RemoteException {
-        if (DEBUG) {
-            Slog.d(TAG, "Registering callback " + callback);
-        }
+        Slog.d(TAG, "Registering callback " + callback);
         mCallbacks.register(callback);
     }
 
     @Override
     public void unregisterCallback(IAdbCallback callback) throws RemoteException {
-        if (DEBUG) {
-            Slog.d(TAG, "Unregistering callback " + callback);
-        }
+        Slog.d(TAG, "Unregistering callback " + callback);
         mCallbacks.unregister(callback);
     }
     /**
@@ -500,18 +462,15 @@
     }
 
     private void setAdbEnabled(boolean enable, byte transportType) {
-        if (DEBUG) {
-            Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled
-                    + ", mIsAdbWifiEnabled=" + mIsAdbWifiEnabled + ", transportType="
-                        + transportType);
-        }
+        Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled
+                 + ", mIsAdbWifiEnabled=" + mIsAdbWifiEnabled + ", transportType=" + transportType);
 
         if (transportType == AdbTransportType.USB && enable != mIsAdbUsbEnabled) {
             mIsAdbUsbEnabled = enable;
         } else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) {
             mIsAdbWifiEnabled = enable;
             if (mIsAdbWifiEnabled) {
-                if (!AdbProperties.secure().orElse(false) && mDebuggingManager == null) {
+                if (!AdbProperties.secure().orElse(false)) {
                     // Start adbd. If this is secure adb, then we defer enabling adb over WiFi.
                     SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
                     mConnectionPortPoller =
@@ -545,24 +504,16 @@
             }
         }
 
-        if (mDebuggingManager != null) {
-            mDebuggingManager.setAdbEnabled(enable, transportType);
-        }
+        mDebuggingManager.setAdbEnabled(enable, transportType);
 
-        if (DEBUG) {
-            Slog.d(TAG, "Broadcasting enable = " + enable + ", type = " + transportType);
-        }
+        Slog.d(TAG, "Broadcasting enable = " + enable + ", type = " + transportType);
         mCallbacks.broadcast((callback) -> {
-            if (DEBUG) {
-                Slog.d(TAG, "Sending enable = " + enable + ", type = " + transportType
-                        + " to " + callback);
-            }
+            Slog.d(TAG, "Sending enable = " + enable + ", type = " + transportType + " to "
+                    + callback);
             try {
                 callback.onDebuggingChanged(enable, transportType);
             } catch (RemoteException ex) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Unable to send onDebuggingChanged:", ex);
-                }
+                Slog.w(TAG, "Unable to send onDebuggingChanged:", ex);
             }
         });
     }
@@ -600,11 +551,8 @@
                     dump = new DualDumpOutputStream(new IndentingPrintWriter(pw, "  "));
                 }
 
-                if (mDebuggingManager != null) {
-                    mDebuggingManager.dump(dump, "debugging_manager",
-                            AdbServiceDumpProto.DEBUGGING_MANAGER);
-                }
-
+                mDebuggingManager.dump(dump, "debugging_manager",
+                        AdbServiceDumpProto.DEBUGGING_MANAGER);
                 dump.flush();
             } else {
                 pw.println("Dump current ADB state");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b0b34d0..59c5e0e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -137,6 +137,7 @@
 import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static com.android.internal.util.FrameworkStatsLog.EXTRA_INTENT_KEYS_COLLECTED_ON_SERVER;
 import static com.android.internal.util.FrameworkStatsLog.INTENT_CREATOR_TOKEN_ADDED;
 import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED;
 import static com.android.sdksandbox.flags.Flags.sdkSandboxInstrumentationInfo;
@@ -2004,7 +2005,7 @@
                 new IAppOpsCallback.Stub() {
                     @Override public void opChanged(int op, int uid, String packageName,
                             String persistentDeviceId) {
-                        if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
+                        if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && uid >= 0) {
                             if (getAppOpsManager().checkOpNoThrow(op, uid, packageName)
                                     != AppOpsManager.MODE_ALLOWED) {
                                 runInBackgroundDisabled(uid);
@@ -16190,14 +16191,16 @@
         return mUserController.switchUser(targetUserId);
     }
 
+    @Nullable
     @Override
-    public String getSwitchingFromUserMessage() {
-        return mUserController.getSwitchingFromSystemUserMessage();
+    public String getSwitchingFromUserMessage(@UserIdInt int userId) {
+        return mUserController.getSwitchingFromUserMessage(userId);
     }
 
+    @Nullable
     @Override
-    public String getSwitchingToUserMessage() {
-        return mUserController.getSwitchingToSystemUserMessage();
+    public String getSwitchingToUserMessage(@UserIdInt int userId) {
+        return mUserController.getSwitchingToUserMessage(userId);
     }
 
     @Override
@@ -16938,13 +16941,13 @@
         }
 
         @Override
-        public void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage) {
-            mUserController.setSwitchingFromSystemUserMessage(switchingFromSystemUserMessage);
+        public void setSwitchingFromUserMessage(@UserIdInt int userId, @Nullable String message) {
+            mUserController.setSwitchingFromUserMessage(userId, message);
         }
 
         @Override
-        public void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) {
-            mUserController.setSwitchingToSystemUserMessage(switchingToSystemUserMessage);
+        public void setSwitchingToUserMessage(@UserIdInt int userId, @Nullable String message) {
+            mUserController.setSwitchingToUserMessage(userId, message);
         }
 
         @Override
@@ -19413,12 +19416,14 @@
         if (!preventIntentRedirect()) return;
         if (intent == null) return;
 
+        int callingUid = Binder.getCallingUid();
         if (((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0)
                 && intent.getExtras() != null && intent.getExtras().hasIntent()) {
             Slog.wtf(TAG,
                     "[IntentRedirect Hardening] The intent does not have its nested keys collected as a "
                             + "preparation for creating intent creator tokens. Intent: "
                             + intent + "; creatorPackage: " + creatorPackage);
+            FrameworkStatsLog.write(EXTRA_INTENT_KEYS_COLLECTED_ON_SERVER, callingUid);
             if (preventIntentRedirectShowToastIfNestedKeysNotCollectedRW()) {
                 UiThread.getHandler().post(
                         () -> Toast.makeText(mContext,
@@ -19445,7 +19450,7 @@
                 targetPackage);
         final boolean noExtraIntentKeys =
                 intent.getExtraIntentKeys() == null || intent.getExtraIntentKeys().isEmpty();
-        final int creatorUid = noExtraIntentKeys ? DEFAULT_INTENT_CREATOR_UID : Binder.getCallingUid();
+        final int creatorUid = noExtraIntentKeys ? DEFAULT_INTENT_CREATOR_UID : callingUid;
 
         intent.forEachNestedCreatorToken(extraIntent -> {
             if (isCreatorSameAsTarget) {
diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java
index a47beae..800e2b2 100644
--- a/services/core/java/com/android/server/am/AppPermissionTracker.java
+++ b/services/core/java/com/android/server/am/AppPermissionTracker.java
@@ -394,6 +394,7 @@
     private class MyAppOpsCallback extends IAppOpsCallback.Stub {
         @Override
         public void opChanged(int op, int uid, String packageName, String persistentDeviceId) {
+            if (uid < 0) return;
             mHandler.obtainMessage(MyHandler.MSG_APPOPS_CHANGED, op, uid, packageName)
                     .sendToTarget();
         }
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 81d34f6..fe4fa10 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -41,6 +41,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Tunable parameters for broadcast dispatch policy
@@ -286,6 +287,17 @@
             "max_frozen_outgoing_broadcasts";
     private static final int DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS = 32;
 
+    /**
+     * For {@link BroadcastQueueImpl}: Indicates how long after a process start was initiated,
+     * it should be considered abandoned and discarded.
+     */
+    public long PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS =
+            DEFAULT_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS * Build.HW_TIMEOUT_MULTIPLIER;
+    private static final String KEY_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS =
+            "pending_cold_start_abandon_timeout_millis";
+    private static final long DEFAULT_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS =
+            TimeUnit.MINUTES.toMillis(5);
+
     // Settings override tracking for this instance
     private String mSettingsKey;
     private SettingsObserver mSettingsObserver;
@@ -434,6 +446,10 @@
             MAX_FROZEN_OUTGOING_BROADCASTS = getDeviceConfigInt(
                     KEY_MAX_FROZEN_OUTGOING_BROADCASTS,
                     DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS);
+            PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS = getDeviceConfigLong(
+                    KEY_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS,
+                    DEFAULT_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS)
+                            * Build.HW_TIMEOUT_MULTIPLIER;
         }
 
         // TODO: migrate BroadcastRecord to accept a BroadcastConstants
@@ -491,6 +507,8 @@
                     PENDING_COLD_START_CHECK_INTERVAL_MILLIS).println();
             pw.print(KEY_MAX_FROZEN_OUTGOING_BROADCASTS,
                     MAX_FROZEN_OUTGOING_BROADCASTS).println();
+            pw.print(KEY_PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS,
+                    PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS).println();
             pw.decreaseIndent();
             pw.println();
         }
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index db0562f..c0fe738 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -245,6 +245,24 @@
      */
     private final ArrayList<BroadcastRecord> mOutgoingBroadcasts = new ArrayList<>();
 
+    /**
+     * The timestamp, in {@link SystemClock#uptimeMillis()}, at which a cold start was initiated
+     * for the process associated with this queue.
+     *
+     * Note: We could use the already existing {@link ProcessRecord#getStartUptime()} instead
+     * of this, but the need for this timestamp is to identify an issue (b/393898613) where the
+     * suspicion is that process is not attached or getting changed. So, we don't want to rely on
+     * ProcessRecord directly for this purpose.
+     */
+    private long mProcessStartInitiatedTimestampMillis;
+
+    /**
+     * Indicates whether the number of current receivers has been incremented using
+     * {@link ProcessReceiverRecord#incrementCurReceivers()}. This allows to skip decrementing
+     * the receivers when it is not required.
+     */
+    private boolean mCurReceiversIncremented;
+
     public BroadcastProcessQueue(@NonNull BroadcastConstants constants,
             @NonNull String processName, int uid) {
         this.constants = Objects.requireNonNull(constants);
@@ -652,6 +670,52 @@
         return mActiveFirstLaunch;
     }
 
+    public void incrementCurAppReceivers() {
+        app.mReceivers.incrementCurReceivers();
+        mCurReceiversIncremented = true;
+    }
+
+    public void decrementCurAppReceivers() {
+        if (mCurReceiversIncremented) {
+            app.mReceivers.decrementCurReceivers();
+            mCurReceiversIncremented = false;
+        }
+    }
+
+    public void setProcessStartInitiatedTimestampMillis(@UptimeMillisLong long timestampMillis) {
+        mProcessStartInitiatedTimestampMillis = timestampMillis;
+    }
+
+    @UptimeMillisLong
+    public long getProcessStartInitiatedTimestampMillis() {
+        return mProcessStartInitiatedTimestampMillis;
+    }
+
+    public boolean hasProcessStartInitiationTimedout() {
+        if (mProcessStartInitiatedTimestampMillis <= 0) {
+            return false;
+        }
+        return (SystemClock.uptimeMillis() - mProcessStartInitiatedTimestampMillis)
+                > constants.PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS;
+    }
+
+    /**
+     * Returns if the process start initiation is expected to be timed out at this point. This
+     * allows us to dump necessary state for debugging before the process start is timed out
+     * and discarded.
+     */
+    public boolean isProcessStartInitiationTimeoutExpected() {
+        if (mProcessStartInitiatedTimestampMillis <= 0) {
+            return false;
+        }
+        return (SystemClock.uptimeMillis() - mProcessStartInitiatedTimestampMillis)
+                > constants.PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS / 2;
+    }
+
+    public void clearProcessStartInitiatedTimestampMillis() {
+        mProcessStartInitiatedTimestampMillis = 0;
+    }
+
     /**
      * Get package name of the first application loaded into this process.
      */
@@ -810,7 +874,7 @@
      * Return the broadcast being actively dispatched in this process.
      */
     public @NonNull BroadcastRecord getActive() {
-        return Objects.requireNonNull(mActive);
+        return Objects.requireNonNull(mActive, toString());
     }
 
     /**
@@ -818,7 +882,7 @@
      * being actively dispatched in this process.
      */
     public int getActiveIndex() {
-        Objects.requireNonNull(mActive);
+        Objects.requireNonNull(mActive, toString());
         return mActiveIndex;
     }
 
@@ -1558,6 +1622,10 @@
         if (mActiveReEnqueued) {
             pw.print("activeReEnqueued:"); pw.println(mActiveReEnqueued);
         }
+        if (mProcessStartInitiatedTimestampMillis > 0) {
+            pw.print("processStartInitiatedTimestamp:"); pw.println(
+                    TimeUtils.formatUptime(mProcessStartInitiatedTimestampMillis));
+        }
     }
 
     @NeverCompile
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index c76a0d0..6e893ad 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -534,6 +534,7 @@
                 // skip to look for another warm process
                 if (mRunningColdStart == null) {
                     mRunningColdStart = queue;
+                    mRunningColdStart.clearProcessStartInitiatedTimestampMillis();
                 } else if (isPendingColdStartValid()) {
                     // Move to considering next runnable queue
                     queue = nextQueue;
@@ -542,6 +543,7 @@
                     // Pending cold start is not valid, so clear it and move on.
                     clearInvalidPendingColdStart();
                     mRunningColdStart = queue;
+                    mRunningColdStart.clearProcessStartInitiatedTimestampMillis();
                 }
             }
 
@@ -588,7 +590,9 @@
 
     @GuardedBy("mService")
     private boolean isPendingColdStartValid() {
-        if (mRunningColdStart.app.getPid() > 0) {
+        if (mRunningColdStart.hasProcessStartInitiationTimedout()) {
+            return false;
+        } else if (mRunningColdStart.app.getPid() > 0) {
             // If the process has already started, check if it wasn't killed.
             return !mRunningColdStart.app.isKilled();
         } else {
@@ -606,8 +610,9 @@
         } else {
             mRunningColdStart.reEnqueueActiveBroadcast();
         }
-        demoteFromRunningLocked(mRunningColdStart);
+        final BroadcastProcessQueue queue = mRunningColdStart;
         clearRunningColdStart();
+        demoteFromRunningLocked(queue);
         enqueueUpdateRunningList();
     }
 
@@ -672,6 +677,7 @@
         if ((mRunningColdStart != null) && (mRunningColdStart == queue)) {
             // We've been waiting for this app to cold start, and it's ready
             // now; dispatch its next broadcast and clear the slot
+            mRunningColdStart.clearProcessStartInitiatedTimestampMillis();
             mRunningColdStart = null;
 
             // Now that we're running warm, we can finally request that OOM
@@ -755,6 +761,7 @@
 
         // We've been waiting for this app to cold start, and it had
         // trouble; clear the slot and fail delivery below
+        mRunningColdStart.clearProcessStartInitiatedTimestampMillis();
         mRunningColdStart = null;
 
         // We might be willing to kick off another cold start
@@ -1035,6 +1042,7 @@
                     "startProcessLocked failed");
             return true;
         }
+        queue.setProcessStartInitiatedTimestampMillis(SystemClock.uptimeMillis());
         // TODO: b/335420031 - cache receiver intent to avoid multiple calls to getReceiverIntent.
         mService.mProcessList.getAppStartInfoTracker().handleProcessBroadcastStart(
                 startTimeNs, queue.app, r.getReceiverIntent(receiver), r.alarm /* isAlarm */);
@@ -1527,6 +1535,15 @@
 
         final int cookie = traceBegin("demoteFromRunning");
         // We've drained running broadcasts; maybe move back to runnable
+        if (mRunningColdStart == queue) {
+            // TODO: b/399020479 - Remove wtf log once we identify the case where mRunningColdStart
+            // is not getting cleared.
+            // If this queue is mRunningColdStart, then it should have been cleared before
+            // it is demoted. Log a wtf if this isn't the case.
+            Slog.wtf(TAG, "mRunningColdStart has not been cleared; mRunningColdStart.app: "
+                    + mRunningColdStart.app + " , queue.app: " + queue.app,
+                            new IllegalStateException());
+        }
         queue.makeActiveIdle();
         queue.traceProcessEnd();
 
@@ -1981,6 +1998,32 @@
         if (mRunningColdStart != null) {
             checkState(getRunningIndexOf(mRunningColdStart) >= 0,
                     "isOrphaned " + mRunningColdStart);
+
+            final BroadcastProcessQueue queue = getProcessQueue(mRunningColdStart.processName,
+                    mRunningColdStart.uid);
+            checkState(queue == mRunningColdStart, "Conflicting " + mRunningColdStart
+                    + " with queue " + queue
+                    + ";\n mRunningColdStart.app: " + mRunningColdStart.app.toDetailedString()
+                    + ";\n queue.app: " + queue.app.toDetailedString());
+
+            checkState(mRunningColdStart.app != null, "Empty cold start queue "
+                    + mRunningColdStart);
+
+            if (mRunningColdStart.isProcessStartInitiationTimeoutExpected()) {
+                final StringBuilder sb = new StringBuilder();
+                sb.append("Process start timeout expected for app ");
+                sb.append(mRunningColdStart.app);
+                sb.append(" in queue ");
+                sb.append(mRunningColdStart);
+                sb.append("; startUpTime: ");
+                final long startupTimeMs =
+                        mRunningColdStart.getProcessStartInitiatedTimestampMillis();
+                sb.append(startupTimeMs == 0 ? "<none>"
+                        : TimeUtils.formatDuration(startupTimeMs - SystemClock.uptimeMillis()));
+                sb.append(";\n app: ");
+                sb.append(mRunningColdStart.app.toDetailedString());
+                checkState(false, sb.toString());
+            }
         }
 
         // Verify health of all known process queues
@@ -2080,7 +2123,7 @@
     @GuardedBy("mService")
     private void notifyStartedRunning(@NonNull BroadcastProcessQueue queue) {
         if (queue.app != null) {
-            queue.app.mReceivers.incrementCurReceivers();
+            queue.incrementCurAppReceivers();
 
             // Don't bump its LRU position if it's in the background restricted.
             if (mService.mInternal.getRestrictionLevel(
@@ -2105,7 +2148,7 @@
     @GuardedBy("mService")
     private void notifyStoppedRunning(@NonNull BroadcastProcessQueue queue) {
         if (queue.app != null) {
-            queue.app.mReceivers.decrementCurReceivers();
+            queue.decrementCurAppReceivers();
 
             if (queue.runningOomAdjusted) {
                 mService.enqueueOomAdjTargetLocked(queue.app);
@@ -2332,12 +2375,6 @@
 
     @VisibleForTesting
     @GuardedBy("mService")
-    @Nullable BroadcastProcessQueue removeProcessQueue(@NonNull ProcessRecord app) {
-        return removeProcessQueue(app.processName, app.info.uid);
-    }
-
-    @VisibleForTesting
-    @GuardedBy("mService")
     @Nullable BroadcastProcessQueue removeProcessQueue(@NonNull String processName,
             int uid) {
         BroadcastProcessQueue prev = null;
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 31704c4..4e1d77c 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -142,6 +142,10 @@
                 | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS);
     }
 
+    @Override
+    public boolean transmitsCpuTime() {
+        return !hasFlag(Context.BIND_ALLOW_FREEZE);
+    }
 
     public long getFlags() {
         return flags;
@@ -273,6 +277,9 @@
         if (hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
             sb.append("CAPS ");
         }
+        if (hasFlag(Context.BIND_ALLOW_FREEZE)) {
+            sb.append("!CPU ");
+        }
         if (serviceDead) {
             sb.append("DEAD ");
         }
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 4b6d6bc..fd2d835 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -61,7 +61,7 @@
 per-file ProcessStateController.java = file:/OOM_ADJUSTER_OWNERS
 
 # Miscellaneous
-per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com
 per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
 
 # Activity Security
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 336a35e..115ae41 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2643,7 +2643,7 @@
         }
 
         capability |= getDefaultCapability(app, procState);
-        capability |= getCpuCapability(app, now);
+        capability |= getCpuCapability(app, now, foregroundActivities);
 
         // Procstates below BFGS should never have this capability.
         if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
@@ -2802,7 +2802,7 @@
         // we check the final procstate, and remove it if the procsate is below BFGS.
         capability |= getBfslCapabilityFromClient(client);
 
-        capability |= getCpuCapabilityFromClient(client);
+        capability |= getCpuCapabilityFromClient(cr, client);
 
         if (cr.notHasFlag(Context.BIND_WAIVE_PRIORITY)) {
             if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
@@ -3259,7 +3259,7 @@
         // we check the final procstate, and remove it if the procsate is below BFGS.
         capability |= getBfslCapabilityFromClient(client);
 
-        capability |= getCpuCapabilityFromClient(client);
+        capability |= getCpuCapabilityFromClient(conn, client);
 
         if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
             // If the other app is cached for any reason, for purposes here
@@ -3422,15 +3422,18 @@
         return baseCapabilities | networkCapabilities;
     }
 
-    private static int getCpuCapability(ProcessRecord app, long nowUptime) {
+    private static int getCpuCapability(ProcessRecord app, long nowUptime,
+            boolean hasForegroundActivities) {
         // Note: persistent processes get all capabilities, including CPU_TIME.
         final UidRecord uidRec = app.getUidRecord();
         if (uidRec != null && uidRec.isCurAllowListed()) {
             // Process is in the power allowlist.
             return PROCESS_CAPABILITY_CPU_TIME;
         }
-        if (app.mState.getCachedHasVisibleActivities()) {
-            // Process has user visible activities.
+        if (hasForegroundActivities) {
+            // TODO: b/402987519 - This grants the Top Sleeping process CPU_TIME but eventually
+            //  should not.
+            // Process has user perceptible activities.
             return PROCESS_CAPABILITY_CPU_TIME;
         }
         if (Flags.prototypeAggressiveFreezing()) {
@@ -3502,10 +3505,13 @@
     /**
      * @return the CPU capability from a client (of a service binding or provider).
      */
-    private static int getCpuCapabilityFromClient(ProcessRecord client) {
-        // Just grant CPU capability every time
-        // TODO(b/370817323): Populate with reasons to not propagate cpu capability across bindings.
-        return client.mState.getCurCapability() & PROCESS_CAPABILITY_CPU_TIME;
+    private static int getCpuCapabilityFromClient(OomAdjusterModernImpl.Connection conn,
+            ProcessRecord client) {
+        if (conn == null || conn.transmitsCpuTime()) {
+            return client.mState.getCurCapability() & PROCESS_CAPABILITY_CPU_TIME;
+        } else {
+            return 0;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index 1b7e8f0..7e7b568 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -635,6 +635,15 @@
          * Returns true if this connection can propagate capabilities.
          */
         boolean canAffectCapabilities();
+
+        /**
+         * Returns whether this connection transmits PROCESS_CAPABILITY_CPU_TIME to the host, if the
+         * client possesses it.
+         */
+        default boolean transmitsCpuTime() {
+            // Always lend this capability by default.
+            return true;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index eea667e..400c699 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -70,6 +70,7 @@
 import com.android.server.wm.WindowProcessListener;
 
 import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.Arrays;
 import java.util.List;
 import java.util.function.Consumer;
@@ -1414,6 +1415,16 @@
         return mStringName = sb.toString();
     }
 
+    String toDetailedString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(this);
+        final StringWriter sw = new StringWriter();
+        final PrintWriter pw = new PrintWriter(sw);
+        dump(pw, "  ");
+        sb.append(sw);
+        return sb.toString();
+    }
+
     /*
      *  Return true if package has been added false if not
      */
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 3a041fd..cb4342f 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -187,10 +187,12 @@
         "crumpet",
         "dck_framework",
         "desktop_apps",
+        "desktop_audio",
         "desktop_better_together",
         "desktop_bsp",
         "desktop_camera",
         "desktop_connectivity",
+        "desktop_dev_experience",
         "desktop_display",
         "desktop_commercial",
         "desktop_firmware",
@@ -199,10 +201,14 @@
         "desktop_input",
         "desktop_kernel",
         "desktop_ml",
+        "desktop_networking",
         "desktop_serviceability",
         "desktop_oobe",
         "desktop_peripherals",
+        "desktop_personalization",
         "desktop_pnp",
+        "desktop_privacy",
+        "desktop_release",
         "desktop_security",
         "desktop_stats",
         "desktop_sysui",
@@ -247,11 +253,12 @@
         "pixel_state_server",
         "pixel_system_sw_video",
         "pixel_video_sw",
+        "pixel_vpn",
         "pixel_watch",
+        "pixel_watch_debug_trace",
         "pixel_wifi",
         "platform_compat",
         "platform_security",
-        "pixel_watch_debug_trace",
         "pmw",
         "power",
         "preload_safety",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 18f3500..40a9bbe 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -340,16 +340,16 @@
     private volatile ArraySet<String> mCurWaitingUserSwitchCallbacks;
 
     /**
-     * Messages for switching from {@link android.os.UserHandle#SYSTEM}.
+     * Message shown when switching from a user.
      */
     @GuardedBy("mLock")
-    private String mSwitchingFromSystemUserMessage;
+    private final SparseArray<String> mSwitchingFromUserMessage = new SparseArray<>();
 
     /**
-     * Messages for switching to {@link android.os.UserHandle#SYSTEM}.
+     * Message shown when switching to a user.
      */
     @GuardedBy("mLock")
-    private String mSwitchingToSystemUserMessage;
+    private final SparseArray<String> mSwitchingToUserMessage = new SparseArray<>();
 
     /**
      * Callbacks that are still active after {@link #getUserSwitchTimeoutMs}
@@ -2271,8 +2271,8 @@
     private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
         // The dialog will show and then initiate the user switch by calling startUserInForeground
         mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
-                getSwitchingFromSystemUserMessageUnchecked(),
-                getSwitchingToSystemUserMessageUnchecked(),
+                getSwitchingFromUserMessageUnchecked(fromToUserPair.first.id),
+                getSwitchingToUserMessageUnchecked(fromToUserPair.second.id),
                 /* onShown= */ () -> sendStartUserSwitchFgMessage(fromToUserPair.second.id));
     }
 
@@ -3388,41 +3388,45 @@
         return mLockPatternUtils.isLockScreenDisabled(userId);
     }
 
-    void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage) {
+    void setSwitchingFromUserMessage(@UserIdInt int user, @Nullable String message) {
         synchronized (mLock) {
-            mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage;
+            mSwitchingFromUserMessage.put(user, message);
         }
     }
 
-    void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) {
+    void setSwitchingToUserMessage(@UserIdInt int user, @Nullable String message) {
         synchronized (mLock) {
-            mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
+            mSwitchingToUserMessage.put(user, message);
         }
     }
 
     // Called by AMS, must check permission
-    String getSwitchingFromSystemUserMessage() {
-        checkHasManageUsersPermission("getSwitchingFromSystemUserMessage()");
+    @Nullable
+    String getSwitchingFromUserMessage(@UserIdInt int userId) {
+        checkHasManageUsersPermission("getSwitchingFromUserMessage()");
 
-        return getSwitchingFromSystemUserMessageUnchecked();
+        return getSwitchingFromUserMessageUnchecked(userId);
     }
 
     // Called by AMS, must check permission
-    String getSwitchingToSystemUserMessage() {
-        checkHasManageUsersPermission("getSwitchingToSystemUserMessage()");
+    @Nullable
+    String getSwitchingToUserMessage(@UserIdInt int userId) {
+        checkHasManageUsersPermission("getSwitchingToUserMessage()");
 
-        return getSwitchingToSystemUserMessageUnchecked();
+        return getSwitchingToUserMessageUnchecked(userId);
     }
 
-    private String getSwitchingFromSystemUserMessageUnchecked() {
+    @Nullable
+    private String getSwitchingFromUserMessageUnchecked(@UserIdInt int userId) {
         synchronized (mLock) {
-            return mSwitchingFromSystemUserMessage;
+            return mSwitchingFromUserMessage.get(userId);
         }
     }
 
-    private String getSwitchingToSystemUserMessageUnchecked() {
+    @Nullable
+    private String getSwitchingToUserMessageUnchecked(@UserIdInt int userId) {
         synchronized (mLock) {
-            return mSwitchingToSystemUserMessage;
+            return mSwitchingToUserMessage.get(userId);
         }
     }
 
@@ -3518,12 +3522,8 @@
                     + mIsBroadcastSentForSystemUserStarted);
             pw.println("  mIsBroadcastSentForSystemUserStarting:"
                     + mIsBroadcastSentForSystemUserStarting);
-            if (mSwitchingFromSystemUserMessage != null) {
-                pw.println("  mSwitchingFromSystemUserMessage: " + mSwitchingFromSystemUserMessage);
-            }
-            if (mSwitchingToSystemUserMessage != null) {
-                pw.println("  mSwitchingToSystemUserMessage: " + mSwitchingToSystemUserMessage);
-            }
+            pw.println("  mSwitchingFromUserMessage:" + mSwitchingFromUserMessage);
+            pw.println("  mSwitchingToUserMessage:" + mSwitchingToUserMessage);
             pw.println("  mLastUserUnlockingUptime: " + mLastUserUnlockingUptime);
         }
     }
@@ -4046,7 +4046,7 @@
         }
 
         void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
-                String switchingFromSystemUserMessage, String switchingToSystemUserMessage,
+                @Nullable String switchingFromUserMessage, @Nullable String switchingToUserMessage,
                 @NonNull Runnable onShown) {
             if (mService.mContext.getPackageManager()
                     .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
@@ -4059,7 +4059,7 @@
             synchronized (mUserSwitchingDialogLock) {
                 dismissUserSwitchingDialog(null);
                 mUserSwitchingDialog = new UserSwitchingDialog(mService.mContext, fromUser, toUser,
-                        mHandler, switchingFromSystemUserMessage, switchingToSystemUserMessage);
+                        mHandler, switchingFromUserMessage, switchingToUserMessage);
                 mUserSwitchingDialog.show(onShown);
             }
         }
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 223e0b7..f4e733a 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -52,6 +52,7 @@
 import com.android.internal.util.ObjectUtils;
 import com.android.internal.util.UserIcons;
 
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -75,21 +76,23 @@
 
     protected final UserInfo mOldUser;
     protected final UserInfo mNewUser;
-    private final String mSwitchingFromSystemUserMessage;
-    private final String mSwitchingToSystemUserMessage;
+    @Nullable
+    private final String mSwitchingFromUserMessage;
+    @Nullable
+    private final String mSwitchingToUserMessage;
     protected final Context mContext;
     private final int mTraceCookie;
 
     UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser, Handler handler,
-            String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
+            @Nullable String switchingFromUserMessage, @Nullable String switchingToUserMessage) {
         super(context, R.style.Theme_Material_NoActionBar_Fullscreen);
 
         mContext = context;
         mOldUser = oldUser;
         mNewUser = newUser;
         mHandler = handler;
-        mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage;
-        mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
+        mSwitchingFromUserMessage = switchingFromUserMessage;
+        mSwitchingToUserMessage = switchingToUserMessage;
         mDisableAnimations = SystemProperties.getBoolean(
                 "debug.usercontroller.disable_user_switching_dialog_animations", false);
         mTraceCookie = UserHandle.MAX_SECONDARY_USER_ID * oldUser.id + newUser.id;
@@ -166,14 +169,14 @@
                     : R.string.demo_starting_message);
         }
 
-        final String message =
-                mOldUser.id == UserHandle.USER_SYSTEM ? mSwitchingFromSystemUserMessage
-                : mNewUser.id == UserHandle.USER_SYSTEM ? mSwitchingToSystemUserMessage : null;
+        if (mSwitchingFromUserMessage != null || mSwitchingToUserMessage != null) {
+            if (mSwitchingFromUserMessage != null && mSwitchingToUserMessage != null) {
+                return mSwitchingFromUserMessage + " " + mSwitchingToUserMessage;
+            }
+            return Objects.requireNonNullElse(mSwitchingFromUserMessage, mSwitchingToUserMessage);
+        }
 
-        return message != null ? message
-                // If switchingFromSystemUserMessage or switchingToSystemUserMessage is null,
-                // fallback to system message.
-                : res.getString(R.string.user_switching_message, mNewUser.name);
+        return res.getString(R.string.user_switching_message, mNewUser.name);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index f7d7ed5..5ecac22 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -182,6 +182,7 @@
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.pm.PackageList;
 import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.ProtectedPackages;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
@@ -311,6 +312,8 @@
     private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface(
             ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
 
+    private ProtectedPackages mProtectedPackages;
+
     /**
      * Registered callbacks, called from {@link #collectAsyncNotedOp}.
      *
@@ -367,7 +370,7 @@
     private static final Duration RATE_LIMITER_WINDOW = Duration.ofMillis(10);
     private final RateLimiter mRateLimiter = new RateLimiter(RATE_LIMITER_WINDOW);
 
-    volatile @NonNull HistoricalRegistry mHistoricalRegistry;
+    volatile @NonNull HistoricalRegistryInterface mHistoricalRegistry;
 
     /*
      * These are app op restrictions imposed per user from various parties.
@@ -822,7 +825,8 @@
         @Override
         public void onOpModeChanged(int op, int uid, String packageName, String persistentDeviceId)
                 throws RemoteException {
-            mCallback.opChanged(op, uid, packageName, persistentDeviceId);
+            mCallback.opChanged(op, uid, packageName != null ? packageName : "",
+                    Objects.requireNonNull(persistentDeviceId));
         }
     }
 
@@ -1056,7 +1060,11 @@
         AppOpsManager.invalidateAppOpModeCache();
         AppOpsManager.disableAppOpModeCache();
 
-        mHistoricalRegistry = new HistoricalRegistry(this, context);
+        if (Flags.enableAllSqliteAppopsAccesses()) {
+            mHistoricalRegistry = new HistoricalRegistrySql(context);
+        } else {
+            mHistoricalRegistry = new LegacyHistoricalRegistry(this, context);
+        }
     }
 
     public void publish() {
@@ -1424,7 +1432,7 @@
 
     @GuardedBy("this")
     private void packageRemovedLocked(int uid, String packageName) {
-        getIoHandler().post(PooledLambda.obtainRunnable(HistoricalRegistry::clearHistory,
+        getIoHandler().post(PooledLambda.obtainRunnable(HistoricalRegistryInterface::clearHistory,
                 mHistoricalRegistry, uid, packageName));
 
         UidState uidState = mUidStates.get(uid);
@@ -1992,10 +2000,12 @@
                         new String[attributionChainExemptPackages.size()]) : null;
 
         // Must not hold the appops lock
-        getIoHandler().post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
+        getIoHandler().post(PooledLambda.obtainRunnable(
+                HistoricalRegistryInterface::getHistoricalOps,
                 mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
                 filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
-                callback).recycleOnUse());
+                callback
+        ).recycleOnUse());
     }
 
     @Override
@@ -2024,7 +2034,7 @@
 
         // Must not hold the appops lock
         getIoHandler().post(PooledLambda.obtainRunnable(
-                HistoricalRegistry::getHistoricalOpsFromDiskRaw,
+                HistoricalRegistryInterface::getHistoricalOpsFromDiskRaw,
                 mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
                 filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
                 callback).recycleOnUse());
@@ -2121,6 +2131,12 @@
 
         enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
         verifyIncomingOp(code);
+
+        if (isDeviceProvisioningPackage(uid, null)) {
+            Slog.w(TAG, "Cannot set uid mode for device provisioning app by Shell");
+            return;
+        }
+
         code = AppOpsManager.opToSwitch(code);
 
         if (permissionPolicyCallback == null) {
@@ -2431,6 +2447,11 @@
             return;
         }
 
+        if (isDeviceProvisioningPackage(uid, packageName)) {
+            Slog.w(TAG, "Cannot set op mode for device provisioning app by Shell");
+            return;
+        }
+
         code = AppOpsManager.opToSwitch(code);
 
         PackageVerificationResult pvr;
@@ -2461,6 +2482,36 @@
         notifyStorageManagerOpModeChangedSync(code, uid, packageName, mode, previousMode);
     }
 
+    // Device provisioning package is restricted from setting app op mode through shell command
+    private boolean isDeviceProvisioningPackage(int uid,
+            @Nullable String packageName) {
+        if (UserHandle.getAppId(Binder.getCallingUid()) == Process.SHELL_UID) {
+            ProtectedPackages protectedPackages = getProtectedPackages();
+
+            if (packageName != null && protectedPackages.isDeviceProvisioningPackage(packageName)) {
+                return true;
+            }
+
+            String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+            if (packageNames != null) {
+                for (String pkg : packageNames) {
+                    if (protectedPackages.isDeviceProvisioningPackage(pkg)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    // Race condition is allowed here for better performance
+    private ProtectedPackages getProtectedPackages() {
+        if (mProtectedPackages == null) {
+            mProtectedPackages = new ProtectedPackages(mContext);
+        }
+        return mProtectedPackages;
+    }
+
     private void notifyOpChanged(ArraySet<OnOpModeChangedListener> callbacks, int code,
             int uid, String packageName, String persistentDeviceId) {
         for (int i = 0; i < callbacks.size(); i++) {
@@ -6961,7 +7012,6 @@
         offsetHistory_enforcePermission();
         // Must not hold the appops lock
         mHistoricalRegistry.offsetHistory(offsetMillis);
-        mHistoricalRegistry.offsetDiscreteHistory(offsetMillis);
     }
 
     @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_APPOPS)
@@ -7002,7 +7052,14 @@
             SystemClock.sleep(offlineDurationMillis);
         }
 
-        mHistoricalRegistry = new HistoricalRegistry(mHistoricalRegistry);
+        if (Flags.enableAllSqliteAppopsAccesses()) {
+            mHistoricalRegistry = new HistoricalRegistrySql(
+                    (HistoricalRegistrySql) mHistoricalRegistry);
+        } else {
+            mHistoricalRegistry = new LegacyHistoricalRegistry(
+                    (LegacyHistoricalRegistry) mHistoricalRegistry);
+        }
+
         mHistoricalRegistry.systemReady(mContext.getContentResolver());
         mHistoricalRegistry.persistPendingHistory();
     }
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 9dd09ce..b9bc27d 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -113,7 +113,7 @@
         mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                 parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime,
                 AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE,
-                DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP, accessCount);
+                accessCount);
     }
 
     /**
@@ -163,7 +163,7 @@
     public void rejected(@AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) {
         rejected(System.currentTimeMillis(), uidState, flags);
 
-        mAppOpsService.mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid,
+        mAppOpsService.mHistoricalRegistry.incrementOpRejectedCount(parent.op, parent.uid,
                 parent.packageName, tag, uidState, flags);
     }
 
@@ -257,8 +257,7 @@
         if (isStarted) {
             mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                     parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
-                    attributionFlags, attributionChainId,
-                    DiscreteOpsRegistry.ACCESS_TYPE_START_OP, 1);
+                    attributionFlags, attributionChainId, 1);
         }
     }
 
@@ -344,9 +343,7 @@
             mAppOpsService.mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
                     parent.packageName, persistentDeviceId, tag, event.getUidState(),
                     event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
-                    event.getAttributionFlags(), event.getAttributionChainId(),
-                    isPausing ? DiscreteOpsRegistry.ACCESS_TYPE_PAUSE_OP
-                            : DiscreteOpsRegistry.ACCESS_TYPE_FINISH_OP);
+                    event.getAttributionFlags(), event.getAttributionChainId());
 
             if (!isPausing) {
                 mAppOpsService.mInProgressStartOpEventPool.release(event);
@@ -454,7 +451,7 @@
             mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                     parent.packageName, persistentDeviceId, tag, event.getUidState(),
                     event.getFlags(), startTime, event.getAttributionFlags(),
-                    event.getAttributionChainId(), DiscreteOpsRegistry.ACCESS_TYPE_RESUME_OP, 1);
+                    event.getAttributionChainId(), 1);
             if (shouldSendActive) {
                 mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
                         parent.packageName, tag, event.getVirtualDeviceId(), true,
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
index 86f5d9b..3a8d583 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
@@ -24,6 +24,7 @@
 import android.database.DefaultDatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteFullException;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteRawStatement;
 import android.os.Environment;
@@ -174,11 +175,16 @@
         if (DEBUG) {
             Slog.i(LOG_TAG, "DB execSQL, sql: " + sql);
         }
-        SQLiteDatabase db = getWritableDatabase();
-        if (bindArgs == null) {
-            db.execSQL(sql);
-        } else {
-            db.execSQL(sql, bindArgs);
+        try {
+            SQLiteDatabase db = getWritableDatabase();
+            if (bindArgs == null) {
+                db.execSQL(sql);
+            } else {
+                db.execSQL(sql, bindArgs);
+            }
+        } catch (SQLiteFullException exception) {
+            Slog.e(LOG_TAG, "Couldn't exec sql command, disk is full. Discrete ops "
+                    + "db file size (bytes) : " + getDatabaseFile().length(), exception);
         }
     }
 
@@ -189,11 +195,11 @@
             @AppOpsManager.HistoricalOpsRequestFilter int requestFilters,
             int uidFilter, @Nullable String packageNameFilter,
             @Nullable String attributionTagFilter, IntArray opCodesFilter, int opFlagsFilter,
-            long beginTime, long endTime, int limit, String orderByColumn) {
+            long beginTime, long endTime, int limit, String orderByColumn, boolean ascending) {
         List<SQLCondition> conditions = prepareConditions(beginTime, endTime, requestFilters,
                 uidFilter, packageNameFilter,
                 attributionTagFilter, opCodesFilter, opFlagsFilter);
-        String sql = buildSql(conditions, orderByColumn, limit);
+        String sql = buildSql(conditions, orderByColumn, ascending, limit);
         long startTime = 0;
         if (Flags.sqliteDiscreteOpEventLoggingEnabled()) {
             startTime = SystemClock.elapsedRealtime();
@@ -249,7 +255,8 @@
         return results;
     }
 
-    private String buildSql(List<SQLCondition> conditions, String orderByColumn, int limit) {
+    private String buildSql(List<SQLCondition> conditions, String orderByColumn, boolean ascending,
+            int limit) {
         StringBuilder sql = new StringBuilder(DiscreteOpsTable.SELECT_TABLE_DATA);
         if (!conditions.isEmpty()) {
             sql.append(" WHERE ");
@@ -264,6 +271,7 @@
 
         if (orderByColumn != null) {
             sql.append(" ORDER BY ").append(orderByColumn);
+            sql.append(ascending ? " ASC " : " DESC ");
         }
         if (limit > 0) {
             sql.append(" LIMIT ").append(limit);
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.java
index c38ee55..29e78be 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.java
@@ -40,7 +40,16 @@
     static void migrateDiscreteOpsToXml(DiscreteOpsSqlRegistry sqlRegistry,
             DiscreteOpsXmlRegistry xmlRegistry) {
         List<DiscreteOpsSqlRegistry.DiscreteOp> sqlOps = sqlRegistry.getAllDiscreteOps();
-        DiscreteOpsXmlRegistry.DiscreteOps xmlOps = getXmlDiscreteOps(sqlOps);
+
+        // Only migrate configured discrete ops. Sqlite may contain all runtime ops, and more.
+        List<DiscreteOpsSqlRegistry.DiscreteOp> filteredList = new ArrayList<>();
+        for (DiscreteOpsSqlRegistry.DiscreteOp opEvent: sqlOps) {
+            if (DiscreteOpsRegistry.isDiscreteOp(opEvent.getOpCode(), opEvent.getOpFlags())) {
+                filteredList.add(opEvent);
+            }
+        }
+
+        DiscreteOpsXmlRegistry.DiscreteOps xmlOps = getXmlDiscreteOps(filteredList);
         xmlRegistry.migrateSqliteData(xmlOps);
         sqlRegistry.deleteDatabase();
     }
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
index 12c35ae..3867cbe 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
@@ -16,6 +16,9 @@
 
 package com.android.server.appop;
 
+import static android.app.AppOpsManager.OP_ACCESS_ACCESSIBILITY;
+import static android.app.AppOpsManager.OP_ACCESS_NOTIFICATIONS;
+import static android.app.AppOpsManager.OP_BIND_ACCESSIBILITY_SERVICE;
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_COARSE_LOCATION;
 import static android.app.AppOpsManager.OP_EMERGENCY_LOCATION;
@@ -23,30 +26,24 @@
 import static android.app.AppOpsManager.OP_FLAG_SELF;
 import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
 import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
+import static android.app.AppOpsManager.OP_GPS;
 import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
 import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
 import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
 import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
-import static android.app.AppOpsManager.OP_PROCESS_OUTGOING_CALLS;
+import static android.app.AppOpsManager.OP_READ_DEVICE_IDENTIFIERS;
 import static android.app.AppOpsManager.OP_READ_HEART_RATE;
-import static android.app.AppOpsManager.OP_READ_ICC_SMS;
 import static android.app.AppOpsManager.OP_READ_OXYGEN_SATURATION;
 import static android.app.AppOpsManager.OP_READ_SKIN_TEMPERATURE;
-import static android.app.AppOpsManager.OP_READ_SMS;
 import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
 import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING;
-import static android.app.AppOpsManager.OP_SEND_SMS;
-import static android.app.AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS;
-import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.app.AppOpsManager.OP_WRITE_ICC_SMS;
-import static android.app.AppOpsManager.OP_WRITE_SMS;
+import static android.app.AppOpsManager.OP_RUN_IN_BACKGROUND;
 
 import static java.lang.Long.min;
 import static java.lang.Math.max;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
@@ -54,16 +51,13 @@
 import android.os.Build;
 import android.permission.flags.Flags;
 import android.provider.DeviceConfig;
+import android.util.IntArray;
 import android.util.Slog;
 
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
-
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.text.SimpleDateFormat;
 import java.time.Duration;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.Set;
 
@@ -88,9 +82,8 @@
  * INITIALIZATION: We can initialize persistence only after the system is ready
  * as we need to check the optional configuration override from the settings
  * database which is not initialized at the time the app ops service is created. This class
- * relies on {@link HistoricalRegistry} for controlling that no calls are allowed until then. All
- * outside calls are going through {@link HistoricalRegistry}.
- *
+ * relies on {@link LegacyHistoricalRegistry} for controlling that no calls are allowed until then.
+ * All outside calls are going through {@link LegacyHistoricalRegistry}.
  */
 abstract class DiscreteOpsRegistry {
     private static final String TAG = DiscreteOpsRegistry.class.getSimpleName();
@@ -100,21 +93,37 @@
     static final String PROPERTY_DISCRETE_HISTORY_QUANTIZATION =
             "discrete_history_quantization_millis";
     static final String PROPERTY_DISCRETE_FLAGS = "discrete_history_op_flags";
+    // Comma separated app ops list config for testing i.e. "1,2,3,4"
     static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist";
-    static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
+    // These ops are deemed important for detecting a malicious app, and are recorded.
+    static final int[] IMPORTANT_OPS_FOR_SECURITY = new int[] {
+            OP_GPS,
+            OP_ACCESS_NOTIFICATIONS,
+            OP_RUN_IN_BACKGROUND,
+            OP_BIND_ACCESSIBILITY_SERVICE,
+            OP_ACCESS_ACCESSIBILITY,
+            OP_READ_DEVICE_IDENTIFIERS,
+            OP_MONITOR_HIGH_POWER_LOCATION,
+            OP_MONITOR_LOCATION
+    };
+
+    // These are additional ops, which are not backed by runtime permissions, but are recorded.
+    static final int[] ADDITIONAL_DISCRETE_OPS = new int[] {
+            OP_PHONE_CALL_MICROPHONE,
+            OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
+            OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
+            OP_PHONE_CALL_CAMERA,
+            OP_EMERGENCY_LOCATION,
+            OP_RESERVED_FOR_TESTING
+    };
+
+    // Legacy ops captured in discrete database.
+    private static final String LEGACY_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
             + "," + OP_EMERGENCY_LOCATION + "," + OP_CAMERA + "," + OP_RECORD_AUDIO + ","
             + OP_PHONE_CALL_MICROPHONE + "," + OP_PHONE_CALL_CAMERA + ","
             + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + "," + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
             + "," + OP_READ_HEART_RATE + "," + OP_READ_OXYGEN_SATURATION + ","
             + OP_READ_SKIN_TEMPERATURE + "," + OP_RESERVED_FOR_TESTING;
-    static final int[] sDiscreteOpsToLog =
-            new int[]{OP_FINE_LOCATION, OP_COARSE_LOCATION, OP_EMERGENCY_LOCATION, OP_CAMERA,
-                    OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, OP_PHONE_CALL_CAMERA,
-                    OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, OP_READ_SMS,
-                    OP_WRITE_SMS, OP_SEND_SMS, OP_READ_ICC_SMS, OP_WRITE_ICC_SMS,
-                    OP_SMS_FINANCIAL_TRANSACTIONS, OP_SYSTEM_ALERT_WINDOW, OP_MONITOR_LOCATION,
-                    OP_MONITOR_HIGH_POWER_LOCATION, OP_PROCESS_OUTGOING_CALLS,
-            };
 
     static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(7).toMillis();
     static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
@@ -126,7 +135,7 @@
     // in case of duplicate op events.
     static long sDiscreteHistoryQuantization;
 
-    static int[] sDiscreteOps;
+    static int[] sDiscreteOps = new int[0];
     static int sDiscreteFlags;
 
     static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED
@@ -134,27 +143,6 @@
 
     boolean mDebugMode = false;
 
-    static final int ACCESS_TYPE_NOTE_OP =
-            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__NOTE_OP;
-    static final int ACCESS_TYPE_START_OP =
-            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__START_OP;
-    static final int ACCESS_TYPE_FINISH_OP =
-            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__FINISH_OP;
-    static final int ACCESS_TYPE_PAUSE_OP =
-            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__PAUSE_OP;
-    static final int ACCESS_TYPE_RESUME_OP =
-            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__RESUME_OP;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"ACCESS_TYPE_"}, value = {
-            ACCESS_TYPE_NOTE_OP,
-            ACCESS_TYPE_START_OP,
-            ACCESS_TYPE_FINISH_OP,
-            ACCESS_TYPE_PAUSE_OP,
-            ACCESS_TYPE_RESUME_OP
-    })
-    @interface AccessType {}
-
     void systemReady() {
         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
                 AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties p) -> {
@@ -166,8 +154,7 @@
     abstract void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId,
             int op, @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
             @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
-            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
-            @DiscreteOpsRegistry.AccessType int accessType);
+            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId);
 
     /**
      * A periodic callback from {@link AppOpsService} to flush the in memory events to disk.
@@ -218,7 +205,7 @@
     }
 
     static boolean isDiscreteOp(int op, @AppOpsManager.OpFlags int flags) {
-        if (!ArrayUtils.contains(sDiscreteOps, op)) {
+        if (Arrays.binarySearch(sDiscreteOps, op) < 0) {
             return false;
         }
         if ((flags & (sDiscreteFlags)) == 0) {
@@ -227,9 +214,6 @@
         return true;
     }
 
-    // could this be impl detail of discrete registry, just one test is using the method
-    // abstract DiscreteRegistry.DiscreteOps getAllDiscreteOps();
-
     private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
         if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_CUTOFF)) {
             sDiscreteHistoryCutoff = p.getLong(PROPERTY_DISCRETE_HISTORY_CUTOFF,
@@ -251,11 +235,42 @@
         } else {
             sDiscreteHistoryQuantization = DEFAULT_DISCRETE_HISTORY_QUANTIZATION;
         }
-        sDiscreteFlags = p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS) ? sDiscreteFlags =
-                p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE) : OP_FLAGS_DISCRETE;
-        sDiscreteOps = p.getKeyset().contains(PROPERTY_DISCRETE_OPS_LIST) ? parseOpsList(
-                p.getString(PROPERTY_DISCRETE_OPS_LIST, DEFAULT_DISCRETE_OPS)) : parseOpsList(
-                DEFAULT_DISCRETE_OPS);
+        sDiscreteFlags = p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS)
+                ? p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE) : OP_FLAGS_DISCRETE;
+        String opsListConfig = p.getString(PROPERTY_DISCRETE_OPS_LIST, null);
+        sDiscreteOps = opsListConfig == null ? getDefaultOpsList() : parseOpsList(opsListConfig);
+
+        Arrays.sort(sDiscreteOps);
+    }
+
+    // App ops backed by runtime/dangerous permissions.
+    private static IntArray getRuntimePermissionOps() {
+        IntArray runtimeOps = new IntArray();
+        for (int op = 0; op < AppOpsManager._NUM_OP; op++) {
+            if (AppOpsManager.opIsRuntimePermission(op)) {
+                runtimeOps.add(op);
+            }
+        }
+        return runtimeOps;
+    }
+
+    /**
+     * @return an array of app ops captured into discrete database.
+     */
+    private static int[] getDefaultOpsList() {
+        if (!(Flags.recordAllRuntimeAppopsSqlite() && Flags.enableSqliteAppopsAccesses())) {
+            return getDefaultLegacyOps();
+        }
+
+        IntArray discreteOpsArray = getRuntimePermissionOps();
+        discreteOpsArray.addAll(IMPORTANT_OPS_FOR_SECURITY);
+        discreteOpsArray.addAll(ADDITIONAL_DISCRETE_OPS);
+
+        return discreteOpsArray.toArray();
+    }
+
+    private static int[] getDefaultLegacyOps() {
+        return parseOpsList(LEGACY_OPS);
     }
 
     private static int[] parseOpsList(String opsList) {
@@ -273,32 +288,8 @@
             }
         } catch (NumberFormatException e) {
             Slog.e(TAG, "Failed to parse Discrete ops list: " + e.getMessage());
-            return parseOpsList(DEFAULT_DISCRETE_OPS);
+            return getDefaultOpsList();
         }
         return result;
     }
-
-    /**
-     * Whether app op access tacking is enabled and a metric event should be logged.
-     */
-    static boolean shouldLogAccess(int op) {
-        return Flags.appopAccessTrackingLoggingEnabled()
-                && ArrayUtils.contains(sDiscreteOpsToLog, op);
-    }
-
-    String getAttributionTag(String attributionTag, String packageName) {
-        if (attributionTag == null || packageName == null) {
-            return attributionTag;
-        }
-        int firstChar = 0;
-        if (attributionTag.startsWith(packageName)) {
-            firstChar = packageName.length();
-            if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar)
-                    == '.') {
-                firstChar++;
-            }
-        }
-        return attributionTag.substring(firstChar);
-    }
-
 }
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
index dc11be9..f50f45a 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
@@ -36,7 +36,6 @@
 import android.util.LongSparseArray;
 import android.util.Slog;
 
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.ServiceThread;
 
 import java.io.File;
@@ -97,15 +96,7 @@
     void recordDiscreteAccess(int uid, String packageName,
             @NonNull String deviceId, int op,
             @Nullable String attributionTag, int flags, int uidState,
-            long accessTime, long accessDuration, int attributionFlags, int attributionChainId,
-            int accessType) {
-        if (shouldLogAccess(op)) {
-            FrameworkStatsLog.write(FrameworkStatsLog.APP_OP_ACCESS_TRACKED, uid, op, accessType,
-                    uidState, flags, attributionFlags,
-                    getAttributionTag(attributionTag, packageName),
-                    attributionChainId);
-        }
-
+            long accessTime, long accessDuration, int attributionFlags, int attributionChainId) {
         if (!isDiscreteOp(op, flags)) {
             return;
         }
@@ -127,7 +118,7 @@
     @Override
     void shutdown() {
         mSqliteWriteHandler.removeAllPendingMessages();
-        mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.getAllEventsAndClear());
+        mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.evictAllAppOpEvents());
     }
 
     @Override
@@ -181,15 +172,19 @@
             @Nullable String[] opNamesFilter,
             @Nullable String attributionTagFilter, int opFlagsFilter,
             Set<String> attributionExemptPkgs) {
-        // flush the cache into database before read.
-        mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.getAllEventsAndClear());
-        boolean assembleChains = attributionExemptPkgs != null;
         IntArray opCodes = getAppOpCodes(filter, opNamesFilter);
+        // flush the cache into database before read.
+        if (opCodes != null) {
+            mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.evictAppOpEvents(opCodes));
+        } else {
+            mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.evictAllAppOpEvents());
+        }
+        boolean assembleChains = attributionExemptPkgs != null;
         beginTimeMillis = Math.max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
                 ChronoUnit.MILLIS).toEpochMilli());
         List<DiscreteOp> discreteOps = mDiscreteOpsDbHelper.getDiscreteOps(filter, uidFilter,
                 packageNameFilter, attributionTagFilter, opCodes, opFlagsFilter, beginTimeMillis,
-                endTimeMillis, -1, null);
+                endTimeMillis, -1, null, false);
 
         LongSparseArray<AttributionChain> attributionChains = null;
         if (assembleChains) {
@@ -222,14 +217,15 @@
             @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
             @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
             int nDiscreteOps) {
-        writeAndClearOldAccessHistory();
+        // flush the cache into database before dump.
+        mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.evictAllAppOpEvents());
         IntArray opCodes = new IntArray();
         if (dumpOp != AppOpsManager.OP_NONE) {
             opCodes.add(dumpOp);
         }
         List<DiscreteOp> discreteOps = mDiscreteOpsDbHelper.getDiscreteOps(filter, uidFilter,
                 packageNameFilter, attributionTagFilter, opCodes, 0, -1,
-                -1, nDiscreteOps, DiscreteOpsTable.Columns.ACCESS_TIME);
+                -1, nDiscreteOps, DiscreteOpsTable.Columns.ACCESS_TIME, false);
 
         pw.print(prefix);
         pw.print("Largest chain id: ");
@@ -374,7 +370,7 @@
                     try {
                         List<DiscreteOp> evictedEvents;
                         synchronized (mDiscreteOpCache) {
-                            evictedEvents = mDiscreteOpCache.evict();
+                            evictedEvents = mDiscreteOpCache.evictOldAppOpEvents();
                         }
                         mDiscreteOpsDbHelper.insertDiscreteOps(evictedEvents);
                     } finally {
@@ -397,7 +393,7 @@
                     try {
                         List<DiscreteOp> evictedEvents;
                         synchronized (mDiscreteOpCache) {
-                            evictedEvents = mDiscreteOpCache.evict();
+                            evictedEvents = mDiscreteOpCache.evictOldAppOpEvents();
                             // if nothing to evict, just write the whole cache to database.
                             if (evictedEvents.isEmpty()
                                     && mDiscreteOpCache.size() >= mDiscreteOpCache.capacity()) {
@@ -459,9 +455,10 @@
         }
 
         /**
-         * Evict entries older than {@link DiscreteOpsRegistry#sDiscreteHistoryQuantization}.
+         * Evict entries older than {@link DiscreteOpsRegistry#sDiscreteHistoryQuantization} i.e.
+         * app op events older than one minute (default quantization) will be evicted.
          */
-        private List<DiscreteOp> evict() {
+        private List<DiscreteOp> evictOldAppOpEvents() {
             synchronized (this) {
                 List<DiscreteOp> evictedEvents = new ArrayList<>();
                 Set<DiscreteOp> snapshot = new ArraySet<>(mCache);
@@ -478,11 +475,9 @@
         }
 
         /**
-         * Remove all the entries from cache.
-         *
-         * @return return all removed entries.
+         * Evict all app op entries from cache, and return the list of removed ops.
          */
-        public List<DiscreteOp> getAllEventsAndClear() {
+        public List<DiscreteOp> evictAllAppOpEvents() {
             synchronized (this) {
                 List<DiscreteOp> cachedOps = new ArrayList<>(mCache.size());
                 if (mCache.isEmpty()) {
@@ -494,6 +489,25 @@
             }
         }
 
+        /**
+         * Evict specified app ops from cache, and return the list of evicted ops.
+         */
+        public List<DiscreteOp> evictAppOpEvents(IntArray ops) {
+            synchronized (this) {
+                List<DiscreteOp> evictedOps = new ArrayList<>();
+                if (mCache.isEmpty()) {
+                    return evictedOps;
+                }
+                for (DiscreteOp discreteOp: mCache) {
+                    if (ops.contains(discreteOp.getOpCode())) {
+                        evictedOps.add(discreteOp);
+                    }
+                }
+                evictedOps.forEach(mCache::remove);
+                return evictedOps;
+            }
+        }
+
         int size() {
             return mCache.size();
         }
@@ -654,7 +668,10 @@
                     + ", uidState=" + getUidStateName(mUidState)
                     + ", chainId=" + mChainId
                     + ", accessTime=" + mAccessTime
-                    + ", duration=" + mDuration + '}';
+                    + ", mDiscretizedAccessTime=" + mDiscretizedAccessTime
+                    + ", duration=" + mDuration
+                    + ", mDiscretizedDuration=" + mDiscretizedDuration
+                    + '}';
         }
 
         public int getUid() {
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java b/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java
index 1523cca..909a04c 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java
@@ -48,15 +48,13 @@
     @Override
     void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId, int op,
             @Nullable String attributionTag, int flags, int uidState, long accessTime,
-            long accessDuration, int attributionFlags, int attributionChainId, int accessType) {
+            long accessDuration, int attributionFlags, int attributionChainId) {
         long start = SystemClock.uptimeMillis();
         mXmlRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, attributionTag, flags,
-                uidState, accessTime, accessDuration, attributionFlags, attributionChainId,
-                accessType);
+                uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
         long start2 = SystemClock.uptimeMillis();
         mSqlRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, attributionTag, flags,
-                uidState, accessTime, accessDuration, attributionFlags, attributionChainId,
-                accessType);
+                uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
         long end = SystemClock.uptimeMillis();
         long xmlTimeTaken = start2 - start;
         long sqlTimeTaken = end - start2;
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
index a6e3fc7..771df19 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
@@ -45,7 +45,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
@@ -82,7 +81,7 @@
  * THREADING AND LOCKING:
  * For in-memory transactions this class relies on {@link DiscreteOpsXmlRegistry#mInMemoryLock}.
  * It is assumed that the same lock is used for in-memory transactions in {@link AppOpsService},
- * {@link HistoricalRegistry}, and {@link DiscreteOpsXmlRegistry }.
+ * {@link LegacyHistoricalRegistry}, and {@link DiscreteOpsXmlRegistry }.
  * {@link DiscreteOpsRegistry#recordDiscreteAccess} must only be called while holding this lock.
  * {@link DiscreteOpsXmlRegistry#mOnDiskLock} is used when disk transactions are performed.
  * It is very important to release {@link DiscreteOpsXmlRegistry#mInMemoryLock} as soon as
@@ -159,15 +158,7 @@
     void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId, int op,
             @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
             @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
-            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
-            @AccessType int accessType) {
-        if (shouldLogAccess(op)) {
-            FrameworkStatsLog.write(FrameworkStatsLog.APP_OP_ACCESS_TRACKED, uid, op, accessType,
-                    uidState, flags, attributionFlags,
-                    getAttributionTag(attributionTag, packageName),
-                    attributionChainId);
-        }
-
+            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
         if (!isDiscreteOp(op, flags)) {
             return;
         }
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistryInterface.java b/services/core/java/com/android/server/appop/HistoricalRegistryInterface.java
new file mode 100644
index 0000000..b6210a7
--- /dev/null
+++ b/services/core/java/com/android/server/appop/HistoricalRegistryInterface.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.ContentResolver;
+import android.os.RemoteCallback;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * A registry to record app operation access events, which are generated upon an application's
+ * access to private data or system resources. These events are stored in both aggregated
+ * and individual/discrete formats.
+ */
+public interface HistoricalRegistryInterface {
+    /**
+     * A callback to inform system components are ready.
+     */
+    void systemReady(@NonNull ContentResolver resolver);
+
+    /**
+     * Callback for system shutdown.
+     */
+    void shutdown();
+
+    /**
+     * Dumps aggregated/historical events to the console based on the filters.
+     */
+    void dump(String prefix, PrintWriter pw, int filterUid,
+            @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp,
+            @AppOpsManager.HistoricalOpsRequestFilter int filter);
+
+    /**
+     * Dumps discrete/individual events to the console based on filters.
+     */
+    void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter,
+            @Nullable String packageNameFilter, @Nullable String attributionTagFilter,
+            @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
+            @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
+            int nDiscreteOps);
+
+    /**
+     * Record duration for given op.
+     */
+    void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
+            @NonNull String deviceId, @Nullable String attributionTag,
+            @AppOpsManager.UidState int uidState,
+            @AppOpsManager.OpFlags int flags, long eventStartTime, long increment,
+            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId);
+
+    /**
+     * Record access counts for given op.
+     */
+    void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
+            @NonNull String deviceId, @Nullable String attributionTag,
+            @AppOpsManager.UidState int uidState,
+            @AppOpsManager.OpFlags int flags, long accessTime,
+            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
+            int accessCount);
+
+    /**
+     * Record rejected counts for given op.
+     */
+    void incrementOpRejectedCount(int op, int uid, @NonNull String packageName,
+            @Nullable String attributionTag, @AppOpsManager.UidState int uidState,
+            @AppOpsManager.OpFlags int flags);
+
+    /**
+     * Read historical ops from both aggregated and discrete events based on input filter.
+     */
+    void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag,
+            @Nullable String[] opNames, @AppOpsManager.OpHistoryFlags int historyFlags,
+            @AppOpsManager.HistoricalOpsRequestFilter int filter, long beginTimeMillis,
+            long endTimeMillis,
+            @AppOpsManager.OpFlags int flags, @Nullable String[] attributionExemptPkgs,
+            @NonNull RemoteCallback callback);
+
+    /**
+     * Remove app op events for a given UID and package.
+     */
+    void clearHistory(int uid, String packageName);
+
+    /**
+     * A periodic callback from {@link AppOpsService} to flush the in memory discrete
+     * app op events to disk/database.
+     */
+    void writeAndClearDiscreteHistory();
+
+    /**
+     * A callback flush the in memory app op events to disk/database.
+     */
+    void persistPendingHistory();
+
+    /**
+     * Set history parameters.
+     *
+     * @param mode - Whether historical registry is Active, Passive or Disabled.
+     * @param baseSnapshotInterval - Interval between 2 snapshots, default 15 minutes.
+     * @param intervalCompressionMultiplier - Interval compression multiplier, default is 10.
+     */
+    void setHistoryParameters(@AppOpsManager.HistoricalMode int mode,
+            long baseSnapshotInterval, long intervalCompressionMultiplier);
+
+    /**
+     * Reset history parameters to defaults.
+     */
+    void resetHistoryParameters();
+
+    /**
+     * Remove all app op accesses from both aggregated and individual event's storage.
+     */
+    void clearAllHistory();
+
+    /**
+     * Offsets the history by the given duration.
+     */
+    void offsetHistory(long offsetMillis);
+
+    /**
+     * Retrieve historical app op stats for a period form disk.
+     */
+    void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName,
+            @Nullable String attributionTag, @Nullable String[] opNames,
+            @AppOpsManager.OpHistoryFlags int historyFlags,
+            @AppOpsManager.HistoricalOpsRequestFilter int filter,
+            long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flags,
+            String[] attributionExemptedPackages, @NonNull RemoteCallback callback);
+
+    /**
+     * Adds ops to the history directly. This could be useful for testing especially
+     * when the historical registry operates in passive mode.
+     */
+    void addHistoricalOps(AppOpsManager.HistoricalOps ops);
+}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistrySql.java b/services/core/java/com/android/server/appop/HistoricalRegistrySql.java
new file mode 100644
index 0000000..cf5b941
--- /dev/null
+++ b/services/core/java/com/android/server/appop/HistoricalRegistrySql.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.RemoteCallback;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+// TODO add more documentation later
+
+/**
+ * This historical registry implementation store app events in sqlite. The data is stored in 2
+ * tables 1) discrete events 2) aggregated events.
+ */
+public class HistoricalRegistrySql implements HistoricalRegistryInterface {
+    // TODO impl will be added in a separate CL
+    HistoricalRegistrySql(Context context) {
+    }
+
+    HistoricalRegistrySql(HistoricalRegistrySql other) {
+    }
+
+    @Override
+    public void systemReady(@NonNull ContentResolver resolver) {
+
+    }
+
+    @Override
+    public void shutdown() {
+
+    }
+
+    @Override
+    public void dump(String prefix, PrintWriter pw, int filterUid,
+            @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp,
+            int filter) {
+
+    }
+
+    @Override
+    public void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter,
+            @Nullable String packageNameFilter, @Nullable String attributionTagFilter, int filter,
+            int dumpOp, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
+            int nDiscreteOps) {
+
+    }
+
+    @Override
+    public void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
+            @NonNull String deviceId, @Nullable String attributionTag, int uidState, int flags,
+            long eventStartTime, long increment, int attributionFlags, int attributionChainId) {
+
+    }
+
+    @Override
+    public void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
+            @NonNull String deviceId, @Nullable String attributionTag, int uidState, int flags,
+            long accessTime, int attributionFlags, int attributionChainId, int accessCount) {
+
+    }
+
+    @Override
+    public void incrementOpRejectedCount(int op, int uid, @NonNull String packageName,
+            @Nullable String attributionTag, int uidState, int flags) {
+
+    }
+
+    @Override
+    public void getHistoricalOps(int uid, @Nullable String packageName,
+            @Nullable String attributionTag, @Nullable String[] opNames, int historyFlags,
+            int filter, long beginTimeMillis, long endTimeMillis, int flags,
+            @Nullable String[] attributionExemptPkgs, @NonNull RemoteCallback callback) {
+
+    }
+
+    @Override
+    public void clearHistory(int uid, String packageName) {
+
+    }
+
+    @Override
+    public void writeAndClearDiscreteHistory() {
+
+    }
+
+    @Override
+    public void persistPendingHistory() {
+
+    }
+
+    @Override
+    public void setHistoryParameters(int mode, long baseSnapshotInterval,
+            long intervalCompressionMultiplier) {
+
+    }
+
+    @Override
+    public void resetHistoryParameters() {
+
+    }
+
+    @Override
+    public void clearAllHistory() {
+
+    }
+
+    @Override
+    public void offsetHistory(long offsetMillis) {
+
+    }
+
+    @Override
+    public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName,
+            @Nullable String attributionTag, @Nullable String[] opNames, int historyFlags,
+            int filter, long beginTimeMillis, long endTimeMillis, int flags,
+            String[] attributionExemptedPackages, @NonNull RemoteCallback callback) {
+
+    }
+
+    @Override
+    public void addHistoricalOps(AppOpsManager.HistoricalOps ops) {
+
+    }
+}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
similarity index 97%
rename from services/core/java/com/android/server/appop/HistoricalRegistry.java
rename to services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
index d267e0d..f4f5775 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/LegacyHistoricalRegistry.java
@@ -128,11 +128,11 @@
  */
 // TODO (bug:122218838): Make sure we handle start of epoch time
 // TODO (bug:122218838): Validate changed time is handled correctly
-final class HistoricalRegistry {
+final class LegacyHistoricalRegistry implements HistoricalRegistryInterface {
     private static final boolean DEBUG = false;
     private static final boolean KEEP_WTF_LOG = Build.IS_DEBUGGABLE;
 
-    private static final String LOG_TAG = HistoricalRegistry.class.getSimpleName();
+    private static final String LOG_TAG = LegacyHistoricalRegistry.class.getSimpleName();
 
     private static final String PARAMETER_DELIMITER = ",";
     private static final String PARAMETER_ASSIGNMENT = "=";
@@ -200,7 +200,7 @@
 
     private final Context mContext;
 
-    HistoricalRegistry(@NonNull Object lock, Context context) {
+    LegacyHistoricalRegistry(@NonNull Object lock, Context context) {
         mInMemoryLock = lock;
         mContext = context;
         if (Flags.enableSqliteAppopsAccesses()) {
@@ -210,7 +210,7 @@
         }
     }
 
-    HistoricalRegistry(@NonNull HistoricalRegistry other) {
+    LegacyHistoricalRegistry(@NonNull LegacyHistoricalRegistry other) {
         this(other.mInMemoryLock, other.mContext);
         mMode = other.mMode;
         mBaseSnapshotInterval = other.mBaseSnapshotInterval;
@@ -218,7 +218,8 @@
         mDiscreteRegistry = other.mDiscreteRegistry;
     }
 
-    void systemReady(@NonNull ContentResolver resolver) {
+    @Override
+    public void systemReady(@NonNull ContentResolver resolver) {
         mDiscreteRegistry.systemReady();
         final Uri uri = Settings.Global.getUriFor(Settings.Global.APPOP_HISTORY_PARAMETERS);
         resolver.registerContentObserver(uri, false, new ContentObserver(
@@ -312,16 +313,18 @@
                 final int mode = AppOpsManager.parseHistoricalMode(modeValue);
                 final long baseSnapshotInterval = Long.parseLong(baseSnapshotIntervalValue);
                 final int intervalCompressionMultiplier = Integer.parseInt(intervalMultiplierValue);
-                setHistoryParameters(mode, baseSnapshotInterval,intervalCompressionMultiplier);
+                setHistoryParameters(mode, baseSnapshotInterval, intervalCompressionMultiplier);
                 return;
-            } catch (NumberFormatException ignored) {}
+            } catch (NumberFormatException ignored) { }
         }
         Slog.w(LOG_TAG, "Bad value for" + Settings.Global.APPOP_HISTORY_PARAMETERS
                 + "=" + setting + " resetting!");
     }
 
-    void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage,
-            @Nullable String filterAttributionTag, int filterOp,
+
+    @Override
+    public void dump(String prefix, PrintWriter pw, int filterUid,
+            @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp,
             @HistoricalOpsRequestFilter int filter) {
         synchronized (mOnDiskLock) {
             synchronized (mInMemoryLock) {
@@ -366,7 +369,8 @@
         }
     }
 
-    void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter,
+    @Override
+    public void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter,
             @Nullable String packageNameFilter, @Nullable String attributionTagFilter,
             @HistoricalOpsRequestFilter int filter, int dumpOp,
             @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
@@ -381,7 +385,8 @@
         }
     }
 
-    void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName,
+    @Override
+    public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName,
             @Nullable String attributionTag, @Nullable String[] opNames,
             @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter,
             long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
@@ -414,11 +419,12 @@
         callback.sendResult(payload);
     }
 
-    void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag,
-            @Nullable String[] opNames, @OpHistoryFlags int historyFlags,
-            @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
-            @OpFlags int flags, @Nullable String[] attributionExemptPkgs,
-            @NonNull RemoteCallback callback) {
+    @Override
+    public void getHistoricalOps(int uid, @Nullable String packageName,
+            @Nullable String attributionTag, @Nullable String[] opNames,
+            @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter,
+            long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
+            @Nullable String[] attributionExemptPkgs, @NonNull RemoteCallback callback) {
         final long currentTimeMillis = System.currentTimeMillis();
         if (endTimeMillis == Long.MAX_VALUE) {
             endTimeMillis = currentTimeMillis;
@@ -493,11 +499,12 @@
         callback.sendResult(payload);
     }
 
-    void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
+    @Override
+    public void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
             @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
             @OpFlags int flags, long accessTime,
             @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
-            @DiscreteOpsRegistry.AccessType int accessType, int accessCount) {
+            int accessCount) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -510,12 +517,13 @@
 
                 mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
                         attributionTag, flags, uidState, accessTime, -1, attributionFlags,
-                        attributionChainId, accessType);
+                        attributionChainId);
             }
         }
     }
 
-    void incrementOpRejected(int op, int uid, @NonNull String packageName,
+    @Override
+    public void incrementOpRejectedCount(int op, int uid, @NonNull String packageName,
             @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
@@ -530,11 +538,11 @@
         }
     }
 
-    void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
+    @Override
+    public void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
             @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
             @OpFlags int flags, long eventStartTime, long increment,
-            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
-            @DiscreteOpsRegistry.AccessType int accessType) {
+            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -546,12 +554,13 @@
                         attributionTag, uidState, flags, increment);
                 mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op,
                         attributionTag, flags, uidState, eventStartTime, increment,
-                        attributionFlags, attributionChainId, accessType);
+                        attributionFlags, attributionChainId);
             }
         }
     }
 
-    void setHistoryParameters(@HistoricalMode int mode,
+    @Override
+    public void setHistoryParameters(@HistoricalMode int mode,
             long baseSnapshotInterval, long intervalCompressionMultiplier) {
         synchronized (mOnDiskLock) {
             synchronized (mInMemoryLock) {
@@ -586,7 +595,8 @@
         }
     }
 
-    void offsetHistory(long offsetMillis) {
+    @Override
+    public void offsetHistory(long offsetMillis) {
         synchronized (mOnDiskLock) {
             synchronized (mInMemoryLock) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -608,13 +618,11 @@
                 mPersistence.persistHistoricalOpsDLocked(history);
             }
         }
-    }
-
-    void offsetDiscreteHistory(long offsetMillis) {
         mDiscreteRegistry.offsetHistory(offsetMillis);
     }
 
-    void addHistoricalOps(HistoricalOps ops) {
+    @Override
+    public void addHistoricalOps(HistoricalOps ops) {
         final List<HistoricalOps> pendingWrites;
         synchronized (mInMemoryLock) {
             if (!isPersistenceInitializedMLocked()) {
@@ -635,7 +643,8 @@
         offsetHistory(offsetMillis);
     }
 
-    void resetHistoryParameters() {
+    @Override
+    public void resetHistoryParameters() {
         if (!isPersistenceInitializedMLocked()) {
             Slog.d(LOG_TAG, "Interaction before persistence initialized");
             return;
@@ -645,7 +654,8 @@
         mDiscreteRegistry.setDebugMode(false);
     }
 
-    void clearHistory(int uid, String packageName) {
+    @Override
+    public void clearHistory(int uid, String packageName) {
         synchronized (mOnDiskLock) {
             synchronized (mInMemoryLock) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -669,11 +679,13 @@
         mDiscreteRegistry.clearHistory(uid, packageName);
     }
 
-    void writeAndClearDiscreteHistory() {
+    @Override
+    public void writeAndClearDiscreteHistory() {
         mDiscreteRegistry.writeAndClearOldAccessHistory();
     }
 
-    void clearAllHistory() {
+    @Override
+    public void clearAllHistory() {
         clearHistoricalRegistry();
         mDiscreteRegistry.clearHistory();
     }
@@ -742,7 +754,8 @@
         return mCurrentHistoricalOps;
     }
 
-    void shutdown() {
+    @Override
+    public void shutdown() {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) {
                 return;
@@ -753,7 +766,8 @@
         mDiscreteRegistry.shutdown();
     }
 
-    void persistPendingHistory() {
+    @Override
+    public void persistPendingHistory() {
         final List<HistoricalOps> pendingWrites;
         synchronized (mOnDiskLock) {
             synchronized (mInMemoryLock) {
@@ -791,7 +805,7 @@
 
     private void schedulePersistHistoricalOpsMLocked(@NonNull HistoricalOps ops) {
         final Message message = PooledLambda.obtainMessage(
-                HistoricalRegistry::persistPendingHistory, HistoricalRegistry.this);
+                LegacyHistoricalRegistry::persistPendingHistory, LegacyHistoricalRegistry.this);
         message.what = MSG_WRITE_PENDING_HISTORY;
         IoThread.getHandler().sendMessage(message);
         mPendingWrites.offerFirst(ops);
@@ -799,7 +813,7 @@
 
     private static void makeRelativeToEpochStart(@NonNull HistoricalOps ops, long nowMillis) {
         ops.setBeginAndEndTime(nowMillis - ops.getEndTimeMillis(),
-                nowMillis- ops.getBeginTimeMillis());
+                nowMillis - ops.getBeginTimeMillis());
     }
 
     private void pruneFutureOps(@NonNull List<HistoricalOps> ops) {
@@ -965,7 +979,7 @@
                     final HistoricalOps readOp = readOps.get(i);
                     currentOps.merge(readOp);
                 }
-             }
+            }
         }
 
         private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(int filterUid,
@@ -1111,7 +1125,7 @@
                 if (existingOpCount > 0) {
                     // Compute elapsed time
                     final long elapsedTimeMillis = passedOps.get(passedOps.size() - 1)
-                        .getEndTimeMillis();
+                            .getEndTimeMillis();
                     for (int i = 0; i < existingOpCount; i++) {
                         final HistoricalOps existingOp = existingOps.get(i);
                         existingOp.offsetBeginAndEndTime(elapsedTimeMillis);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 8ef79a91..4b5f06b 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1472,8 +1472,8 @@
         mAudioService.postAccessoryPlugMediaUnmute(device);
     }
 
-    /*package*/ int getVssVolumeForDevice(int streamType, int device) {
-        return mAudioService.getVssVolumeForDevice(streamType, device);
+    /*package*/ int getVolumeForDeviceIgnoreMute(int streamType, int device) {
+        return mAudioService.getVolumeForDeviceIgnoreMute(streamType, device);
     }
 
     /*package*/ int getMaxVssVolumeForStream(int streamType) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 829d9ea..2e6d984 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -2482,7 +2482,7 @@
     @GuardedBy("mDevicesLock")
     private void makeHearingAidDeviceAvailable(
             String address, String name, int streamType, String eventSource) {
-        final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
+        final int hearingAidVolIndex = mDeviceBroker.getVolumeForDeviceIgnoreMute(streamType,
                 DEVICE_OUT_HEARING_AID);
         mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
 
@@ -2672,7 +2672,7 @@
             }
 
             final int leAudioVolIndex = (volumeIndex == -1)
-                    ? mDeviceBroker.getVssVolumeForDevice(streamType, device)
+                    ? mDeviceBroker.getVolumeForDeviceIgnoreMute(streamType, device)
                     : volumeIndex;
             final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType);
             mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType);
diff --git a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
index 7502664..180ef85 100644
--- a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
+++ b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
@@ -39,6 +39,7 @@
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.IntArray;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.media.permission.INativePermissionController;
@@ -62,6 +63,8 @@
 /** Responsible for synchronizing system server permission state to the native audioserver. */
 public class AudioServerPermissionProvider {
 
+    static final String TAG = "AudioServerPermissionProvider";
+
     static final String[] MONITORED_PERMS = new String[PermissionEnum.ENUM_SIZE];
 
     static final byte[] HDS_PERMS = new byte[] {PermissionEnum.CAPTURE_AUDIO_HOTWORD,
@@ -219,10 +222,13 @@
     public void setIsolatedServiceUid(int uid, int owningUid) {
         synchronized (mLock) {
             if (mHdsUid == uid) return;
-            var packageNameSet = mPackageMap.get(owningUid);
-            if (packageNameSet == null) return;
-            var packageName = packageNameSet.iterator().next();
-            onModifyPackageState(uid, packageName, /* isRemove= */ false);
+            var packageNameSet = mPackageMap.get(UserHandle.getAppId(owningUid));
+            if (packageNameSet != null) {
+                var packageName = packageNameSet.iterator().next();
+                onModifyPackageState(uid, packageName, /* isRemove= */ false);
+            } else {
+                Log.wtf(TAG, "setIsolatedService owning uid not found");
+            }
             // permissions
             mHdsUid = uid;
             if (mDest == null) {
@@ -249,11 +255,19 @@
 
     public void clearIsolatedServiceUid(int uid) {
         synchronized (mLock) {
-            if (mHdsUid != uid) return;
-            var packageNameSet = mPackageMap.get(uid);
-            if (packageNameSet == null) return;
-            var packageName = packageNameSet.iterator().next();
-            onModifyPackageState(uid, packageName, /* isRemove= */ true);
+            var packageNameSet = mPackageMap.get(UserHandle.getAppId(uid));
+            if (mHdsUid != uid) {
+                Log.wtf(TAG,
+                        "Unexpected isolated service uid cleared: " + uid + packageNameSet
+                                + ", expected " + mHdsUid);
+                return;
+            }
+            if (packageNameSet != null) {
+                var packageName = packageNameSet.iterator().next();
+                onModifyPackageState(uid, packageName, /* isRemove= */ true);
+            } else {
+                Log.wtf(TAG, "clearIsolatedService uid not found");
+            }
             // permissions
             if (mDest == null) {
                 mIsUpdateDeferred = true;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ada1cd7..bf7f194 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -32,7 +32,6 @@
 import static android.Manifest.permission.WRITE_SETTINGS;
 import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT;
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 import static android.content.Intent.EXTRA_ARCHIVAL;
 import static android.content.Intent.EXTRA_REPLACING;
 import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
@@ -529,7 +528,7 @@
      */
     private InputDeviceVolumeHelper mInputDeviceVolumeHelper;
 
-    /*package*/ int getVssVolumeForDevice(int stream, int device) {
+    /*package*/ int getVolumeForDeviceIgnoreMute(int stream, int device) {
         final VolumeStreamState streamState = mStreamStates.get(stream);
         return streamState != null ? streamState.getIndex(device) : -1;
     }
@@ -3336,11 +3335,25 @@
     }
 
     private int rescaleIndex(int index, int srcStream, int dstStream) {
-        return rescaleIndex(index,
-                getVssForStreamOrDefault(srcStream).getMinIndex(),
-                getVssForStreamOrDefault(srcStream).getMaxIndex(),
-                getVssForStreamOrDefault(dstStream).getMinIndex(),
-                getVssForStreamOrDefault(dstStream).getMaxIndex());
+        final VolumeStreamState srcVss = getVssForStreamOrDefault(srcStream);
+        final VolumeStreamState dstVss = getVssForStreamOrDefault(dstStream);
+        int newIndex = rescaleIndex(index, srcVss.getMinIndex(), srcVss.getMaxIndex(),
+                dstVss.getMinIndex(), dstVss.getMaxIndex());
+        // only apply solution for DTMF stream to make sure that it is not muted when
+        // re-aliasing to voice call stream. With ringMyCar flag enabled this will be
+        // automatically solved since we are sending the mute state to APM
+        // TODO(b/402542630): revisit stream aliasing logic with different min index
+        //  values / mute states
+        if (!ringMyCar() && dstStream == AudioSystem.STREAM_DTMF
+                && srcStream == AudioSystem.STREAM_VOICE_CALL
+                && srcVss.getMinIndex() > dstVss.getMinIndex()) {
+            newIndex += srcVss.getMinIndex() - dstVss.getMinIndex();
+            if (newIndex > dstVss.getMaxIndex()) {
+                newIndex = dstVss.getMaxIndex();
+            }
+        }
+
+        return newIndex;
     }
 
     private int rescaleIndex(int index, int srcMin, int srcMax, int dstMin, int dstMax) {
@@ -4997,6 +5010,8 @@
         pw.println("\tcom.android.media.audio.disablePrescaleAbsoluteVolume:"
                 + disablePrescaleAbsoluteVolume());
         pw.println("\tcom.android.media.audio.setStreamVolumeOrder - EOL");
+        pw.println("\tandroid.media.audio.ringtoneUserUriCheck:"
+                + android.media.audio.Flags.ringtoneUserUriCheck());
         pw.println("\tandroid.media.audio.roForegroundAudioControl:"
                 + roForegroundAudioControl());
         pw.println("\tandroid.media.audio.scoManagedByAudio:"
@@ -5098,7 +5113,7 @@
         }
 
         final int device = absVolumeDevices.toArray(new Integer[0])[0].intValue();
-        final int index = getStreamVolume(streamType, device);
+        final int index = (getVolumeForDeviceIgnoreMute(streamType, device) + 5) / 10;
 
         if (DEBUG_VOL) {
             Slog.i(TAG, "onUpdateContextualVolumes streamType: " + streamType
@@ -12927,11 +12942,12 @@
                 );
         audioPolicy.registerOnStartTask(() -> {
             provider.onServiceStart(audioPolicy.getPermissionController());
+            sLifecycleLogger.enqueue(new EventLogger.StringEvent(
+                    "Controller start task complete").printLog(ALOGI, TAG));
         });
 
         IntentFilter packageUpdateFilter = new IntentFilter();
         packageUpdateFilter.addAction(ACTION_PACKAGE_ADDED);
-        packageUpdateFilter.addAction(ACTION_PACKAGE_REMOVED);
         packageUpdateFilter.addDataScheme("package");
 
         context.registerReceiverForAllUsers(new BroadcastReceiver() {
@@ -12945,9 +12961,6 @@
                 if (ACTION_PACKAGE_ADDED.equals(action)) {
                     audioserverExecutor.execute(() ->
                             provider.onModifyPackageState(uid, pkgName, false /* isRemoved */));
-                } else if (ACTION_PACKAGE_REMOVED.equals(action)) {
-                    audioserverExecutor.execute(() ->
-                            provider.onModifyPackageState(uid, pkgName, true /* isRemoved */));
                 }
             }
         }, packageUpdateFilter, null, null); // main thread is fine, since dispatch on executor
@@ -15107,6 +15120,61 @@
 
     /**
      * @hide
+     * Returns the current audio output device delay value of key in milliseconds.
+     *
+     * In aidl implementation, the param of getParameters is "key" and reply delay of all supported
+     * devices which be composited as "key=device,address,delay|device,address,delay|...".
+     * e.g.
+     * param: additional_output_device_delay=
+     * reply: additional_output_device_delay=2,,0|4,,0|8,,0|128,,0|256,,0|512,,0|1024,,0|8192,,0|
+     * 16384,,0|262144,,0|262145,,0|524288,,0|67108864,,0|134217728,,0|536870912,,0|536870913,,0|
+     * 536870914,,0
+     *
+     * In hidl implementation, the param of getParameters is "key=device,address" and reply a
+     * specific device delay which be composited as "key=device,address,delay".
+     * e.g.
+     * param: additional_output_device_delay=2,
+     * reply: additional_output_device_delay=2,,0
+     *
+     * @param key
+     * @param deviceType
+     * @param address
+     * @return the delay value of key. This is a non-negative number.
+     *     {@code 0} is returned if unsupported.
+     */
+    private long getDelayByKeyDevice(@NonNull String key, @NonNull AudioDeviceAttributes device) {
+        long delayMillis = 0;
+
+        try {
+            if (AudioHalVersionInfo.AUDIO_HAL_TYPE_AIDL == getHalVersion().getHalType()) {
+                final String reply = AudioSystem.getParameters(key);
+                final String keyDeviceAddressPrefix =
+                    Integer.toUnsignedString(device.getInternalType()) + "," + device.getAddress() +
+                    ",";
+                int start = reply.indexOf(keyDeviceAddressPrefix);
+                int end = -1;
+                if (start != -1) {
+                    start += keyDeviceAddressPrefix.length();
+                    end = reply.indexOf("|", start);
+                    delayMillis = Long.parseLong(
+                            end == -1 ? reply.substring(start) : reply.substring(start, end));
+                }
+            } else {
+                final String reply = AudioSystem.getParameters(
+                    key + "=" + device.getInternalType() + "," + device.getAddress());
+                if (reply.contains(key)) {
+                    delayMillis = Long.parseLong(reply.substring(key.length() + 1));
+                }
+            }
+        } catch (NullPointerException e) {
+            Log.w(TAG, "NullPointerException when getting delay for device " + device, e);
+        }
+
+        return delayMillis;
+    }
+
+    /**
+     * @hide
      * Returns the current additional audio output device delay in milliseconds.
      *
      * @param deviceType
@@ -15122,17 +15190,8 @@
         device = retrieveBluetoothAddress(device);
 
         final String key = "additional_output_device_delay";
-        final String reply = AudioSystem.getParameters(
-                key + "=" + device.getInternalType() + "," + device.getAddress());
-        long delayMillis = 0;
-        if (reply.contains(key)) {
-            try {
-                delayMillis = Long.parseLong(reply.substring(key.length() + 1));
-            } catch (NullPointerException e) {
-                delayMillis = 0;
-            }
-        }
-        return delayMillis;
+
+        return getDelayByKeyDevice(key, device);
     }
 
     /**
@@ -15154,17 +15213,8 @@
         device = retrieveBluetoothAddress(device);
 
         final String key = "max_additional_output_device_delay";
-        final String reply = AudioSystem.getParameters(
-                key + "=" + device.getInternalType() + "," + device.getAddress());
-        long delayMillis = 0;
-        if (reply.contains(key)) {
-            try {
-                delayMillis = Long.parseLong(reply.substring(key.length() + 1));
-            } catch (NullPointerException e) {
-                delayMillis = 0;
-            }
-        }
-        return delayMillis;
+
+        return getDelayByKeyDevice(key, device);
     }
 
     @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 643f330..67afff7 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -724,7 +724,7 @@
                 int device = mAudioService.getDeviceForStream(AudioSystem.STREAM_MUSIC);
                 if (safeDevicesContains(device) && isStreamActive) {
                     scheduleMusicActiveCheck();
-                    int index = mAudioService.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC,
+                    int index = mAudioService.getVolumeForDeviceIgnoreMute(AudioSystem.STREAM_MUSIC,
                             device);
                     if (index > safeMediaVolumeIndex(device)) {
                         // Approximate cumulative active music time
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ac0892b..aa98590 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1113,7 +1113,11 @@
             }
             // Remove always-on VPN if it's not supported.
             if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
-                setAlwaysOnPackage(null, false, null);
+                // Do not remove the always-on setting due to the restricted ability in safe mode.
+                // The always-on VPN can then start after the device reboots to normal mode.
+                if (!mContext.getPackageManager().isSafeMode()) {
+                    setAlwaysOnPackage(null, false, null);
+                }
                 return false;
             }
             // Skip if the service is already established. This isn't bulletproof: it's not bound
diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java
index ddea285..1d70953 100644
--- a/services/core/java/com/android/server/display/DisplayControl.java
+++ b/services/core/java/com/android/server/display/DisplayControl.java
@@ -29,7 +29,7 @@
  */
 public class DisplayControl {
     private static native IBinder nativeCreateVirtualDisplay(String name, boolean secure,
-            String uniqueId, float requestedRefreshRate);
+            boolean optimizeForPower, String uniqueId, float requestedRefreshRate);
     private static native void nativeDestroyVirtualDisplay(IBinder displayToken);
     private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
     private static native long[] nativeGetPhysicalDisplayIds();
@@ -49,7 +49,7 @@
      */
     public static IBinder createVirtualDisplay(String name, boolean secure) {
         Objects.requireNonNull(name, "name must not be null");
-        return nativeCreateVirtualDisplay(name, secure, "", 0.0f);
+        return nativeCreateVirtualDisplay(name, secure, true, "", 0.0f);
     }
 
     /**
@@ -57,6 +57,10 @@
      *
      * @param name The name of the virtual display.
      * @param secure Whether this display is secure.
+     * @param optimizeForPower Whether SurfaceFlinger should optimize for power (instead of
+     *                         performance). Such displays will depend on another display for it to
+     *                         be shown and rendered, and that display will optimize for
+     *                         performance when it is on.
      * @param uniqueId The unique ID for the display.
      * @param requestedRefreshRate The requested refresh rate in frames per second.
      * For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on
@@ -66,10 +70,11 @@
      * @return The token reference for the display in SurfaceFlinger.
      */
     public static IBinder createVirtualDisplay(String name, boolean secure,
-            String uniqueId, float requestedRefreshRate) {
+            boolean optimizeForPower, String uniqueId, float requestedRefreshRate) {
         Objects.requireNonNull(name, "name must not be null");
         Objects.requireNonNull(uniqueId, "uniqueId must not be null");
-        return nativeCreateVirtualDisplay(name, secure, uniqueId, requestedRefreshRate);
+        return nativeCreateVirtualDisplay(name, secure, optimizeForPower, uniqueId,
+                requestedRefreshRate);
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 3aaf4f6..7450dff 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -122,8 +122,8 @@
     public static final int FLAG_MASK_DISPLAY_CUTOUT = 1 << 11;
 
     /**
-     * Flag: This flag identifies secondary displays that should show system decorations, such as
-     * navigation bar, home activity or wallpaper.
+     * Flag: This flag identifies secondary displays that should always show system decorations,
+     * such as navigation bar, home activity or wallpaper.
      * <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
      * @hide
      */
@@ -191,6 +191,19 @@
     public static final int FLAG_STEAL_TOP_FOCUS_DISABLED = 1 << 19;
 
     /**
+     * Flag: Indicates that the display is allowed to switch the content mode between
+     * projected/extended and mirroring. This allows the display to dynamically add or remove the
+     * home and system decorations.
+     *
+     * Note that this flag should not be enabled with any of {@link #FLAG_PRIVATE},
+     * {@link #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}, or {@link #FLAG_OWN_CONTENT_ONLY} at the
+     * same time; otherwise it will be ignored.
+     *
+     * @hide
+     */
+    public static final int FLAG_ALLOWS_CONTENT_MODE_SWITCH = 1 << 20;
+
+    /**
      * Touch attachment: Display does not receive touch.
      */
     public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index f73b66c..ce8d8a6 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import android.util.IndentingPrintWriter;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -97,4 +99,14 @@
         }
         return displayIds;
     }
+
+    /** Dumps information about the DisplayGroup. */
+    void dumpLocked(IndentingPrintWriter ipw) {
+        final int numDisplays = mDisplays.size();
+        for (int i = 0; i < numDisplays; i++) {
+            LogicalDisplay logicalDisplay = mDisplays.get(i);
+            ipw.println("Display " + logicalDisplay.getDisplayIdLocked() + " "
+                    + logicalDisplay.getPrimaryDisplayDeviceLocked());
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 7016c11..a28069b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -674,9 +674,9 @@
         mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
         mExtraDisplayLoggingPackageName = DisplayProperties.debug_vri_package().orElse(null);
         mExtraDisplayEventLogging = !TextUtils.isEmpty(mExtraDisplayLoggingPackageName);
-
+        // TODO(b/400384229): stats service needs to react to mirror-extended switch
         mExternalDisplayStatsService = new ExternalDisplayStatsService(mContext, mHandler,
-                this::isExtendedDisplayEnabled);
+                this::isExtendedDisplayAllowed);
         mDisplayNotificationManager = new DisplayNotificationManager(mFlags, mContext,
                 mExternalDisplayStatsService);
         mExternalDisplayPolicy = new ExternalDisplayPolicy(new ExternalDisplayPolicyInjector());
@@ -690,7 +690,7 @@
                         deliverTopologyUpdate(update.first);
                     };
             mDisplayTopologyCoordinator = new DisplayTopologyCoordinator(
-                    this::isExtendedDisplayEnabled, topologyChangedCallback,
+                    this::isExtendedDisplayAllowed, topologyChangedCallback,
                     new HandlerExecutor(mHandler), mSyncRoot, backupManager::dataChanged);
         } else {
             mDisplayTopologyCoordinator = null;
@@ -2411,7 +2411,10 @@
         updateLogicalDisplayState(display);
     }
 
-    private boolean isExtendedDisplayEnabled() {
+    private boolean isExtendedDisplayAllowed() {
+        if (mFlags.isDisplayContentModeManagementEnabled()) {
+            return true;
+        }
         try {
             return 0 != Settings.Global.getInt(
                     mContext.getContentResolver(),
@@ -6045,7 +6048,13 @@
                 return;
             }
             if (inTopology) {
-                mDisplayTopologyCoordinator.onDisplayAdded(getDisplayInfo(displayId));
+                var info = getDisplayInfo(displayId);
+                if (info == null) {
+                    Slog.w(TAG, "onDisplayBelongToTopologyChanged: cancelled displayId="
+                            + displayId + " info=null");
+                    return;
+                }
+                mDisplayTopologyCoordinator.onDisplayAdded(info);
             } else {
                 mDisplayTopologyCoordinator.onDisplayRemoved(displayId);
             }
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 997fff5..b4df1f7 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -69,9 +69,9 @@
     private final SparseArray<String> mDisplayIdToUniqueIdMapping = new SparseArray<>();
 
     /**
-     * Check if extended displays are enabled. If not, a topology is not needed.
+     * Check if extended displays are allowed. If not, a topology is not needed.
      */
-    private final BooleanSupplier mIsExtendedDisplayEnabled;
+    private final BooleanSupplier mIsExtendedDisplayAllowed;
 
     /**
      * Callback used to send topology updates.
@@ -83,21 +83,21 @@
     private final DisplayManagerService.SyncRoot mSyncRoot;
     private final Runnable mTopologySavedCallback;
 
-    DisplayTopologyCoordinator(BooleanSupplier isExtendedDisplayEnabled,
+    DisplayTopologyCoordinator(BooleanSupplier isExtendedDisplayAllowed,
             Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback,
             Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot,
             Runnable topologySavedCallback) {
-        this(new Injector(), isExtendedDisplayEnabled, onTopologyChangedCallback,
+        this(new Injector(), isExtendedDisplayAllowed, onTopologyChangedCallback,
                 topologyChangeExecutor, syncRoot, topologySavedCallback);
     }
 
     @VisibleForTesting
-    DisplayTopologyCoordinator(Injector injector, BooleanSupplier isExtendedDisplayEnabled,
+    DisplayTopologyCoordinator(Injector injector, BooleanSupplier isExtendedDisplayAllowed,
             Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> onTopologyChangedCallback,
             Executor topologyChangeExecutor, DisplayManagerService.SyncRoot syncRoot,
             Runnable topologySavedCallback) {
         mTopology = injector.getTopology();
-        mIsExtendedDisplayEnabled = isExtendedDisplayEnabled;
+        mIsExtendedDisplayAllowed = isExtendedDisplayAllowed;
         mOnTopologyChangedCallback = onTopologyChangedCallback;
         mTopologyChangeExecutor = topologyChangeExecutor;
         mSyncRoot = syncRoot;
@@ -262,9 +262,9 @@
             return false;
         }
         if ((info.type == Display.TYPE_EXTERNAL || info.type == Display.TYPE_OVERLAY)
-                && !mIsExtendedDisplayEnabled.getAsBoolean()) {
+                && !mIsExtendedDisplayAllowed.getAsBoolean()) {
             Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because "
-                    + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayEnabled");
+                    + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayAllowed");
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7b714ad..2cad7ed 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -766,7 +766,7 @@
                         mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
                     }
                 } else {
-                    if (!res.getBoolean(R.bool.config_localDisplaysMirrorContent)) {
+                    if (shouldOwnContentOnly()) {
                         mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
                     }
 
@@ -780,6 +780,15 @@
                     }
                 }
 
+                if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+                    // Public display with FLAG_OWN_CONTENT_ONLY disabled is allowed to switch the
+                    // content mode.
+                    if (mIsFirstDisplay
+                            || (!isDisplayPrivate(physicalAddress) && !shouldOwnContentOnly())) {
+                        mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+                    }
+                }
+
                 if (DisplayCutout.getMaskBuiltInDisplayCutout(res, mInfo.uniqueId)) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
                 }
@@ -822,6 +831,7 @@
                                 R.string.display_manager_hdmi_display_name);
                     }
                 }
+
                 mInfo.frameRateOverrides = mFrameRateOverrides;
 
                 // The display is trusted since it is created by system.
@@ -1467,6 +1477,11 @@
             return false;
         }
 
+        private boolean shouldOwnContentOnly() {
+            final Resources res = getOverlayContext().getResources();
+            return !res.getBoolean(R.bool.config_localDisplaysMirrorContent);
+        }
+
         private boolean isDisplayStealTopFocusDisabled(DisplayAddress.Physical physicalAddress) {
             if (physicalAddress == null) {
                 return false;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index b2b9ef1..0e6870f 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -489,6 +489,11 @@
             if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_STEAL_TOP_FOCUS_DISABLED) != 0) {
                 mBaseDisplayInfo.flags |= Display.FLAG_STEAL_TOP_FOCUS_DISABLED;
             }
+            // Rear display should not be allowed to use the content mode switch.
+            if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0
+                    && mDevicePosition != Layout.Display.POSITION_REAR) {
+                mBaseDisplayInfo.flags |= Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+            }
             Rect maskingInsets = getMaskingInsets(deviceInfo);
             int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
             int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
@@ -1155,6 +1160,7 @@
         pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing);
         pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides));
         pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids);
+        pw.println("mDisplayGroupId=" + mDisplayGroupId);
         pw.println("mDisplayGroupName=" + mDisplayGroupName);
         pw.println("mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
         pw.println("mLeadDisplayId=" + mLeadDisplayId);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index f4daf87..4a4c616 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -478,6 +478,21 @@
             ipw.decreaseIndent();
             ipw.println();
         }
+
+        final int displayGroupCount = mDisplayGroups.size();
+        ipw.println();
+        ipw.println("Display Groups: size=" + displayGroupCount);
+        for (int i = 0; i < displayGroupCount; i++) {
+            int groupId = mDisplayGroups.keyAt(i);
+            DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
+            ipw.println("Group " + groupId + ":");
+            ipw.increaseIndent();
+            displayGroup.dumpLocked(ipw);
+            ipw.decreaseIndent();
+            ipw.println();
+        }
+
+
         mDeviceStateToLayoutMap.dumpLocked(ipw);
     }
 
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index b5a9b19..60b7fca 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -76,6 +76,7 @@
  * <li><code>secure</code>: creates a secure display</li>
  * <li><code>own_content_only</code>: only shows this display's own content</li>
  * <li><code>should_show_system_decorations</code>: supports system decorations</li>
+ * <li><code>fixed_content_mode</code>: not allowed to switch content mode</li>
  * <li><code>gravity_top_left</code>: display the overlay at the top left of the screen</li>
  * <li><code>gravity_top_right</code>: display the overlay at the top right of the screen</li>
  * <li><code>gravity_bottom_right</code>: display the overlay at the bottom right of the screen</li>
@@ -117,6 +118,18 @@
     private static final String OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS =
             "should_show_system_decorations";
 
+    /**
+     * When this flag is set, the overlay display is not allowed to switch content mode.
+     * Note that it is the opposite of {@link  DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH},
+     * because we want overlay displays (such as those used for connected display simulation in
+     * development) to have {@link  DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH} enabled by
+     * default without explicitly specifying it.
+     *
+     * @see DisplayDeviceInfo#FLAG_ALLOWS_CONTENT_MODE_SWITCH
+     */
+    private static final String OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE =
+            "fixed_content_mode";
+
     // Gravity flags to decide where the overlay should be shown.
     private static final String GRAVITY_TOP_LEFT = "gravity_top_left";
     private static final String GRAVITY_BOTTOM_RIGHT = "gravity_bottom_right";
@@ -384,6 +397,17 @@
                 if (mFlags.mShouldShowSystemDecorations) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
                 }
+                if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+                    if (!mFlags.mFixedContentMode
+                            && !mFlags.mOwnContentOnly
+                            && !mFlags.mShouldShowSystemDecorations) {
+                        // For overlay displays, if FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS and
+                        // FLAG_OWN_CONTENT_ONLY are both disabled,
+                        // then FLAG_ALLOWS_CONTENT_MODE_SWITCH should be enabled by default,
+                        // unless OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE is set.
+                        mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+                    }
+                }
                 mInfo.type = Display.TYPE_OVERLAY;
                 mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
                 mInfo.state = mState;
@@ -628,16 +652,21 @@
         /** See {@link #OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}. */
         final boolean mShouldShowSystemDecorations;
 
+        /** See {@link #OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE}. */
+        final boolean mFixedContentMode;
+
         final int mGravity;
 
         OverlayFlags(
                 boolean secure,
                 boolean ownContentOnly,
                 boolean shouldShowSystemDecorations,
+                boolean fixedContentMode,
                 int gravity) {
             mSecure = secure;
             mOwnContentOnly = ownContentOnly;
             mShouldShowSystemDecorations = shouldShowSystemDecorations;
+            mFixedContentMode = fixedContentMode;
             mGravity = gravity;
         }
 
@@ -647,12 +676,14 @@
                         false /* secure */,
                         false /* ownContentOnly */,
                         false /* shouldShowSystemDecorations */,
+                        false /* fixedContentMode */,
                         Gravity.NO_GRAVITY);
             }
 
             boolean secure = false;
             boolean ownContentOnly = false;
             boolean shouldShowSystemDecorations = false;
+            boolean fixedContentMode = false;
             int gravity = Gravity.NO_GRAVITY;
             for (String flag: flagString.split(FLAG_SPLITTER)) {
                 if (OVERLAY_DISPLAY_FLAG_SECURE.equals(flag)) {
@@ -661,11 +692,14 @@
                     ownContentOnly = true;
                 } else if (OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS.equals(flag)) {
                     shouldShowSystemDecorations = true;
+                } else if (OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE.equals(flag)) {
+                    fixedContentMode = true;
                 } else {
                     gravity = parseOverlayGravity(flag);
                 }
             }
-            return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations, gravity);
+            return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations,
+                    fixedContentMode, gravity);
         }
 
         @Override
@@ -674,6 +708,7 @@
                     .append("secure=").append(mSecure)
                     .append(", ownContentOnly=").append(mOwnContentOnly)
                     .append(", shouldShowSystemDecorations=").append(mShouldShowSystemDecorations)
+                    .append(", fixedContentMode=").append(mFixedContentMode)
                     .append(", gravity").append(Gravity.toString(mGravity))
                     .append("}")
                     .toString();
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index ac03a93..c2eac86 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -103,10 +103,10 @@
             Context context, Handler handler, Listener listener, DisplayManagerFlags featureFlags) {
         this(syncRoot, context, handler, listener, new SurfaceControlDisplayFactory() {
             @Override
-            public IBinder createDisplay(String name, boolean secure, String uniqueId,
-                                         float requestedRefreshRate) {
-                return DisplayControl.createVirtualDisplay(name, secure, uniqueId,
-                                                           requestedRefreshRate);
+            public IBinder createDisplay(String name, boolean secure, boolean optimizeForPower,
+                    String uniqueId, float requestedRefreshRate) {
+                return DisplayControl.createVirtualDisplay(name, secure, optimizeForPower, uniqueId,
+                        requestedRefreshRate);
             }
 
             @Override
@@ -182,9 +182,13 @@
 
         String name = virtualDisplayConfig.getName();
         boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
+        boolean neverBlank = isNeverBlank(flags);
 
-        IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure, uniqueId,
-                virtualDisplayConfig.getRequestedRefreshRate());
+        // Never-blank displays are considered to be dependent on another display to be rendered.
+        // As a result, such displays should optimize for power instead of performance when it is
+        // powered on.
+        IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure, neverBlank,
+                uniqueId, virtualDisplayConfig.getRequestedRefreshRate());
         MediaProjectionCallback mediaProjectionCallback =  null;
         if (projection != null) {
             mediaProjectionCallback = new MediaProjectionCallback(appToken);
@@ -318,6 +322,12 @@
         return mVirtualDisplayDevices.remove(appToken);
     }
 
+    private static boolean isNeverBlank(int flags) {
+        // Private non-mirror displays are never blank and always on.
+        return (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0
+                && (flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0;
+    }
+
     private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient {
         private static final int PENDING_SURFACE_CHANGE = 0x01;
         private static final int PENDING_RESIZE = 0x02;
@@ -377,9 +387,7 @@
             mCallback = callback;
             mProjection = projection;
             mMediaProjectionCallback = mediaProjectionCallback;
-            // Private non-mirror displays are never blank and always on.
-            mNeverBlank = (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0
-                    && (flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0;
+            mNeverBlank = isNeverBlank(flags);
             if (android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState()
                     && !mNeverBlank) {
                 // The display's power state depends on the power state of the state of its
@@ -782,6 +790,10 @@
          *
          * @param name The name of the display.
          * @param secure Whether this display is secure.
+         * @param optimizeForPower Whether SurfaceFlinger should optimize for power (instead of
+         *                         performance). Such displays will depend on another display for
+         *                         it to be shown and rendered, and that display will optimize for
+         *                         performance when it is on.
          * @param uniqueId The unique ID for the display.
          * @param requestedRefreshRate
          *     The refresh rate, frames per second, to request on the virtual display.
@@ -791,8 +803,8 @@
          *     the refresh rate of the leader physical display.
          * @return The token reference for the display in SurfaceFlinger.
          */
-        IBinder createDisplay(String name, boolean secure, String uniqueId,
-                              float requestedRefreshRate);
+        IBinder createDisplay(String name, boolean secure, boolean optimizeForPower,
+                String uniqueId, float requestedRefreshRate);
 
         /**
          * Destroy a display in SurfaceFlinger.
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 902eefa..89679c7 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -666,6 +666,12 @@
                 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
                 // The display is trusted since it is created by system.
                 mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
+                if (getFeatureFlags().isDisplayContentModeManagementEnabled()) {
+                    // The wifi display is allowed to switch content mode since FLAG_PRIVATE,
+                    // FLAG_OWN_CONTENT_ONLY, and FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS are not
+                    // enabled in WifiDisplayDevice#getDisplayDeviceInfoLocked().
+                    mInfo.flags |= DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+                }
                 mInfo.displayShape =
                         DisplayShape.createDefaultDisplayShape(mInfo.width, mInfo.height, false);
             }
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 7cc178d..f5228df 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -280,6 +280,11 @@
             Flags::committedStateSeparateEvent
     );
 
+    private final FlagState mSeparateTimeouts = new FlagState(
+            Flags.FLAG_SEPARATE_TIMEOUTS,
+            Flags::separateTimeouts
+    );
+
     private final FlagState mDelayImplicitRrRegistrationUntilRrAccessed = new FlagState(
             Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED,
             Flags::delayImplicitRrRegistrationUntilRrAccessed
@@ -608,6 +613,14 @@
     }
 
     /**
+     * @return {@code true} if the flag for having a separate timeouts for power groups
+     * is enabled
+     */
+    public boolean isSeparateTimeoutsEnabled() {
+        return mSeparateTimeouts.isEnabled();
+    }
+
+    /**
      * @return {@code true} if the flag for only explicit subscription for RR changes is enabled
      */
     public boolean isDelayImplicitRrRegistrationUntilRrAccessedEnabled() {
@@ -671,6 +684,7 @@
         pw.println(" " + mFramerateOverrideTriggersRrCallbacks);
         pw.println(" " + mRefreshRateEventForForegroundApps);
         pw.println(" " + mCommittedStateSeparateEvent);
+        pw.println(" " + mSeparateTimeouts);
         pw.println(" " + mDelayImplicitRrRegistrationUntilRrAccessed);
     }
 
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 a0064a9..007646f 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
@@ -509,6 +509,14 @@
     }
 }
 
+
+flag {
+    name: "separate_timeouts"
+    namespace: "lse_desktop_experience"
+    description: "Allow separate timeouts for different power groups"
+    bug: "402356291"
+}
+
 flag {
     name: "delay_implicit_rr_registration_until_rr_accessed"
     namespace: "display_manager"
diff --git a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
index 2751835..50782a2 100644
--- a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
+++ b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
@@ -16,9 +16,11 @@
 
 package com.android.server.display.mode;
 
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
 import android.os.Looper;
+import android.util.LongSparseArray;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayAddress;
 import android.view.DisplayEventReceiver;
@@ -34,72 +36,128 @@
 
     @SuppressWarnings("unused")
     private DisplayEventReceiver mModeChangeListener;
-    private final SparseArray<Set<Integer>> mRejectedModesByDisplay = new SparseArray<>();
-    private Looper mLooper;
+    private DisplayManager.DisplayListener mDisplayListener;
+    private final LongSparseArray<Set<Integer>> mRejectedModesMap =
+            new LongSparseArray<>();
+    private final LongSparseArray<Integer> mPhysicalIdToLogicalIdMap = new LongSparseArray<>();
+    private final Looper mLooper;
+    private final Handler mHandler;
 
+    /**
+     * Observer for display mode changes.
+     * This class observes display mode rejections and updates the vote storage
+     * for rejected modes vote accordingly.
+     */
     ModeChangeObserver(VotesStorage votesStorage, DisplayModeDirector.Injector injector,
                     Looper looper) {
         mVotesStorage = votesStorage;
         mInjector = injector;
         mLooper = looper;
+        mHandler = new Handler(mLooper);
     }
 
+    /**
+     * Start observing display mode changes.
+     */
     void observe() {
+        updatePhysicalIdToLogicalIdMap();
+        mDisplayListener = new DisplayManager.DisplayListener() {
+            @Override
+            public void onDisplayAdded(int displayId) {
+                updateVoteForDisplay(displayId);
+            }
+
+            @Override
+            public void onDisplayRemoved(int displayId) {
+                int oldPhysicalDisplayIdIndex = mPhysicalIdToLogicalIdMap.indexOfValue(displayId);
+                if (oldPhysicalDisplayIdIndex < 0) {
+                    Slog.e(TAG, "Removed display not found");
+                    return;
+                }
+                long oldPhysicalDisplayId =
+                        mPhysicalIdToLogicalIdMap.keyAt(oldPhysicalDisplayIdIndex);
+                mPhysicalIdToLogicalIdMap.delete(oldPhysicalDisplayId);
+                mRejectedModesMap.delete(oldPhysicalDisplayId);
+                mVotesStorage.updateVote(displayId, Vote.PRIORITY_REJECTED_MODES, null);
+            }
+
+            @Override
+            public void onDisplayChanged(int displayId) {
+                int oldPhysicalDisplayIdIndex = mPhysicalIdToLogicalIdMap.indexOfValue(displayId);
+                if (oldPhysicalDisplayIdIndex < 0) {
+                    Slog.e(TAG, "Changed display not found");
+                    return;
+                }
+                long oldPhysicalDisplayId =
+                        mPhysicalIdToLogicalIdMap.keyAt(oldPhysicalDisplayIdIndex);
+                mPhysicalIdToLogicalIdMap.delete(oldPhysicalDisplayId);
+
+                updateVoteForDisplay(displayId);
+            }
+        };
+        mInjector.registerDisplayListener(mDisplayListener, mHandler,
+                    DisplayManager.EVENT_TYPE_DISPLAY_ADDED
+                            | DisplayManager.EVENT_TYPE_DISPLAY_CHANGED
+                            | DisplayManager.EVENT_TYPE_DISPLAY_REMOVED);
         mModeChangeListener = new DisplayEventReceiver(mLooper) {
             @Override
             public void onModeRejected(long physicalDisplayId, int modeId) {
                 Slog.d(TAG, "Mode Rejected event received");
-                int displayId = getLogicalDisplayId(physicalDisplayId);
-                if (displayId < 0) {
-                    Slog.e(TAG, "Logical Display Id not found");
+                updateRejectedModesListByDisplay(physicalDisplayId, modeId);
+                if (mPhysicalIdToLogicalIdMap.indexOfKey(physicalDisplayId) < 0) {
+                    Slog.d(TAG, "Rejected Modes Vote will be updated after display is added");
                     return;
                 }
-                populateRejectedModesListByDisplay(displayId, modeId);
-            }
-
-            @Override
-            public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
-                Slog.d(TAG, "Hotplug event received");
-                if (!connected) {
-                    int displayId = getLogicalDisplayId(physicalDisplayId);
-                    if (displayId < 0) {
-                        Slog.e(TAG, "Logical Display Id not found");
-                        return;
-                    }
-                    clearRejectedModesListByDisplay(displayId);
-                }
+                mVotesStorage.updateVote(mPhysicalIdToLogicalIdMap.get(physicalDisplayId),
+                        Vote.PRIORITY_REJECTED_MODES,
+                        Vote.forRejectedModes(mRejectedModesMap.get(physicalDisplayId)));
             }
         };
     }
 
-    private int getLogicalDisplayId(long rejectedModePhysicalDisplayId) {
+    private void updateVoteForDisplay(int displayId) {
+        Display display = mInjector.getDisplay(displayId);
+        if (display == null) {
+            // We can occasionally get a display added or changed event for a display that was
+            // subsequently removed, which means this returns null. Check this case and bail
+            // out early; if it gets re-attached we will eventually get another call back for it.
+            Slog.e(TAG, "Added or Changed display has disappeared");
+            return;
+        }
+        DisplayAddress address = display.getAddress();
+        if (address instanceof DisplayAddress.Physical physical) {
+            long physicalDisplayId = physical.getPhysicalDisplayId();
+            mPhysicalIdToLogicalIdMap.put(physicalDisplayId, displayId);
+            Set<Integer> modes = mRejectedModesMap.get(physicalDisplayId);
+            mVotesStorage.updateVote(displayId, Vote.PRIORITY_REJECTED_MODES,
+                    modes != null ? Vote.forRejectedModes(modes) : null);
+        }
+    }
+
+    private void updatePhysicalIdToLogicalIdMap() {
         Display[] displays = mInjector.getDisplays();
 
         for (Display display : displays) {
+            if (display == null) {
+                continue;
+            }
             DisplayAddress address = display.getAddress();
             if (address instanceof DisplayAddress.Physical physical) {
-                long physicalDisplayId = physical.getPhysicalDisplayId();
-                if (physicalDisplayId == rejectedModePhysicalDisplayId) {
-                    return display.getDisplayId();
-                }
+                mPhysicalIdToLogicalIdMap.put(physical.getPhysicalDisplayId(),
+                        display.getDisplayId());
             }
         }
-        return -1;
     }
 
-    private void populateRejectedModesListByDisplay(int displayId, int rejectedModeId) {
-        Set<Integer> alreadyRejectedModes = mRejectedModesByDisplay.get(displayId);
+    private void updateRejectedModesListByDisplay(long rejectedModePhysicalDisplayId,
+                                                    int rejectedModeId) {
+        Set<Integer> alreadyRejectedModes =
+                mRejectedModesMap.get(rejectedModePhysicalDisplayId);
         if (alreadyRejectedModes == null) {
             alreadyRejectedModes = new HashSet<>();
-            mRejectedModesByDisplay.put(displayId, alreadyRejectedModes);
+            mRejectedModesMap.put(rejectedModePhysicalDisplayId,
+                    alreadyRejectedModes);
         }
         alreadyRejectedModes.add(rejectedModeId);
-        mVotesStorage.updateVote(displayId, Vote.PRIORITY_REJECTED_MODES,
-                Vote.forRejectedModes(alreadyRejectedModes));
-    }
-
-    private void clearRejectedModesListByDisplay(int displayId) {
-        mRejectedModesByDisplay.remove(displayId);
-        mVotesStorage.updateVote(displayId, Vote.PRIORITY_REJECTED_MODES, null);
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 97f9a7c..32a61fa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -79,6 +79,9 @@
     // True by default for all the ARC-enabled ports.
     private final SparseBooleanArray mArcFeatureEnabled = new SparseBooleanArray();
 
+    @GuardedBy("mLock")
+    private List<byte[]> mSupportedSads = new ArrayList<>();
+
     // Whether the System Audio Control feature is enabled or not. True by default.
     @GuardedBy("mLock")
     private boolean mSystemAudioControlFeatureEnabled;
@@ -219,7 +222,9 @@
                 && reason != HdmiControlService.INITIATED_BY_BOOT_UP;
         List<HdmiCecMessage> bufferedActiveSource = mDelayedMessageBuffer
                 .getBufferedMessagesWithOpcode(Constants.MESSAGE_ACTIVE_SOURCE);
-        if (bufferedActiveSource.isEmpty()) {
+        List<HdmiCecMessage> bufferedActiveSourceFromService = mService.getCecMessageWithOpcode(
+                Constants.MESSAGE_ACTIVE_SOURCE);
+        if (bufferedActiveSource.isEmpty() && bufferedActiveSourceFromService.isEmpty()) {
             addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
                 @Override
                 public void onComplete(int result) {
@@ -856,6 +861,13 @@
                 new SystemAudioActionFromTv(this, avr.getLogicalAddress(), enabled, callback));
     }
 
+    void clearSads() {
+        synchronized (mLock) {
+            mSupportedSads.clear();
+        }
+    }
+
+
     // # Seq 25
     void setSystemAudioMode(boolean on) {
         if (!isSystemAudioControlFeatureEnabled() && on) {
@@ -909,13 +921,41 @@
     }
 
     @ServiceThreadOnly
-    void enableArc(List<byte[]> supportedSads) {
+    void enableArc() {
         assertRunOnServiceThread();
         HdmiLogger.debug("Set Arc Status[old:%b new:true]", mArcEstablished);
 
         enableAudioReturnChannel(true);
-        notifyArcStatusToAudioService(true, supportedSads);
+        //Ensure mSupportedSads is empty before fetching SADs
+        synchronized (mLock) {
+            mSupportedSads.clear();
+            notifyArcStatusToAudioService(true, mSupportedSads);
+        }
         mArcEstablished = true;
+
+        // Avoid triggering duplicate RequestSadAction events.
+        // This could lead to unexpected responses from the AVR and cause the TV to receive data
+        // out of order. The SAD report does not provide information about the order of events.
+        if (hasAction(RequestSadAction.class)) {
+            return;
+        }
+
+        // Send Request SAD to get real SAD instead of default empty
+        RequestSadAction action = new RequestSadAction(
+                this, Constants.ADDR_AUDIO_SYSTEM,
+                new RequestSadAction.RequestSadCallback() {
+                    @Override
+                    public void onRequestSadDone(List<byte[]> supportedSadsDone) {
+                        synchronized (mLock) {
+                            mSupportedSads = supportedSadsDone;
+                        }
+                        notifyArcStatusToAudioService(false, new ArrayList<>());
+                        synchronized (mLock) {
+                            notifyArcStatusToAudioService(true, mSupportedSads);
+                        }
+                    }
+                });
+        addAndStartAction(action);
     }
 
     @ServiceThreadOnly
@@ -926,6 +966,7 @@
         enableAudioReturnChannel(false);
         notifyArcStatusToAudioService(false, new ArrayList<>());
         mArcEstablished = false;
+        clearSads();
     }
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 6d973ac..fdd0ef2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1593,6 +1593,17 @@
         this.mCecMessageBuffer = cecMessageBuffer;
     }
 
+    List<HdmiCecMessage> getCecMessageWithOpcode(int opcode) {
+        List<HdmiCecMessage> cecMessagesWithOpcode = new ArrayList<>();
+        List<HdmiCecMessage> cecMessages = mCecMessageBuffer.getBuffer();
+        for (HdmiCecMessage message: cecMessages) {
+            if (message.getOpcode() == opcode) {
+                cecMessagesWithOpcode.add(message);
+            }
+        }
+        return cecMessagesWithOpcode;
+    }
+
     /**
      * Returns {@link Looper} of main thread. Use this {@link Looper} instance
      * for tasks that are running on main service thread.
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index e6abcb9..d8dfe44 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -60,35 +60,20 @@
     boolean start() {
         // Seq #37.
         if (mEnabled) {
-            // Avoid triggering duplicate RequestSadAction events.
-            // This could lead to unexpected responses from the AVR and cause the TV to receive data
-            // out of order. The SAD report does not provide information about the order of events.
-            if ((tv().hasAction(RequestSadAction.class))) {
-                return true;
-            }
-            // Request SADs before enabling ARC
-            RequestSadAction action = new RequestSadAction(
-                    localDevice(), Constants.ADDR_AUDIO_SYSTEM,
-                    new RequestSadAction.RequestSadCallback() {
-                        @Override
-                        public void onRequestSadDone(List<byte[]> supportedSads) {
-                            // Enable ARC status immediately before sending <Report Arc Initiated>.
-                            // If AVR responds with <Feature Abort>, disable ARC status again.
-                            // This is different from spec that says that turns ARC status to
-                            // "Enabled" if <Report ARC Initiated> is acknowledged and no
-                            // <Feature Abort> is received.
-                            // But implemented this way to save the time having to wait for
-                            // <Feature Abort>.
-                            Slog.i(TAG, "Enabling ARC");
-                            tv().enableArc(supportedSads);
-                            // If succeeds to send <Report ARC Initiated>, wait general timeout to
-                            // check whether there is no <Feature Abort> for <Report ARC Initiated>.
-                            mState = STATE_WAITING_TIMEOUT;
-                            addTimer(mState, HdmiConfig.TIMEOUT_MS);
-                            sendReportArcInitiated();
-                        }
-                    });
-            addAndStartAction(action);
+            // Enable ARC status immediately before sending <Report Arc Initiated>.
+            // If AVR responds with <Feature Abort>, disable ARC status again.
+            // This is different from spec that says that turns ARC status to
+            // "Enabled" if <Report ARC Initiated> is acknowledged and no
+            // <Feature Abort> is received.
+            // But implemented this way to save the time having to wait for
+            // <Feature Abort>.
+            Slog.i(TAG, "Enabling ARC");
+            tv().enableArc();
+            // If succeeds to send <Report ARC Initiated>, wait general timeout to
+            // check whether there is no <Feature Abort> for <Report ARC Initiated>.
+            mState = STATE_WAITING_TIMEOUT;
+            addTimer(mState, HdmiConfig.TIMEOUT_MS);
+            sendReportArcInitiated();
         } else {
             disableArc();
             finish();
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index c2fecf2..6e6d00d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -568,6 +568,7 @@
         }
         mWindowManagerCallbacks = callbacks;
         registerLidSwitchCallbackInternal(mWindowManagerCallbacks);
+        mKeyGestureController.setWindowManagerCallbacks(callbacks);
     }
 
     public void setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks) {
@@ -1229,7 +1230,7 @@
                 "registerTabletModeChangedListener()")) {
             throw new SecurityException("Requires TABLET_MODE_LISTENER permission");
         }
-        Objects.requireNonNull(listener, "event must not be null");
+        Objects.requireNonNull(listener, "listener must not be null");
 
         synchronized (mTabletModeLock) {
             final int callingPid = Binder.getCallingPid();
@@ -1341,7 +1342,7 @@
 
     @Override
     public void requestPointerCapture(IBinder inputChannelToken, boolean enabled) {
-        Objects.requireNonNull(inputChannelToken, "event must not be null");
+        Objects.requireNonNull(inputChannelToken, "inputChannelToken must not be null");
 
         mNative.requestPointerCapture(inputChannelToken, enabled);
     }
@@ -2756,24 +2757,6 @@
                     @Nullable IBinder focussedToken) {
                 return InputManagerService.this.handleKeyGestureEvent(event);
             }
-
-            @Override
-            public boolean isKeyGestureSupported(int gestureType) {
-                switch (gestureType) {
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
-                    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;
-
-                }
-            }
         });
     }
 
@@ -3371,6 +3354,11 @@
          */
         @Nullable
         SurfaceControl createSurfaceForGestureMonitor(String name, int displayId);
+
+        /**
+         * Provide information on whether the keyguard is currently locked or not.
+         */
+        boolean isKeyguardLocked(int displayId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index ef5babf..395c773 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -62,8 +62,10 @@
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+import android.view.ViewConfiguration;
 
 import com.android.internal.R;
+import com.android.internal.accessibility.AccessibilityShortcutController;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.IShortcutService;
@@ -104,6 +106,7 @@
     private static final int MSG_NOTIFY_KEY_GESTURE_EVENT = 1;
     private static final int MSG_PERSIST_CUSTOM_GESTURES = 2;
     private static final int MSG_LOAD_CUSTOM_GESTURES = 3;
+    private static final int MSG_ACCESSIBILITY_SHORTCUT = 4;
 
     // must match: config_settingsKeyBehavior in config.xml
     private static final int SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0;
@@ -122,12 +125,15 @@
     static final int POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS = 2;
 
     private final Context mContext;
+    private InputManagerService.WindowManagerCallbacks mWindowManagerCallbacks;
     private final Handler mHandler;
     private final Handler mIoHandler;
     private final int mSystemPid;
     private final KeyCombinationManager mKeyCombinationManager;
     private final SettingsObserver mSettingsObserver;
     private final AppLaunchShortcutManager mAppLaunchShortcutManager;
+    @VisibleForTesting
+    final AccessibilityShortcutController mAccessibilityShortcutController;
     private final InputGestureManager mInputGestureManager;
     private final DisplayManager mDisplayManager;
     @GuardedBy("mInputDataStore")
@@ -175,8 +181,14 @@
 
     private final boolean mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled();
 
-    KeyGestureController(Context context, Looper looper, Looper ioLooper,
+    public KeyGestureController(Context context, Looper looper, Looper ioLooper,
             InputDataStore inputDataStore) {
+        this(context, looper, ioLooper, inputDataStore, new Injector());
+    }
+
+    @VisibleForTesting
+    KeyGestureController(Context context, Looper looper, Looper ioLooper,
+            InputDataStore inputDataStore, Injector injector) {
         mContext = context;
         mHandler = new Handler(looper, this::handleMessage);
         mIoHandler = new Handler(ioLooper, this::handleIoMessage);
@@ -197,6 +209,8 @@
         mSettingsObserver = new SettingsObserver(mHandler);
         mAppLaunchShortcutManager = new AppLaunchShortcutManager(mContext);
         mInputGestureManager = new InputGestureManager(mContext);
+        mAccessibilityShortcutController = injector.getAccessibilityShortcutController(mContext,
+                mHandler);
         mDisplayManager = Objects.requireNonNull(mContext.getSystemService(DisplayManager.class));
         mInputDataStore = inputDataStore;
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -295,8 +309,8 @@
                         KeyEvent.KEYCODE_VOLUME_UP) {
                     @Override
                     public boolean preCondition() {
-                        return isKeyGestureSupported(
-                                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD);
+                        return mAccessibilityShortcutController.isAccessibilityShortcutAvailable(
+                                mWindowManagerCallbacks.isKeyguardLocked(DEFAULT_DISPLAY));
                     }
 
                     @Override
@@ -376,15 +390,15 @@
                             KeyEvent.KEYCODE_DPAD_DOWN) {
                         @Override
                         public boolean preCondition() {
-                            return isKeyGestureSupported(
-                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD);
+                            return mAccessibilityShortcutController
+                                    .isAccessibilityShortcutAvailable(false);
                         }
 
                         @Override
                         public void execute() {
                             handleMultiKeyGesture(
                                     new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN},
-                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
                                     KeyGestureEvent.ACTION_GESTURE_START, 0);
                         }
 
@@ -392,7 +406,7 @@
                         public void cancel() {
                             handleMultiKeyGesture(
                                     new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN},
-                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
                                     KeyGestureEvent.ACTION_GESTURE_COMPLETE,
                                     KeyGestureEvent.FLAG_CANCELLED);
                         }
@@ -438,6 +452,7 @@
         mSettingsObserver.observe();
         mAppLaunchShortcutManager.systemRunning();
         mInputGestureManager.systemRunning();
+        initKeyGestures();
 
         int userId;
         synchronized (mUserLock) {
@@ -447,6 +462,27 @@
         mIoHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
     }
 
+    @SuppressLint("MissingPermission")
+    private void initKeyGestures() {
+        InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
+        im.registerKeyGestureEventHandler((event, focusedToken) -> {
+            switch (event.getKeyGestureType()) {
+                case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
+                    if (event.getAction() == KeyGestureEvent.ACTION_GESTURE_START) {
+                        mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
+                        mHandler.sendMessageDelayed(
+                                mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
+                                getAccessibilityShortcutTimeout());
+                    } else {
+                        mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
+                    }
+                    return true;
+                default:
+                    return false;
+            }
+        });
+    }
+
     public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
         if (mVisibleBackgroundUsersEnabled && shouldIgnoreKeyEventForVisibleBackgroundUser(event)) {
             return false;
@@ -971,17 +1007,6 @@
         return false;
     }
 
-    private boolean isKeyGestureSupported(@KeyGestureEvent.KeyGestureType int gestureType) {
-        synchronized (mKeyGestureHandlerRecords) {
-            for (KeyGestureHandlerRecord handler : mKeyGestureHandlerRecords.values()) {
-                if (handler.isKeyGestureSupported(gestureType)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     public void notifyKeyGestureCompleted(int deviceId, int[] keycodes, int modifierState,
             @KeyGestureEvent.KeyGestureType int gestureType) {
         // TODO(b/358569822): Once we move the gesture detection logic to IMS, we ideally
@@ -1019,9 +1044,16 @@
         synchronized (mUserLock) {
             mCurrentUserId = userId;
         }
+        mAccessibilityShortcutController.setCurrentUser(userId);
         mIoHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
     }
 
+
+    public void setWindowManagerCallbacks(
+            @NonNull InputManagerService.WindowManagerCallbacks callbacks) {
+        mWindowManagerCallbacks = callbacks;
+    }
+
     private boolean isDefaultDisplayOn() {
         Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
         if (defaultDisplay == null) {
@@ -1068,6 +1100,9 @@
                 AidlKeyGestureEvent event = (AidlKeyGestureEvent) msg.obj;
                 notifyKeyGestureEvent(event);
                 break;
+            case MSG_ACCESSIBILITY_SHORTCUT:
+                mAccessibilityShortcutController.performAccessibilityShortcut();
+                break;
         }
         return true;
     }
@@ -1347,17 +1382,6 @@
             }
             return false;
         }
-
-        public boolean isKeyGestureSupported(@KeyGestureEvent.KeyGestureType int gestureType) {
-            try {
-                return mKeyGestureHandler.isKeyGestureSupported(gestureType);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to identify if key gesture type is supported by the "
-                        + "process " + mPid + ", assuming it died.", ex);
-                binderDied();
-            }
-            return false;
-        }
     }
 
     private class SettingsObserver extends ContentObserver {
@@ -1413,6 +1437,25 @@
         return event;
     }
 
+    private long getAccessibilityShortcutTimeout() {
+        synchronized (mUserLock) {
+            final ViewConfiguration config = ViewConfiguration.get(mContext);
+            final boolean hasDialogShown = Settings.Secure.getIntForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, mCurrentUserId) != 0;
+            final boolean skipTimeoutRestriction =
+                    Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                            Settings.Secure.SKIP_ACCESSIBILITY_SHORTCUT_DIALOG_TIMEOUT_RESTRICTION,
+                            0, mCurrentUserId) != 0;
+
+            // If users manually set the volume key shortcut for any accessibility service, the
+            // system would bypass the timeout restriction of the shortcut dialog.
+            return hasDialogShown || skipTimeoutRestriction
+                    ? config.getAccessibilityShortcutKeyTimeoutAfterConfirmation()
+                    : config.getAccessibilityShortcutKeyTimeout();
+        }
+    }
+
     public void dump(IndentingPrintWriter ipw) {
         ipw.println("KeyGestureController:");
         ipw.increaseIndent();
@@ -1459,4 +1502,12 @@
         mAppLaunchShortcutManager.dump(ipw);
         mInputGestureManager.dump(ipw);
     }
+
+    @VisibleForTesting
+    static class Injector {
+        AccessibilityShortcutController getAccessibilityShortcutController(Context context,
+                Handler handler) {
+            return new AccessibilityShortcutController(context, handler, UserHandle.USER_SYSTEM);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 600cf7f..1a4ead2 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -22,6 +22,7 @@
 import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
 import static com.android.server.EventLogTags.IMF_HIDE_IME;
 import static com.android.server.EventLogTags.IMF_SHOW_IME;
+import static com.android.server.inputmethod.ImeProtoLogGroup.IME_VISIBILITY_APPLIER_DEBUG;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_NOT_ALWAYS;
@@ -36,7 +37,6 @@
 import android.os.IBinder;
 import android.os.ResultReceiver;
 import android.util.EventLog;
-import android.util.Slog;
 import android.view.MotionEvent;
 import android.view.inputmethod.Flags;
 import android.view.inputmethod.ImeTracker;
@@ -46,6 +46,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.wm.ImeTargetVisibilityPolicy;
 import com.android.server.wm.WindowManagerInternal;
@@ -58,9 +59,7 @@
  */
 final class DefaultImeVisibilityApplier {
 
-    private static final String TAG = "DefaultImeVisibilityApplier";
-
-    private static final boolean DEBUG = InputMethodManagerService.DEBUG;
+    static final String TAG = "DefaultImeVisibilityApplier";
 
     private InputMethodManagerService mService;
 
@@ -93,11 +92,10 @@
         final var bindingController = userData.mBindingController;
         final IInputMethodInvoker curMethod = bindingController.getCurMethod();
         if (curMethod != null) {
-            if (DEBUG) {
-                Slog.v(TAG, "Calling " + curMethod + ".showSoftInput(" + showInputToken
-                        + ", " + showFlags + ", " + resultReceiver + ") for reason: "
-                        + InputMethodDebug.softInputDisplayReasonToString(reason));
-            }
+            ProtoLog.v(IME_VISIBILITY_APPLIER_DEBUG,
+                    "Calling %s.showSoftInput(%s, %s, %s) for reason: %s", curMethod,
+                    showInputToken, showFlags, resultReceiver,
+                    InputMethodDebug.softInputDisplayReasonToString(reason));
             // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
             if (curMethod.showSoftInput(showInputToken, statsToken, showFlags, resultReceiver)) {
                 if (DEBUG_IME_VISIBILITY) {
@@ -136,11 +134,9 @@
             // delivered to the IME process as an IPC.  Hence the inconsistency between
             // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
             // the final state.
-            if (DEBUG) {
-                Slog.v(TAG, "Calling " + curMethod + ".hideSoftInput(0, " + hideInputToken
-                        + ", " + resultReceiver + ") for reason: "
-                        + InputMethodDebug.softInputDisplayReasonToString(reason));
-            }
+            ProtoLog.v(IME_VISIBILITY_APPLIER_DEBUG,
+                    "Calling %s.hideSoftInput(0, %s, %s) for reason: %s", curMethod, hideInputToken,
+                    resultReceiver, InputMethodDebug.softInputDisplayReasonToString(reason));
             // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
             if (curMethod.hideSoftInput(hideInputToken, statsToken, 0, resultReceiver)) {
                 if (DEBUG_IME_VISIBILITY) {
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
index 02987a9..15f186b0 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -132,8 +132,8 @@
                 @Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
                 IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
                 int unverifiedTargetSdkVersion, @UserIdInt int userId,
-                @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
-                boolean useAsyncShowHideMethod);
+                @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible,
+                int startInputSeq, boolean useAsyncShowHideMethod);
 
         InputBindResult startInputOrWindowGainedFocus(
                 @StartInputReason int startInputReason, IInputMethodClient client,
@@ -142,7 +142,7 @@
                 @Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
                 IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
                 int unverifiedTargetSdkVersion, @UserIdInt int userId,
-                @NonNull ImeOnBackInvokedDispatcher imeDispatcher);
+                @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible);
 
         void showInputMethodPickerFromClient(IInputMethodClient client, int auxiliarySubtypeMode);
 
@@ -324,11 +324,11 @@
             IRemoteInputConnection inputConnection,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible) {
         return mCallback.startInputOrWindowGainedFocus(
                 startInputReason, client, windowToken, startInputFlags, softInputMode,
                 windowFlags, editorInfo, inputConnection, remoteAccessibilityInputConnection,
-                unverifiedTargetSdkVersion, userId, imeDispatcher);
+                unverifiedTargetSdkVersion, userId, imeDispatcher, imeRequestedVisible);
     }
 
     @Override
@@ -340,13 +340,13 @@
             IRemoteInputConnection inputConnection,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
-            boolean useAsyncShowHideMethod) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible,
+            int startInputSeq, boolean useAsyncShowHideMethod) {
         mCallback.startInputOrWindowGainedFocusAsync(
                 startInputReason, client, windowToken, startInputFlags, softInputMode,
                 windowFlags, editorInfo, inputConnection, remoteAccessibilityInputConnection,
-                unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq,
-                useAsyncShowHideMethod);
+                unverifiedTargetSdkVersion, userId, imeDispatcher, imeRequestedVisible,
+                startInputSeq, useAsyncShowHideMethod);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/inputmethod/ImeProtoLogGroup.java b/services/core/java/com/android/server/inputmethod/ImeProtoLogGroup.java
index f9a56ef..ea4e295 100644
--- a/services/core/java/com/android/server/inputmethod/ImeProtoLogGroup.java
+++ b/services/core/java/com/android/server/inputmethod/ImeProtoLogGroup.java
@@ -23,7 +23,11 @@
 public enum ImeProtoLogGroup implements IProtoLogGroup {
     // TODO(b/393561240): add info/warn/error log level and replace in IMMS
     IMMS_DEBUG(Consts.ENABLE_DEBUG, false, false,
-            InputMethodManagerService.TAG);
+            InputMethodManagerService.TAG),
+    IME_VISIBILITY_APPLIER_DEBUG(Consts.ENABLE_DEBUG, false, false,
+            DefaultImeVisibilityApplier.TAG),
+    IME_VIS_STATE_COMPUTER_DEBUG(Consts.ENABLE_DEBUG, false, false,
+            ImeVisibilityStateComputer.TAG);
 
     private final boolean mEnabled;
     private volatile boolean mLogToProto;
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 5fe8318..2c07a31 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -32,6 +32,7 @@
 import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString;
 import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
 import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
+import static com.android.server.inputmethod.ImeProtoLogGroup.IME_VIS_STATE_COMPUTER_DEBUG;
 import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget;
 
 import android.accessibilityservice.AccessibilityService;
@@ -58,6 +59,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.protolog.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
@@ -72,9 +74,7 @@
  */
 public final class ImeVisibilityStateComputer {
 
-    private static final String TAG = "ImeVisibilityStateComputer";
-
-    private static final boolean DEBUG = InputMethodManagerService.DEBUG;
+    static final String TAG = "ImeVisibilityStateComputer";
 
     @UserIdInt
     private final int mUserId;
@@ -292,12 +292,14 @@
             @InputMethodManager.HideFlags int hideFlags) {
         if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
                 && (mRequestedShowExplicitly || mShowForced)) {
-            if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
+            ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG,
+                    "Not hiding: explicit show not cancelled by non-explicit hide");
             ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_IMPLICIT);
             return false;
         }
         if (mShowForced && (hideFlags & InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
-            if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
+            ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG,
+                    "Not hiding: forced show not cancelled by not-always hide");
             ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_HIDE_NOT_ALWAYS);
             return false;
         }
@@ -417,8 +419,8 @@
 
     @GuardedBy("ImfLock.class")
     private void setWindowStateInner(IBinder windowToken, @NonNull ImeTargetWindowState newState) {
-        if (DEBUG) Slog.d(TAG, "setWindowStateInner, windowToken=" + windowToken
-                + ", state=" + newState);
+        ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "setWindowStateInner, windowToken=%s, state=%s",
+                windowToken, newState);
         mRequestWindowStateMap.put(windowToken, newState);
     }
 
@@ -441,7 +443,8 @@
     }
 
     @GuardedBy("ImfLock.class")
-    ImeVisibilityResult computeState(ImeTargetWindowState state, boolean allowVisible) {
+    ImeVisibilityResult computeState(ImeTargetWindowState state, boolean allowVisible,
+            boolean imeRequestedVisible) {
         // TODO: Output the request IME visibility state according to the requested window state
         final int softInputVisibility = state.mSoftInputModeState & SOFT_INPUT_MASK_STATE;
         // Should we auto-show the IME even if the caller has not
@@ -466,7 +469,7 @@
         // Because the app might leverage these flags to hide soft-keyboard with showing their own
         // UI for input.
         if (state.hasEditorFocused() && shouldRestoreImeVisibility(state)) {
-            if (DEBUG) Slog.v(TAG, "Will show input to restore visibility");
+            ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "Will show input to restore visibility");
             // Inherit the last requested IME visible state when the target window is still
             // focused with an editor.
             state.setRequestedImeVisible(true);
@@ -483,7 +486,8 @@
                         // There is no focus view, and this window will
                         // be behind any soft input window, so hide the
                         // soft input window if it is shown.
-                        if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
+                        ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG,
+                                "Unspecified window will hide input");
                         return new ImeVisibilityResult(STATE_HIDE_IME_NOT_ALWAYS,
                                 SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW);
                     }
@@ -495,7 +499,7 @@
                     // them good context without input information being obscured
                     // by the IME) or if running on a large screen where there
                     // is more room for the target window + IME.
-                    if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
+                    ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "Unspecified window will show input");
                     return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
                             SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV);
                 }
@@ -513,7 +517,8 @@
                     // the WindowState, as they're already in the correct state
                     break;
                 } else if (isForwardNavigation) {
-                    if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
+                    ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG,
+                            "Window asks to hide input going forward");
                     return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
                             SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV);
                 }
@@ -524,7 +529,7 @@
                     // the WindowState, as they're already in the correct state
                     break;
                 } else if (state.hasImeFocusChanged()) {
-                    if (DEBUG) Slog.v(TAG, "Window asks to hide input");
+                    ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "Window asks to hide input");
                     return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
                             SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
                 }
@@ -532,7 +537,8 @@
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
                 if (isForwardNavigation) {
                     if (allowVisible) {
-                        if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
+                        ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG,
+                                "Window asks to show input going forward");
                         return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
                                 SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV);
                     } else {
@@ -543,7 +549,7 @@
                 }
                 break;
             case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
-                if (DEBUG) Slog.v(TAG, "Window asks to always show input");
+                ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "Window asks to always show input");
                 if (allowVisible) {
                     if (state.hasImeFocusChanged()) {
                         return new ImeVisibilityResult(STATE_SHOW_IME_IMPLICIT,
@@ -565,12 +571,14 @@
             // To maintain compatibility, we are now hiding the IME when we don't have
             // an editor upon refocusing a window.
             if (state.isStartInputByGainFocus()) {
-                if (DEBUG) Slog.v(TAG, "Same window without editor will hide input");
+                ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG,
+                        "Same window without editor will hide input");
                 return new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
                         SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
             }
         }
-        if (!state.hasEditorFocused() && mInputShown && state.isStartInputByGainFocus()
+        if (!state.hasEditorFocused() && (mInputShown || (Flags.refactorInsetsController()
+                && imeRequestedVisible)) && state.isStartInputByGainFocus()
                 && mService.mInputMethodDeviceConfigs.shouldHideImeWhenNoEditorFocus()) {
             // Hide the soft-keyboard when the system do nothing for softInputModeState
             // of the window being gained focus without an editor. This behavior benefits
@@ -579,7 +587,7 @@
             // 1) SOFT_INPUT_STATE_UNCHANGED state without an editor
             // 2) SOFT_INPUT_STATE_VISIBLE state without an editor
             // 3) SOFT_INPUT_STATE_ALWAYS_VISIBLE state without an editor
-            if (DEBUG) Slog.v(TAG, "Window without editor will hide input");
+            ProtoLog.v(IME_VIS_STATE_COMPUTER_DEBUG, "Window without editor will hide input");
             if (Flags.refactorInsetsController()) {
                 state.setRequestedImeVisible(false);
             }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 68ad8f7..fde9165 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3725,8 +3725,8 @@
             IRemoteInputConnection inputConnection,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
-            boolean useAsyncShowHideMethod) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible,
+            int startInputSeq, boolean useAsyncShowHideMethod) {
         // implemented by ZeroJankProxy
     }
 
@@ -3739,7 +3739,7 @@
             IRemoteInputConnection inputConnection,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible) {
         if (UserHandle.getCallingUserId() != userId) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
@@ -3870,7 +3870,8 @@
                     result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
                             client, windowToken, startInputFlags, softInputMode, windowFlags,
                             editorInfo, inputConnection, remoteAccessibilityInputConnection,
-                            unverifiedTargetSdkVersion, bindingController, imeDispatcher, cs);
+                            unverifiedTargetSdkVersion, bindingController, imeDispatcher, cs,
+                            imeRequestedVisible);
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -3899,7 +3900,8 @@
             IRemoteInputConnection inputContext,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @NonNull InputMethodBindingController bindingController,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, @NonNull ClientState cs) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, @NonNull ClientState cs,
+            boolean imeRequestedVisible) {
         ProtoLog.v(IMMS_DEBUG, "startInputOrWindowGainedFocusInternalLocked: reason=%s"
                     + " client=%s"
                     + " inputContext=%s"
@@ -3910,12 +3912,13 @@
                     + " unverifiedTargetSdkVersion=%s"
                     + " bindingController=%s"
                     + " imeDispatcher=%s"
-                    + " cs=%s",
+                    + " cs=%s"
+                    + " imeRequestedVisible=%s",
                 InputMethodDebug.startInputReasonToString(startInputReason), client.asBinder(),
                 inputContext, editorInfo, InputMethodDebug.startInputFlagsToString(startInputFlags),
                 InputMethodDebug.softInputModeToString(softInputMode),
                 Integer.toHexString(windowFlags), unverifiedTargetSdkVersion, bindingController,
-                imeDispatcher, cs);
+                imeDispatcher, cs, imeRequestedVisible);
 
         final int userId = bindingController.getUserId();
         final var userData = getUserData(userId);
@@ -3963,7 +3966,8 @@
         InputBindResult res = null;
 
         final ImeVisibilityResult imeVisRes = visibilityStateComputer.computeState(windowState,
-                isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags));
+                isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags),
+                imeRequestedVisible);
         if (imeVisRes != null) {
             boolean isShow = false;
             switch (imeVisRes.getReason()) {
@@ -5665,11 +5669,6 @@
         LocalServices.addService(InputMethodManagerInternal.class, mInputMethodManagerInternal);
     }
 
-    // TODO(b/352228316): Remove it once IMMIProxy is removed.
-    InputMethodManagerInternal getLocalService(){
-        return mInputMethodManagerInternal;
-    }
-
     private final class LocalServiceImpl extends InputMethodManagerInternal {
 
         @ImfLockFree
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index 7252925..12c1d9c 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -234,15 +234,15 @@
             IRemoteInputConnection inputConnection,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
-            boolean useAsyncShowHideMethod) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible,
+            int startInputSeq, boolean useAsyncShowHideMethod) {
         offload(() -> {
             InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
                     windowToken, startInputFlags, softInputMode, windowFlags,
                     editorInfo,
                     inputConnection, remoteAccessibilityInputConnection,
                     unverifiedTargetSdkVersion,
-                    userId, imeDispatcher);
+                    userId, imeDispatcher, imeRequestedVisible);
             sendOnStartInputResult(client, result, startInputSeq);
             // For first-time client bind, MSG_BIND should arrive after MSG_START_INPUT_RESULT.
             if (result.result == InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION) {
@@ -269,7 +269,7 @@
             IRemoteInputConnection inputConnection,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean imeRequestedVisible) {
         // Should never be called when flag is enabled i.e. when this proxy is used.
         return null;
     }
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 2d937bd..ccb9e3e 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -171,7 +171,8 @@
             }
         }
 
-        public boolean isInMessageHistory(HubMessage message) {
+        public boolean isInReliableMessageHistory(HubMessage message) {
+            if (!message.isResponseRequired()) return false;
             // Clean up the history
             Iterator<Map.Entry<Integer, Long>> iterator =
                     mRxMessageHistoryMap.entrySet().iterator();
@@ -188,7 +189,8 @@
             return mRxMessageHistoryMap.containsKey(message.getMessageSequenceNumber());
         }
 
-        public void addMessageToHistory(HubMessage message) {
+        public void addReliableMessageToHistory(HubMessage message) {
+            if (!message.isResponseRequired()) return;
             if (mRxMessageHistoryMap.containsKey(message.getMessageSequenceNumber())) {
                 long value = mRxMessageHistoryMap.get(message.getMessageSequenceNumber());
                 Log.w(
@@ -299,7 +301,8 @@
             throw new IllegalArgumentException(
                     "Unknown session ID in closeSession: id=" + sessionId);
         }
-        halCloseEndpointSession(sessionId, ContextHubServiceUtil.toHalReason(reason));
+        mEndpointManager.halCloseEndpointSession(
+                sessionId, ContextHubServiceUtil.toHalReason(reason));
     }
 
     @Override
@@ -310,7 +313,7 @@
             // Iterate in reverse since cleanupSessionResources will remove the entry
             for (int i = mSessionMap.size() - 1; i >= 0; i--) {
                 int id = mSessionMap.keyAt(i);
-                halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
+                mEndpointManager.halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
                 cleanupSessionResources(id);
             }
         }
@@ -442,7 +445,8 @@
                     int id = mSessionMap.keyAt(i);
                     HubEndpointInfo target = mSessionMap.get(id).getRemoteEndpointInfo();
                     if (!hasEndpointPermissions(target)) {
-                        halCloseEndpointSessionNoThrow(id, Reason.PERMISSION_DENIED);
+                        mEndpointManager.halCloseEndpointSessionNoThrow(
+                                id, Reason.PERMISSION_DENIED);
                         onCloseEndpointSession(id, Reason.PERMISSION_DENIED);
                         // Resource cleanup is done in onCloseEndpointSession
                     }
@@ -501,17 +505,7 @@
         mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
     }
 
-    /* package */ void onEndpointSessionOpenRequest(
-            int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
-        Optional<Byte> error =
-                onEndpointSessionOpenRequestInternal(sessionId, initiator, serviceDescriptor);
-        if (error.isPresent()) {
-            halCloseEndpointSessionNoThrow(sessionId, error.get());
-            onCloseEndpointSession(sessionId, error.get());
-            // Resource cleanup is done in onCloseEndpointSession
-        }
-    }
-
+    /** Handle close endpoint callback to the client side */
     /* package */ void onCloseEndpointSession(int sessionId, byte reason) {
         if (!cleanupSessionResources(sessionId)) {
             Log.w(TAG, "Unknown session ID in onCloseEndpointSession: id=" + sessionId);
@@ -583,7 +577,7 @@
         }
     }
 
-    private Optional<Byte> onEndpointSessionOpenRequestInternal(
+    /* package */ Optional<Byte> onEndpointSessionOpenRequest(
             int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
         if (!hasEndpointPermissions(initiator)) {
             Log.e(
@@ -592,15 +586,41 @@
                             + initiator
                             + " doesn't have permission for "
                             + mEndpointInfo);
-            return Optional.of(Reason.PERMISSION_DENIED);
+            byte reason = Reason.PERMISSION_DENIED;
+            onCloseEndpointSession(sessionId, reason);
+            return Optional.of(reason);
         }
 
+        // Check & handle error cases for duplicated session id.
+        final boolean existingSession;
+        final boolean existingSessionActive;
         synchronized (mOpenSessionLock) {
             if (hasSessionId(sessionId)) {
-                Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
-                return Optional.of(Reason.UNSPECIFIED);
+                existingSession = true;
+                existingSessionActive = mSessionMap.get(sessionId).isActive();
+                Log.w(
+                        TAG,
+                        "onEndpointSessionOpenRequest: "
+                                + "Existing session ID: "
+                                + sessionId
+                                + ", isActive: "
+                                + existingSessionActive);
+            } else {
+                existingSession = false;
+                existingSessionActive = false;
+                mSessionMap.put(sessionId, new Session(initiator, true));
             }
-            mSessionMap.put(sessionId, new Session(initiator, true));
+        }
+
+        if (existingSession) {
+            if (existingSessionActive) {
+                // Existing session is already active, call onSessionOpenComplete.
+                openSessionRequestComplete(sessionId);
+                return Optional.empty();
+            }
+            // Reject the session open request for now. Consider invalidating previous pending
+            // session open request based on timeout.
+            return Optional.of(Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
         }
 
         boolean success =
@@ -608,7 +628,11 @@
                         (consumer) ->
                                 consumer.onSessionOpenRequest(
                                         sessionId, initiator, serviceDescriptor));
-        return success ? Optional.empty() : Optional.of(Reason.UNSPECIFIED);
+        byte reason = Reason.UNSPECIFIED;
+        if (!success) {
+            onCloseEndpointSession(sessionId, reason);
+        }
+        return success ? Optional.empty() : Optional.of(reason);
     }
 
     private byte onMessageReceivedInternal(int sessionId, HubMessage message) {
@@ -623,7 +647,7 @@
                 return ErrorCode.PERMANENT_ERROR;
             }
             HubEndpointInfo remote = mSessionMap.get(sessionId).getRemoteEndpointInfo();
-            if (mSessionMap.get(sessionId).isInMessageHistory(message)) {
+            if (mSessionMap.get(sessionId).isInReliableMessageHistory(message)) {
                 Log.e(TAG, "Dropping duplicate message: " + message);
                 return ErrorCode.TRANSIENT_ERROR;
             }
@@ -648,36 +672,13 @@
             boolean success =
                     invokeCallback((consumer) -> consumer.onMessageReceived(sessionId, message));
             if (success) {
-                mSessionMap.get(sessionId).addMessageToHistory(message);
+                mSessionMap.get(sessionId).addReliableMessageToHistory(message);
             }
             return success ? ErrorCode.OK : ErrorCode.TRANSIENT_ERROR;
         }
     }
 
     /**
-     * Calls the HAL closeEndpointSession API.
-     *
-     * @param sessionId The session ID to close
-     * @param halReason The HAL reason
-     */
-    private void halCloseEndpointSession(int sessionId, byte halReason) throws RemoteException {
-        try {
-            mHubInterface.closeEndpointSession(sessionId, halReason);
-        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
-            throw e;
-        }
-    }
-
-    /** Same as halCloseEndpointSession but does not throw the exception */
-    private void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
-        try {
-            halCloseEndpointSession(sessionId, halReason);
-        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
-            Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
-        }
-    }
-
-    /**
      * Cleans up resources related to a session with the provided ID.
      *
      * @param sessionId The session ID to clean up resources for
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 8ab581e..e156159 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -29,6 +29,7 @@
 import android.hardware.contexthub.IContextHubEndpointCallback;
 import android.hardware.contexthub.IEndpointCommunication;
 import android.hardware.contexthub.MessageDeliveryStatus;
+import android.hardware.contexthub.Reason;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.util.Log;
@@ -42,6 +43,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
@@ -316,6 +318,11 @@
         }
     }
 
+    /** Returns if a sessionId can be allocated for the service hub. */
+    private boolean isSessionIdAllocatedForService(int sessionId) {
+        return sessionId > mMaxSessionId || sessionId < mMinSessionId;
+    }
+
     /**
      * Unregisters an endpoint given its ID.
      *
@@ -337,8 +344,7 @@
         }
     }
 
-    @Override
-    public void onEndpointSessionOpenRequest(
+    private Optional<Byte> onEndpointSessionOpenRequestInternal(
             int sessionId,
             HubEndpointInfo.HubEndpointIdentifier destination,
             HubEndpointInfo.HubEndpointIdentifier initiator,
@@ -348,7 +354,7 @@
                     TAG,
                     "onEndpointSessionOpenRequest: invalid destination hub ID: "
                             + destination.getHub());
-            return;
+            return Optional.of(Reason.ENDPOINT_INVALID);
         }
         ContextHubEndpointBroker broker = mEndpointMap.get(destination.getEndpoint());
         if (broker == null) {
@@ -356,7 +362,7 @@
                     TAG,
                     "onEndpointSessionOpenRequest: unknown destination endpoint ID: "
                             + destination.getEndpoint());
-            return;
+            return Optional.of(Reason.ENDPOINT_INVALID);
         }
         HubEndpointInfo initiatorInfo = mHubInfoRegistry.getEndpointInfo(initiator);
         if (initiatorInfo == null) {
@@ -364,9 +370,29 @@
                     TAG,
                     "onEndpointSessionOpenRequest: unknown initiator endpoint ID: "
                             + initiator.getEndpoint());
-            return;
+            return Optional.of(Reason.ENDPOINT_INVALID);
         }
-        broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+        if (!isSessionIdAllocatedForService(sessionId)) {
+            Log.e(
+                    TAG,
+                    "onEndpointSessionOpenRequest: invalid session ID, rejected:"
+                            + " sessionId="
+                            + sessionId);
+            return Optional.of(Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
+        }
+        return broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+    }
+
+    @Override
+    public void onEndpointSessionOpenRequest(
+            int sessionId,
+            HubEndpointInfo.HubEndpointIdentifier destination,
+            HubEndpointInfo.HubEndpointIdentifier initiator,
+            String serviceDescriptor) {
+        Optional<Byte> errorOptional =
+                onEndpointSessionOpenRequestInternal(
+                        sessionId, destination, initiator, serviceDescriptor);
+        errorOptional.ifPresent((error) -> halCloseEndpointSessionNoThrow(sessionId, error));
     }
 
     @Override
@@ -418,6 +444,30 @@
         }
     }
 
+    /**
+     * Calls the HAL closeEndpointSession API.
+     *
+     * @param sessionId The session ID to close
+     * @param halReason The HAL reason
+     */
+    /* package */ void halCloseEndpointSession(int sessionId, byte halReason)
+            throws RemoteException {
+        try {
+            mHubInterface.closeEndpointSession(sessionId, halReason);
+        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+            throw e;
+        }
+    }
+
+    /** Same as halCloseEndpointSession but does not throw the exception */
+    /* package */ void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
+        try {
+            halCloseEndpointSession(sessionId, halReason);
+        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+            Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
+        }
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 6a72cc7..7d9f2c2 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -24,6 +24,7 @@
 import android.hardware.location.GeofenceHardwareImpl;
 import android.location.FusedBatchOptions;
 import android.location.GnssAntennaInfo;
+import android.location.GnssAssistance;
 import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementRequest;
@@ -35,6 +36,8 @@
 import android.location.IGpsGeofenceHardware;
 import android.location.Location;
 import android.location.LocationManager;
+import android.location.flags.Flags;
+import android.location.provider.IGnssAssistanceCallback;
 import android.location.util.identity.CallerIdentity;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -47,12 +50,13 @@
 import com.android.server.FgThread;
 import com.android.server.location.gnss.hal.GnssNative;
 import com.android.server.location.injector.Injector;
+import com.android.server.location.provider.proxy.ProxyGnssAssistanceProvider;
 
 import java.io.FileDescriptor;
 import java.util.List;
 
 /** Manages Gnss providers and related Gnss functions for LocationManagerService. */
-public class GnssManagerService {
+public class GnssManagerService implements GnssNative.GnssAssistanceCallbacks {
 
     public static final String TAG = "GnssManager";
     public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
@@ -75,6 +79,8 @@
 
     private final GnssMetrics mGnssMetrics;
 
+    private @Nullable ProxyGnssAssistanceProvider mProxyGnssAssistanceProvider = null;
+
     public GnssManagerService(Context context, Injector injector, GnssNative gnssNative) {
         mContext = context.createAttributionContext(ATTRIBUTION_ID);
         mGnssNative = gnssNative;
@@ -100,6 +106,16 @@
     /** Called when system is ready. */
     public void onSystemReady() {
         mGnssLocationProvider.onSystemReady();
+
+        if (Flags.gnssAssistanceInterfaceJni()) {
+            mProxyGnssAssistanceProvider =
+                    ProxyGnssAssistanceProvider.createAndRegister(mContext);
+            if (mProxyGnssAssistanceProvider == null) {
+                Log.e(TAG, "no gnss assistance provider found");
+            } else {
+                mGnssNative.setGnssAssistanceCallbacks(this);
+            }
+        }
     }
 
     /** Retrieve the GnssLocationProvider. */
@@ -323,6 +339,29 @@
         }
     }
 
+    @Override
+    public void onRequestGnssAssistanceInject() {
+        if (!Flags.gnssAssistanceInterfaceJni()) {
+            return;
+        }
+        if (mProxyGnssAssistanceProvider == null) {
+            Log.e(TAG, "ProxyGnssAssistanceProvider is null");
+            return;
+        }
+        mProxyGnssAssistanceProvider.request(new IGnssAssistanceCallback.Stub() {
+            @Override
+            public void onError() {
+                Log.e(TAG, "GnssAssistanceCallback.onError");
+            }
+
+            @Override
+            public void onResult(GnssAssistance gnssAssistance) {
+                Log.d(TAG, "GnssAssistanceCallback.onResult");
+                mGnssNative.injectGnssAssistance(gnssAssistance);
+            }
+        });
+    }
+
     private class GnssCapabilitiesHalModule implements GnssNative.BaseCallbacks {
 
         GnssCapabilitiesHalModule(GnssNative gnssNative) {
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index c79a21a..7fd400e 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.location.GnssAntennaInfo;
+import android.location.GnssAssistance;
 import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementsEvent;
@@ -30,6 +31,7 @@
 import android.location.GnssSignalType;
 import android.location.GnssStatus;
 import android.location.Location;
+import android.location.flags.Flags;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -275,6 +277,12 @@
         void onRequestPsdsDownload(int psdsType);
     }
 
+    /** Callbacks for HAL requesting GNSS assistance. */
+    public interface GnssAssistanceCallbacks {
+        /** On request GnssAssistance injection. */
+        void onRequestGnssAssistanceInject();
+    }
+
     /** Callbacks for AGPS functionality. */
     public interface AGpsCallbacks {
 
@@ -400,6 +408,7 @@
     private TimeCallbacks mTimeCallbacks;
     private LocationRequestCallbacks mLocationRequestCallbacks;
     private PsdsCallbacks mPsdsCallbacks;
+    private @Nullable GnssAssistanceCallbacks mGnssAssistanceCallbacks;
     private AGpsCallbacks mAGpsCallbacks;
     private NotificationCallbacks mNotificationCallbacks;
 
@@ -504,6 +513,15 @@
         mNotificationCallbacks = Objects.requireNonNull(callbacks);
     }
 
+    /** Sets GnssAssistanceCallbacks. */
+    public void setGnssAssistanceCallbacks(GnssAssistanceCallbacks callbacks) {
+        if (!Flags.gnssAssistanceInterfaceJni()) {
+            return;
+        }
+        Preconditions.checkState(mGnssAssistanceCallbacks == null);
+        mGnssAssistanceCallbacks = Objects.requireNonNull(callbacks);
+    }
+
     /**
      * Registers with the HAL and allows callbacks to begin. Once registered with the native HAL,
      * no more callbacks can be added or set. Must only be called once.
@@ -1053,6 +1071,17 @@
         mGnssHal.injectNiSuplMessageData(data, length, slotIndex);
     }
 
+    /**
+     * Injects GNSS assistance data into the GNSS HAL.
+     */
+    public void injectGnssAssistance(GnssAssistance assistance) {
+        if (!Flags.gnssAssistanceInterfaceJni()) {
+            return;
+        }
+        Preconditions.checkState(mRegistered);
+        mGnssHal.injectGnssAssistance(assistance);
+    }
+
     @NativeEntryPoint
     void reportGnssServiceDied() {
         // Not necessary to clear (and restore) binder identity since it runs on another thread.
@@ -1269,6 +1298,15 @@
     }
 
     @NativeEntryPoint
+    void gnssAssistanceInjectRequest() {
+        if (!Flags.gnssAssistanceInterfaceJni() || mGnssAssistanceCallbacks == null) {
+            return;
+        }
+        Binder.withCleanCallingIdentity(
+                () -> mGnssAssistanceCallbacks.onRequestGnssAssistanceInject());
+    }
+
+    @NativeEntryPoint
     void reportGeofenceTransition(int geofenceId, Location location, int transition,
             long transitionTimestamp) {
         Binder.withCleanCallingIdentity(
@@ -1569,6 +1607,10 @@
         protected void injectNiSuplMessageData(byte[] data, int length, int slotIndex) {
             native_inject_ni_supl_message_data(data, length, slotIndex);
         }
+
+        protected void injectGnssAssistance(GnssAssistance gnssAssistance) {
+            native_inject_gnss_assistance(gnssAssistance);
+        }
     }
 
     // basic APIs
@@ -1718,4 +1760,7 @@
     private static native boolean native_supports_psds();
 
     private static native void native_inject_psds_data(byte[] data, int length, int psdsType);
+
+    // GNSS Assistance APIs
+    private static native void native_inject_gnss_assistance(GnssAssistance gnssAssistance);
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a0e5433..a819026 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -31,7 +31,6 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_SYSTEM;
-import static android.security.Flags.reportPrimaryAuthAttempts;
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
@@ -2483,11 +2482,8 @@
                 requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
             }
         }
-        if (reportPrimaryAuthAttempts()) {
-            final boolean success =
-                    response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
-            notifyLockSettingsStateListeners(success, userId);
-        }
+        final boolean success = response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
+        notifyLockSettingsStateListeners(success, userId);
         return response;
     }
 
@@ -3618,6 +3614,12 @@
             return;
         }
 
+        UserInfo userInfo = mInjector.getUserManagerInternal().getUserInfo(userId);
+        if (userInfo != null && userInfo.isForTesting()) {
+            Slog.i(TAG, "Keeping escrow data for test-only user");
+            return;
+        }
+
         // Disable escrow token permanently on all other device/user types.
         Slogf.i(TAG, "Permanently disabling support for escrow tokens on user %d", userId);
         mSpManager.destroyEscrowData(userId);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index f137de1..988924d 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -25,6 +25,7 @@
 import static android.media.MediaRouter2.SCANNING_STATE_WHILE_INTERACTIVE;
 import static android.media.MediaRouter2Utils.getOriginalId;
 import static android.media.MediaRouter2Utils.getProviderId;
+
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_CREATE_SESSION;
 import static com.android.server.media.MediaRouterStatsLog.MEDIA_ROUTER_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_DESELECT_ROUTE;
@@ -63,6 +64,7 @@
 import android.media.RouteDiscoveryPreference;
 import android.media.RouteListingPreference;
 import android.media.RoutingSessionInfo;
+import android.media.SuggestedDeviceInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -76,18 +78,21 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.media.flags.Flags;
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
+
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -551,6 +556,36 @@
         }
     }
 
+    public void setDeviceSuggestionsWithRouter2(
+            @NonNull IMediaRouter2 router,
+            @Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+        Objects.requireNonNull(router, "router must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                setDeviceSuggestionsWithRouter2Locked(router, suggestedDeviceInfo);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Nullable
+    public Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestionsWithRouter2(
+            @NonNull IMediaRouter2 router) {
+        Objects.requireNonNull(router, "router must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                return getDeviceSuggestionsWithRouter2Locked(router);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     // End of methods that implement MediaRouter2 operations.
 
     // Start of methods that implement MediaRouter2Manager operations.
@@ -805,6 +840,36 @@
         }
     }
 
+    public void setDeviceSuggestionsWithManager(
+            @NonNull IMediaRouter2Manager manager,
+            @Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+        Objects.requireNonNull(manager, "manager must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                setDeviceSuggestionsWithManagerLocked(manager, suggestedDeviceInfo);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Nullable
+    public Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestionsWithManager(
+            @NonNull IMediaRouter2Manager manager) {
+        Objects.requireNonNull(manager, "manager must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                return getDeviceSuggestionsWithManagerLocked(manager);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
     public boolean showMediaOutputSwitcherWithProxyRouter(
             @NonNull IMediaRouter2Manager proxyRouter) {
@@ -1582,6 +1647,61 @@
                         DUMMY_REQUEST_ID, routerRecord, uniqueSessionId));
     }
 
+    @GuardedBy("mLock")
+    private void setDeviceSuggestionsWithRouter2Locked(
+            @NonNull IMediaRouter2 router,
+            @Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+        final IBinder binder = router.asBinder();
+        final RouterRecord routerRecord = mAllRouterRecords.get(binder);
+
+        if (routerRecord == null) {
+            Slog.w(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "Ignoring set device suggestion for unknown router: %s", router));
+            return;
+        }
+
+        Slog.i(
+                TAG,
+                TextUtils.formatSimple(
+                        "setDeviceSuggestions | router: %d suggestion: %d",
+                        routerRecord.mPackageName, suggestedDeviceInfo));
+
+        routerRecord.mUserRecord.updateDeviceSuggestionsLocked(
+                routerRecord.mPackageName, routerRecord.mPackageName, suggestedDeviceInfo);
+        routerRecord.mUserRecord.mHandler.sendMessage(
+                obtainMessage(
+                        UserHandler::notifyDeviceSuggestionsUpdatedOnHandler,
+                        routerRecord.mUserRecord.mHandler,
+                        routerRecord.mPackageName,
+                        routerRecord.mPackageName,
+                        suggestedDeviceInfo));
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestionsWithRouter2Locked(
+            @NonNull IMediaRouter2 router) {
+        final IBinder binder = router.asBinder();
+        final RouterRecord routerRecord = mAllRouterRecords.get(binder);
+
+        if (routerRecord == null) {
+            Slog.w(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "Attempted to get device suggestion for unknown router: %s", router));
+            return null;
+        }
+
+        Slog.i(
+                TAG,
+                TextUtils.formatSimple(
+                        "getDeviceSuggestions | router: %d", routerRecord.mPackageName));
+
+        return routerRecord.mUserRecord.getDeviceSuggestionsLocked(routerRecord.mPackageName);
+    }
+
     // End of locked methods that are used by MediaRouter2.
 
     // Start of locked methods that are used by MediaRouter2Manager.
@@ -1972,6 +2092,68 @@
                         uniqueRequestId, routerRecord, uniqueSessionId));
     }
 
+    @GuardedBy("mLock")
+    private void setDeviceSuggestionsWithManagerLocked(
+            @NonNull IMediaRouter2Manager manager,
+            @Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord == null || managerRecord.mTargetPackageName == null) {
+            Slog.w(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "Ignoring set device suggestion for unknown manager: %s", manager));
+            return;
+        }
+
+        Slog.i(
+                TAG,
+                TextUtils.formatSimple(
+                        "setDeviceSuggestions | manager: %d, suggestingPackageName: %d suggestion:"
+                            + " %d",
+                        managerRecord.mManagerId,
+                        managerRecord.mOwnerPackageName,
+                        suggestedDeviceInfo));
+
+        managerRecord.mUserRecord.updateDeviceSuggestionsLocked(
+                managerRecord.mTargetPackageName,
+                managerRecord.mOwnerPackageName,
+                suggestedDeviceInfo);
+        managerRecord.mUserRecord.mHandler.sendMessage(
+                obtainMessage(
+                        UserHandler::notifyDeviceSuggestionsUpdatedOnHandler,
+                        managerRecord.mUserRecord.mHandler,
+                        managerRecord.mTargetPackageName,
+                        managerRecord.mOwnerPackageName,
+                        suggestedDeviceInfo));
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestionsWithManagerLocked(
+            @NonNull IMediaRouter2Manager manager) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord == null || managerRecord.mTargetPackageName == null) {
+            Slog.w(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "Attempted to get device suggestion for unknown manager: %s", manager));
+            return null;
+        }
+
+        Slog.i(
+                TAG,
+                TextUtils.formatSimple(
+                        "getDeviceSuggestionsWithManagerLocked | manager: %d",
+                        managerRecord.mManagerId));
+
+        return managerRecord.mUserRecord.getDeviceSuggestionsLocked(
+                managerRecord.mTargetPackageName);
+    }
+
     // End of locked methods that are used by MediaRouter2Manager.
 
     // Start of locked methods that are used by both MediaRouter2 and MediaRouter2Manager.
@@ -2047,6 +2229,11 @@
         //TODO: make records private for thread-safety
         final ArrayList<RouterRecord> mRouterRecords = new ArrayList<>();
         final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>();
+
+        // @GuardedBy("mLock")
+        private final Map<String, Map<String, List<SuggestedDeviceInfo>>> mDeviceSuggestions =
+                new HashMap<>();
+
         RouteDiscoveryPreference mCompositeDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
         Set<String> mActivelyScanningPackages = Set.of();
         final UserHandler mHandler;
@@ -2076,6 +2263,25 @@
             return null;
         }
 
+        // @GuardedBy("mLock")
+        public void updateDeviceSuggestionsLocked(
+                String packageName,
+                String suggestingPackageName,
+                List<SuggestedDeviceInfo> deviceSuggestions) {
+            mDeviceSuggestions.putIfAbsent(
+                    packageName, new HashMap<String, List<SuggestedDeviceInfo>>());
+            Map<String, List<SuggestedDeviceInfo>> suggestions =
+                    mDeviceSuggestions.get(packageName);
+            suggestions.put(suggestingPackageName, deviceSuggestions);
+        }
+
+        // @GuardedBy("mLock")
+        @Nullable
+        public Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestionsLocked(
+                String packageName) {
+            return mDeviceSuggestions.get(packageName);
+        }
+
         public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
             pw.println(prefix + "UserRecord");
 
@@ -2314,6 +2520,15 @@
             }
         }
 
+        public void notifyDeviceSuggestionsUpdated(
+                String suggestingPackageName, List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+            try {
+                mRouter.notifyDeviceSuggestionsUpdated(suggestingPackageName, suggestedDeviceInfo);
+            } catch (RemoteException ex) {
+                logRemoteException("notifyDeviceSuggestionsUpdated", ex);
+            }
+        }
+
         /**
          * Sends the corresponding router a {@link RoutingSessionInfo session} creation request,
          * with the given {@link MediaRoute2Info} as the initial member.
@@ -3556,6 +3771,41 @@
             //    need to update routers other than the one making the update.
         }
 
+        private void notifyDeviceSuggestionsUpdatedOnHandler(
+                String routerPackageName,
+                String suggestingPackageName,
+                @Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+            MediaRouter2ServiceImpl service = mServiceRef.get();
+            if (service == null) {
+                return;
+            }
+            List<IMediaRouter2Manager> managers = new ArrayList<>();
+            synchronized (service.mLock) {
+                for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) {
+                    if (TextUtils.equals(managerRecord.mTargetPackageName, routerPackageName)) {
+                        managers.add(managerRecord.mManager);
+                    }
+                }
+                for (IMediaRouter2Manager manager : managers) {
+                    try {
+                        manager.notifyDeviceSuggestionsUpdated(
+                                routerPackageName, suggestingPackageName, suggestedDeviceInfo);
+                    } catch (RemoteException ex) {
+                        Slog.w(
+                                TAG,
+                                "Failed to notify suggesteion changed. Manager probably died.",
+                                ex);
+                    }
+                }
+                for (RouterRecord routerRecord : mUserRecord.mRouterRecords) {
+                    if (TextUtils.equals(routerRecord.mPackageName, routerPackageName)) {
+                        routerRecord.notifyDeviceSuggestionsUpdated(
+                                suggestingPackageName, suggestedDeviceInfo);
+                    }
+                }
+            }
+        }
+
         private void updateDiscoveryPreferenceOnHandler() {
             MediaRouter2ServiceImpl service = mServiceRef.get();
             if (service == null) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 35bb199..11f449e 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -49,6 +49,7 @@
 import android.media.RouteDiscoveryPreference;
 import android.media.RouteListingPreference;
 import android.media.RoutingSessionInfo;
+import android.media.SuggestedDeviceInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -80,6 +81,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -526,6 +528,21 @@
 
     // Binder call
     @Override
+    public void setDeviceSuggestionsWithRouter2(
+            IMediaRouter2 router, @Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+        mService2.setDeviceSuggestionsWithRouter2(router, suggestedDeviceInfo);
+    }
+
+    // Binder call
+    @Override
+    @Nullable
+    public Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestionsWithRouter2(
+            IMediaRouter2 router) {
+        return mService2.getDeviceSuggestionsWithRouter2(router);
+    }
+
+    // Binder call
+    @Override
     public List<RoutingSessionInfo> getRemoteSessions(IMediaRouter2Manager manager) {
         return mService2.getRemoteSessions(manager);
     }
@@ -666,6 +683,22 @@
         return mService2.showMediaOutputSwitcherWithProxyRouter(proxyRouter);
     }
 
+    // Binder call
+    @Override
+    public void setDeviceSuggestionsWithManager(
+            @NonNull IMediaRouter2Manager manager,
+            @Nullable List<SuggestedDeviceInfo> suggestedDeviceInfo) {
+        mService2.setDeviceSuggestionsWithManager(manager, suggestedDeviceInfo);
+    }
+
+    // Binder call
+    @Override
+    @Nullable
+    public Map<String, List<SuggestedDeviceInfo>> getDeviceSuggestionsWithManager(
+            IMediaRouter2Manager manager) {
+        return mService2.getDeviceSuggestionsWithManager(manager);
+    }
+
     void restoreBluetoothA2dp() {
         try {
             boolean a2dpOn;
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 91a2843..9a09807 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -28,6 +28,7 @@
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.hardware.audio.effect.DefaultExtension;
 import android.hardware.tv.mediaquality.AmbientBacklightColorFormat;
 import android.hardware.tv.mediaquality.IMediaQuality;
 import android.hardware.tv.mediaquality.IPictureProfileAdjustmentListener;
@@ -48,7 +49,6 @@
 import android.media.quality.IPictureProfileCallback;
 import android.media.quality.ISoundProfileCallback;
 import android.media.quality.MediaQualityContract.BaseParameters;
-import android.media.quality.MediaQualityManager;
 import android.media.quality.ParameterCapability;
 import android.media.quality.PictureProfile;
 import android.media.quality.PictureProfileHandle;
@@ -58,6 +58,7 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.IBinder;
+import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -187,7 +188,7 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public PictureProfile createPictureProfile(PictureProfile pp, UserHandle user) {
+        public PictureProfile createPictureProfile(PictureProfile pp, int userId) {
             if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
                     && !incomingPackageEqualsCallingUidPackage(pp.getPackageName()))
                     && !hasGlobalPictureQualityServicePermission()) {
@@ -221,7 +222,7 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
+        public void updatePictureProfile(String id, PictureProfile pp, int userId) {
             Long dbId = mPictureProfileTempIdMap.getKey(id);
             if (!hasPermissionToUpdatePictureProfile(dbId, pp)) {
                 mMqManagerNotifier.notifyOnPictureProfileError(id,
@@ -249,7 +250,7 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public void removePictureProfile(String id, UserHandle user) {
+        public void removePictureProfile(String id, int userId) {
             synchronized (mPictureProfileLock) {
                 Long dbId = mPictureProfileTempIdMap.getKey(id);
 
@@ -290,10 +291,8 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public PictureProfile getPictureProfile(int type, String name, Bundle options,
-                UserHandle user) {
-            boolean includeParams =
-                    options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
+        public PictureProfile getPictureProfile(int type, String name, boolean includeParams,
+                int userId) {
             String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
                     + BaseParameters.PARAMETER_NAME + " = ? AND "
                     + BaseParameters.PARAMETER_PACKAGE + " = ?";
@@ -327,7 +326,7 @@
         @GuardedBy("mPictureProfileLock")
         @Override
         public List<PictureProfile> getPictureProfilesByPackage(
-                String packageName, Bundle options, UserHandle user) {
+                String packageName, boolean includeParams, int userId) {
             if (!hasGlobalPictureQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnPictureProfileError(null,
                         PictureProfile.ERROR_NO_PERMISSION,
@@ -335,8 +334,6 @@
             }
 
             synchronized (mPictureProfileLock) {
-                boolean includeParams =
-                        options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
                 String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
                 String[] selectionArguments = {packageName};
                 return mMqDatabaseUtils.getPictureProfilesBasedOnConditions(MediaQualityUtils
@@ -347,17 +344,17 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public List<PictureProfile> getAvailablePictureProfiles(Bundle options, UserHandle user) {
+        public List<PictureProfile> getAvailablePictureProfiles(boolean includeParams, int userId) {
             String packageName = getPackageOfCallingUid();
             if (packageName != null) {
-                return getPictureProfilesByPackage(packageName, options, user);
+                return getPictureProfilesByPackage(packageName, includeParams, userId);
             }
             return new ArrayList<>();
         }
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public boolean setDefaultPictureProfile(String profileId, UserHandle user) {
+        public boolean setDefaultPictureProfile(String profileId, int userId) {
             if (!hasGlobalPictureQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnPictureProfileError(profileId,
                         PictureProfile.ERROR_NO_PERMISSION,
@@ -370,13 +367,17 @@
 
             try {
                 if (mMediaQuality != null) {
+                    PictureParameters pp = new PictureParameters();
                     PictureParameter[] pictureParameters = MediaQualityUtils
                             .convertPersistableBundleToPictureParameterList(params);
 
-                    PictureParameters pp = new PictureParameters();
+                    Parcel parcel = Parcel.obtain();
+                    setVendorPictureParameters(pp, parcel, params);
+
                     pp.pictureParameters = pictureParameters;
 
                     mMediaQuality.sendDefaultPictureParameters(pp);
+                    parcel.recycle();
                     return true;
                 }
             } catch (RemoteException e) {
@@ -387,7 +388,7 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public List<String> getPictureProfilePackageNames(UserHandle user) {
+        public List<String> getPictureProfilePackageNames(int userId) {
             if (!hasGlobalPictureQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnPictureProfileError(null,
                         PictureProfile.ERROR_NO_PERMISSION,
@@ -406,7 +407,7 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public List<PictureProfileHandle> getPictureProfileHandle(String[] ids, UserHandle user) {
+        public List<PictureProfileHandle> getPictureProfileHandle(String[] ids, int userId) {
             List<PictureProfileHandle> toReturn = new ArrayList<>();
             synchronized (mPictureProfileLock) {
                 for (String id : ids) {
@@ -423,13 +424,13 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public List<SoundProfileHandle> getSoundProfileHandle(String[] ids, UserHandle user) {
+        public List<SoundProfileHandle> getSoundProfileHandle(String[] ids, int userId) {
             List<SoundProfileHandle> toReturn = new ArrayList<>();
             synchronized (mSoundProfileLock) {
                 for (String id : ids) {
                     Long key = mSoundProfileTempIdMap.getKey(id);
                     if (key != null) {
-                        toReturn.add(new SoundProfileHandle(key));
+                        toReturn.add(MediaQualityUtils.SOUND_PROFILE_HANDLE_NONE);
                     } else {
                         toReturn.add(null);
                     }
@@ -440,7 +441,7 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public SoundProfile createSoundProfile(SoundProfile sp, UserHandle user) {
+        public SoundProfile createSoundProfile(SoundProfile sp, int userId) {
             if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty()
                     && !incomingPackageEqualsCallingUidPackage(sp.getPackageName()))
                     && !hasGlobalPictureQualityServicePermission()) {
@@ -473,7 +474,7 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) {
+        public void updateSoundProfile(String id, SoundProfile sp, int userId) {
             Long dbId = mSoundProfileTempIdMap.getKey(id);
             if (!hasPermissionToUpdateSoundProfile(dbId, sp)) {
                 mMqManagerNotifier.notifyOnSoundProfileError(id, SoundProfile.ERROR_NO_PERMISSION,
@@ -502,7 +503,7 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public void removeSoundProfile(String id, UserHandle user) {
+        public void removeSoundProfile(String id, int userId) {
             synchronized (mSoundProfileLock) {
                 Long dbId = mSoundProfileTempIdMap.getKey(id);
                 SoundProfile toDelete = mMqDatabaseUtils.getSoundProfile(dbId);
@@ -542,10 +543,8 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public SoundProfile getSoundProfile(int type, String name, Bundle options,
-                UserHandle user) {
-            boolean includeParams =
-                    options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
+        public SoundProfile getSoundProfile(int type, String name, boolean includeParams,
+                int userId) {
             String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
                     + BaseParameters.PARAMETER_NAME + " = ? AND "
                     + BaseParameters.PARAMETER_PACKAGE + " = ?";
@@ -579,15 +578,13 @@
         @GuardedBy("mSoundProfileLock")
         @Override
         public List<SoundProfile> getSoundProfilesByPackage(
-                String packageName, Bundle options, UserHandle user) {
+                String packageName, boolean includeParams, int userId) {
             if (!hasGlobalSoundQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
             }
 
             synchronized (mSoundProfileLock) {
-                boolean includeParams =
-                        options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
                 String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
                 String[] selectionArguments = {packageName};
                 return mMqDatabaseUtils.getSoundProfilesBasedOnConditions(MediaQualityUtils
@@ -598,17 +595,17 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public List<SoundProfile> getAvailableSoundProfiles(Bundle options, UserHandle user) {
+        public List<SoundProfile> getAvailableSoundProfiles(boolean includeParams, int userId) {
             String packageName = getPackageOfCallingUid();
             if (packageName != null) {
-                return getSoundProfilesByPackage(packageName, options, user);
+                return getSoundProfilesByPackage(packageName, includeParams, userId);
             }
             return new ArrayList<>();
         }
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public boolean setDefaultSoundProfile(String profileId, UserHandle user) {
+        public boolean setDefaultSoundProfile(String profileId, int userId) {
             if (!hasGlobalSoundQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnSoundProfileError(profileId,
                         SoundProfile.ERROR_NO_PERMISSION,
@@ -638,7 +635,7 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public List<String> getSoundProfilePackageNames(UserHandle user) {
+        public List<String> getSoundProfilePackageNames(int userId) {
             if (!hasGlobalSoundQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
@@ -737,7 +734,7 @@
         @GuardedBy("mAmbientBacklightLock")
         @Override
         public void setAmbientBacklightSettings(
-                AmbientBacklightSettings settings, UserHandle user) {
+                AmbientBacklightSettings settings, int userId) {
             if (DEBUG) {
                 Slogf.d(TAG, "setAmbientBacklightSettings " + settings);
             }
@@ -775,7 +772,7 @@
 
         @GuardedBy("mAmbientBacklightLock")
         @Override
-        public void setAmbientBacklightEnabled(boolean enabled, UserHandle user) {
+        public void setAmbientBacklightEnabled(boolean enabled, int userId) {
             if (DEBUG) {
                 Slogf.d(TAG, "setAmbientBacklightEnabled " + enabled);
             }
@@ -795,7 +792,7 @@
 
         @Override
         public List<ParameterCapability> getParameterCapabilities(
-                List<String> names, UserHandle user) {
+                List<String> names, int userId) {
             byte[] byteArray = MediaQualityUtils.convertParameterToByteArray(names);
             ParamCapability[] caps = new ParamCapability[byteArray.length];
             try {
@@ -828,7 +825,7 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public List<String> getPictureProfileAllowList(UserHandle user) {
+        public List<String> getPictureProfileAllowList(int userId) {
             if (!hasGlobalPictureQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnPictureProfileError(null,
                         PictureProfile.ERROR_NO_PERMISSION,
@@ -844,7 +841,7 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public void setPictureProfileAllowList(List<String> packages, UserHandle user) {
+        public void setPictureProfileAllowList(List<String> packages, int userId) {
             if (!hasGlobalPictureQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnPictureProfileError(null,
                         PictureProfile.ERROR_NO_PERMISSION,
@@ -857,7 +854,7 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public List<String> getSoundProfileAllowList(UserHandle user) {
+        public List<String> getSoundProfileAllowList(int userId) {
             if (!hasGlobalSoundQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
@@ -872,7 +869,7 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public void setSoundProfileAllowList(List<String> packages, UserHandle user) {
+        public void setSoundProfileAllowList(List<String> packages, int userId) {
             if (!hasGlobalSoundQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
@@ -883,13 +880,13 @@
         }
 
         @Override
-        public boolean isSupported(UserHandle user) {
+        public boolean isSupported(int userId) {
             return false;
         }
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) {
+        public void setAutoPictureQualityEnabled(boolean enabled, int userId) {
             if (!hasGlobalPictureQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnPictureProfileError(null,
                         PictureProfile.ERROR_NO_PERMISSION,
@@ -910,7 +907,7 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public boolean isAutoPictureQualityEnabled(UserHandle user) {
+        public boolean isAutoPictureQualityEnabled(int userId) {
             synchronized (mPictureProfileLock) {
                 try {
                     if (mMediaQuality != null) {
@@ -927,7 +924,7 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public void setSuperResolutionEnabled(boolean enabled, UserHandle user) {
+        public void setSuperResolutionEnabled(boolean enabled, int userId) {
             if (!hasGlobalPictureQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnPictureProfileError(null,
                         PictureProfile.ERROR_NO_PERMISSION,
@@ -948,7 +945,7 @@
 
         @GuardedBy("mPictureProfileLock")
         @Override
-        public boolean isSuperResolutionEnabled(UserHandle user) {
+        public boolean isSuperResolutionEnabled(int userId) {
             synchronized (mPictureProfileLock) {
                 try {
                     if (mMediaQuality != null) {
@@ -965,7 +962,7 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) {
+        public void setAutoSoundQualityEnabled(boolean enabled, int userId) {
             if (!hasGlobalSoundQualityServicePermission()) {
                 mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
                         Binder.getCallingUid(), Binder.getCallingPid());
@@ -986,7 +983,7 @@
 
         @GuardedBy("mSoundProfileLock")
         @Override
-        public boolean isAutoSoundQualityEnabled(UserHandle user) {
+        public boolean isAutoSoundQualityEnabled(int userId) {
             synchronized (mSoundProfileLock) {
                 try {
                     if (mMediaQuality != null) {
@@ -1003,7 +1000,7 @@
 
         @GuardedBy("mAmbientBacklightLock")
         @Override
-        public boolean isAmbientBacklightEnabled(UserHandle user) {
+        public boolean isAmbientBacklightEnabled(int userId) {
             return false;
         }
     }
@@ -1428,11 +1425,17 @@
                     MediaQualityUtils.convertPersistableBundleToPictureParameterList(
                             params);
 
+            Parcel parcel = Parcel.obtain();
+            if (params != null) {
+                setVendorPictureParameters(pictureParameters, parcel, params);
+            }
+
             android.hardware.tv.mediaquality.PictureProfile toReturn =
                     new android.hardware.tv.mediaquality.PictureProfile();
             toReturn.pictureProfileId = id;
             toReturn.parameters = pictureParameters;
 
+            parcel.recycle();
             return toReturn;
         }
 
@@ -1738,4 +1741,16 @@
             return android.hardware.tv.mediaquality.IMediaQualityCallback.Stub.VERSION;
         }
     }
+
+    private void setVendorPictureParameters(
+            PictureParameters pictureParameters,
+            Parcel parcel,
+            PersistableBundle vendorPictureParameters) {
+        vendorPictureParameters.writeToParcel(parcel, 0);
+        byte[] vendorBundleToByteArray = parcel.marshall();
+        DefaultExtension defaultExtension = new DefaultExtension();
+        defaultExtension.bytes = Arrays.copyOf(
+                vendorBundleToByteArray, vendorBundleToByteArray.length);
+        pictureParameters.vendorPictureParameters.setParcelable(defaultExtension);
+    }
 }
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
index 88d3f1f..cf8b703 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
@@ -60,6 +60,11 @@
     private static final String TAG = "MediaQualityUtils";
     public static final String SETTINGS = "settings";
 
+    public static final SoundProfileHandle SOUND_PROFILE_HANDLE_NONE = new SoundProfileHandle();
+    static {
+        SOUND_PROFILE_HANDLE_NONE.id = -10000;
+    }
+
     /**
      * Convert PictureParameter List to PersistableBundle.
      */
@@ -368,323 +373,403 @@
         if (params.containsKey(PictureQuality.PARAMETER_BRIGHTNESS)) {
             pictureParams.add(PictureParameter.brightness(params.getLong(
                     PictureQuality.PARAMETER_BRIGHTNESS)));
+            params.remove(PictureQuality.PARAMETER_BRIGHTNESS);
         }
         if (params.containsKey(PictureQuality.PARAMETER_CONTRAST)) {
             pictureParams.add(PictureParameter.contrast(params.getInt(
                     PictureQuality.PARAMETER_CONTRAST)));
+            params.remove(PictureQuality.PARAMETER_CONTRAST);
         }
         if (params.containsKey(PictureQuality.PARAMETER_SHARPNESS)) {
             pictureParams.add(PictureParameter.sharpness(params.getInt(
                     PictureQuality.PARAMETER_SHARPNESS)));
+            params.remove(PictureQuality.PARAMETER_SHARPNESS);
         }
         if (params.containsKey(PictureQuality.PARAMETER_SATURATION)) {
             pictureParams.add(PictureParameter.saturation(params.getInt(
                     PictureQuality.PARAMETER_SATURATION)));
+            params.remove(PictureQuality.PARAMETER_SATURATION);
         }
         if (params.containsKey(PictureQuality.PARAMETER_HUE)) {
             pictureParams.add(PictureParameter.hue(params.getInt(
                     PictureQuality.PARAMETER_HUE)));
+            params.remove(PictureQuality.PARAMETER_HUE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)) {
             pictureParams.add(PictureParameter.colorTunerBrightness(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)) {
             pictureParams.add(PictureParameter.colorTunerSaturation(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE)) {
             pictureParams.add(PictureParameter.colorTunerHue(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_HUE)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_HUE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)) {
             pictureParams.add(PictureParameter.colorTunerRedOffset(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)) {
             pictureParams.add(PictureParameter.colorTunerGreenOffset(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)) {
             pictureParams.add(PictureParameter.colorTunerBlueOffset(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
             pictureParams.add(PictureParameter.colorTunerRedGain(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
             pictureParams.add(PictureParameter.colorTunerGreenGain(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
             pictureParams.add(PictureParameter.colorTunerBlueGain(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_NOISE_REDUCTION)) {
             pictureParams.add(PictureParameter.noiseReduction(
                     (byte) params.getInt(PictureQuality.PARAMETER_NOISE_REDUCTION)));
+            params.remove(PictureQuality.PARAMETER_NOISE_REDUCTION);
         }
         if (params.containsKey(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) {
             pictureParams.add(PictureParameter.mpegNoiseReduction(
                     (byte) params.getInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)));
+            params.remove(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION);
         }
         if (params.containsKey(PictureQuality.PARAMETER_FLESH_TONE)) {
             pictureParams.add(PictureParameter.fleshTone(
                     (byte) params.getInt(PictureQuality.PARAMETER_FLESH_TONE)));
+            params.remove(PictureQuality.PARAMETER_FLESH_TONE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_DECONTOUR)) {
             pictureParams.add(PictureParameter.deContour(
                     (byte) params.getInt(PictureQuality.PARAMETER_DECONTOUR)));
+            params.remove(PictureQuality.PARAMETER_DECONTOUR);
         }
         if (params.containsKey(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) {
             pictureParams.add(PictureParameter.dynamicLumaControl(
                     (byte) params.getInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)));
+            params.remove(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL);
         }
         if (params.containsKey(PictureQuality.PARAMETER_FILM_MODE)) {
             pictureParams.add(PictureParameter.filmMode(params.getBoolean(
                     PictureQuality.PARAMETER_FILM_MODE)));
+            params.remove(PictureQuality.PARAMETER_FILM_MODE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_BLUE_STRETCH)) {
             pictureParams.add(PictureParameter.blueStretch(params.getBoolean(
                     PictureQuality.PARAMETER_BLUE_STRETCH)));
+            params.remove(PictureQuality.PARAMETER_BLUE_STRETCH);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNE)) {
             pictureParams.add(PictureParameter.colorTune(params.getBoolean(
                     PictureQuality.PARAMETER_COLOR_TUNE)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) {
             pictureParams.add(PictureParameter.colorTemperature(
                     (byte) params.getInt(
                             PictureQuality.PARAMETER_COLOR_TEMPERATURE)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TEMPERATURE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_GLOBAL_DIMMING)) {
             pictureParams.add(PictureParameter.globeDimming(params.getBoolean(
                     PictureQuality.PARAMETER_GLOBAL_DIMMING)));
+            params.remove(PictureQuality.PARAMETER_GLOBAL_DIMMING);
         }
         if (params.containsKey(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)) {
             pictureParams.add(PictureParameter.autoPictureQualityEnabled(params.getBoolean(
                     PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)));
+            params.remove(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED);
         }
         if (params.containsKey(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)) {
             pictureParams.add(PictureParameter.autoSuperResolutionEnabled(params.getBoolean(
                     PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)));
+            params.remove(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
             pictureParams.add(PictureParameter.colorTemperatureRedGain(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
             pictureParams.add(PictureParameter.colorTemperatureGreenGain(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
             pictureParams.add(PictureParameter.colorTemperatureBlueGain(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_LEVEL_RANGE)) {
             pictureParams.add(PictureParameter.levelRange(
                     (byte) params.getInt(PictureQuality.PARAMETER_LEVEL_RANGE)));
+            params.remove(PictureQuality.PARAMETER_LEVEL_RANGE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_GAMUT_MAPPING)) {
             pictureParams.add(PictureParameter.gamutMapping(params.getBoolean(
                     PictureQuality.PARAMETER_GAMUT_MAPPING)));
+            params.remove(PictureQuality.PARAMETER_GAMUT_MAPPING);
         }
         if (params.containsKey(PictureQuality.PARAMETER_PC_MODE)) {
             pictureParams.add(PictureParameter.pcMode(params.getBoolean(
                     PictureQuality.PARAMETER_PC_MODE)));
+            params.remove(PictureQuality.PARAMETER_PC_MODE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_LOW_LATENCY)) {
             pictureParams.add(PictureParameter.lowLatency(params.getBoolean(
                     PictureQuality.PARAMETER_LOW_LATENCY)));
+            params.remove(PictureQuality.PARAMETER_LOW_LATENCY);
         }
         if (params.containsKey(PictureQuality.PARAMETER_VRR)) {
             pictureParams.add(PictureParameter.vrr(params.getBoolean(
                     PictureQuality.PARAMETER_VRR)));
+            params.remove(PictureQuality.PARAMETER_VRR);
         }
         if (params.containsKey(PictureQuality.PARAMETER_CVRR)) {
             pictureParams.add(PictureParameter.cvrr(params.getBoolean(
                     PictureQuality.PARAMETER_CVRR)));
+            params.remove(PictureQuality.PARAMETER_CVRR);
         }
         if (params.containsKey(PictureQuality.PARAMETER_HDMI_RGB_RANGE)) {
             pictureParams.add(PictureParameter.hdmiRgbRange(
                     (byte) params.getInt(PictureQuality.PARAMETER_HDMI_RGB_RANGE)));
+            params.remove(PictureQuality.PARAMETER_HDMI_RGB_RANGE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_SPACE)) {
             pictureParams.add(PictureParameter.colorSpace(
                     (byte) params.getInt(PictureQuality.PARAMETER_COLOR_SPACE)));
+            params.remove(PictureQuality.PARAMETER_COLOR_SPACE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)) {
             pictureParams.add(PictureParameter.panelInitMaxLuminceNits(
                     params.getInt(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)));
+            params.remove(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS);
         }
         if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)) {
             pictureParams.add(PictureParameter.panelInitMaxLuminceValid(
                     params.getBoolean(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)));
+            params.remove(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID);
         }
         if (params.containsKey(PictureQuality.PARAMETER_GAMMA)) {
             pictureParams.add(PictureParameter.gamma(
                     (byte) params.getInt(PictureQuality.PARAMETER_GAMMA)));
+            params.remove(PictureQuality.PARAMETER_GAMMA);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) {
             pictureParams.add(PictureParameter.colorTemperatureRedOffset(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)) {
             pictureParams.add(PictureParameter.colorTemperatureGreenOffset(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)) {
             pictureParams.add(PictureParameter.colorTemperatureBlueOffset(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET);
         }
         if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_RED)) {
             pictureParams.add(PictureParameter.elevenPointRed(params.getIntArray(
                     PictureQuality.PARAMETER_ELEVEN_POINT_RED)));
+            params.remove(PictureQuality.PARAMETER_ELEVEN_POINT_RED);
         }
         if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)) {
             pictureParams.add(PictureParameter.elevenPointGreen(params.getIntArray(
                     PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)));
+            params.remove(PictureQuality.PARAMETER_ELEVEN_POINT_GREEN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)) {
             pictureParams.add(PictureParameter.elevenPointBlue(params.getIntArray(
                     PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)));
+            params.remove(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)) {
             pictureParams.add(PictureParameter.lowBlueLight(
                     (byte) params.getInt(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)));
+            params.remove(PictureQuality.PARAMETER_LOW_BLUE_LIGHT);
         }
         if (params.containsKey(PictureQuality.PARAMETER_LD_MODE)) {
             pictureParams.add(PictureParameter.LdMode(
                     (byte) params.getInt(PictureQuality.PARAMETER_LD_MODE)));
+            params.remove(PictureQuality.PARAMETER_LD_MODE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_GAIN)) {
             pictureParams.add(PictureParameter.osdRedGain(params.getInt(
                     PictureQuality.PARAMETER_OSD_RED_GAIN)));
+            params.remove(PictureQuality.PARAMETER_OSD_RED_GAIN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_OSD_GREEN_GAIN)) {
             pictureParams.add(PictureParameter.osdGreenGain(params.getInt(
                     PictureQuality.PARAMETER_OSD_GREEN_GAIN)));
+            params.remove(PictureQuality.PARAMETER_OSD_GREEN_GAIN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_OSD_BLUE_GAIN)) {
             pictureParams.add(PictureParameter.osdBlueGain(params.getInt(
                     PictureQuality.PARAMETER_OSD_BLUE_GAIN)));
+            params.remove(PictureQuality.PARAMETER_OSD_BLUE_GAIN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_OFFSET)) {
             pictureParams.add(PictureParameter.osdRedOffset(params.getInt(
                     PictureQuality.PARAMETER_OSD_RED_OFFSET)));
+            params.remove(PictureQuality.PARAMETER_OSD_RED_OFFSET);
         }
         if (params.containsKey(PictureQuality.PARAMETER_OSD_GREEN_OFFSET)) {
             pictureParams.add(PictureParameter.osdGreenOffset(params.getInt(
                     PictureQuality.PARAMETER_OSD_GREEN_OFFSET)));
+            params.remove(PictureQuality.PARAMETER_OSD_GREEN_OFFSET);
         }
         if (params.containsKey(PictureQuality.PARAMETER_OSD_BLUE_OFFSET)) {
             pictureParams.add(PictureParameter.osdBlueOffset(params.getInt(
                     PictureQuality.PARAMETER_OSD_BLUE_OFFSET)));
+            params.remove(PictureQuality.PARAMETER_OSD_BLUE_OFFSET);
         }
         if (params.containsKey(PictureQuality.PARAMETER_OSD_HUE)) {
             pictureParams.add(PictureParameter.osdHue(params.getInt(
                     PictureQuality.PARAMETER_OSD_HUE)));
+            params.remove(PictureQuality.PARAMETER_OSD_HUE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_OSD_SATURATION)) {
             pictureParams.add(PictureParameter.osdSaturation(params.getInt(
                     PictureQuality.PARAMETER_OSD_SATURATION)));
+            params.remove(PictureQuality.PARAMETER_OSD_SATURATION);
         }
         if (params.containsKey(PictureQuality.PARAMETER_OSD_CONTRAST)) {
             pictureParams.add(PictureParameter.osdContrast(params.getInt(
                     PictureQuality.PARAMETER_OSD_CONTRAST)));
+            params.remove(PictureQuality.PARAMETER_OSD_CONTRAST);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)) {
             pictureParams.add(PictureParameter.colorTunerSwitch(params.getBoolean(
                     PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_SWITCH);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)) {
             pictureParams.add(PictureParameter.colorTunerHueRed(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)) {
             pictureParams.add(PictureParameter.colorTunerHueGreen(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)) {
             pictureParams.add(PictureParameter.colorTunerHueBlue(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)) {
             pictureParams.add(PictureParameter.colorTunerHueCyan(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)) {
             pictureParams.add(PictureParameter.colorTunerHueMagenta(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)) {
             pictureParams.add(PictureParameter.colorTunerHueYellow(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)) {
             pictureParams.add(PictureParameter.colorTunerHueFlesh(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)) {
             pictureParams.add(PictureParameter.colorTunerSaturationRed(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)) {
             pictureParams.add(PictureParameter.colorTunerSaturationGreen(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)) {
             pictureParams.add(PictureParameter.colorTunerSaturationBlue(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)) {
             pictureParams.add(PictureParameter.colorTunerSaturationCyan(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)) {
             pictureParams.add(PictureParameter.colorTunerSaturationMagenta(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)) {
             pictureParams.add(PictureParameter.colorTunerSaturationYellow(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)) {
             pictureParams.add(PictureParameter.colorTunerSaturationFlesh(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)) {
             pictureParams.add(PictureParameter.colorTunerLuminanceRed(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)) {
             pictureParams.add(PictureParameter.colorTunerLuminanceGreen(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)) {
             pictureParams.add(PictureParameter.colorTunerLuminanceBlue(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)) {
             pictureParams.add(PictureParameter.colorTunerLuminanceCyan(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)) {
             pictureParams.add(PictureParameter.colorTunerLuminanceMagenta(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)) {
             pictureParams.add(PictureParameter.colorTunerLuminanceYellow(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW);
         }
         if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)) {
             pictureParams.add(PictureParameter.colorTunerLuminanceFlesh(params.getInt(
                     PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)));
+            params.remove(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH);
         }
         if (params.containsKey(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)) {
             pictureParams.add(PictureParameter.pictureQualityEventType(
                     (byte) params.getInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)));
+            params.remove(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE);
         }
         return pictureParams.toArray(new PictureParameter[0]);
     }
@@ -1022,7 +1107,7 @@
                 getInputId(cursor),
                 getPackageName(cursor),
                 jsonToPersistableBundle(getSettingsString(cursor)),
-                SoundProfileHandle.NONE
+                SOUND_PROFILE_HANDLE_NONE
         );
     }
 
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index dd52cce..3f2c222 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -46,7 +46,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 
 public class ConditionProviders extends ManagedServices {
 
@@ -203,14 +202,7 @@
 
     @Override
     protected void loadDefaultsFromConfig() {
-        for (String dndPackage : getDefaultDndAccessPackages(mContext)) {
-            addDefaultComponentOrPackage(dndPackage);
-        }
-    }
-
-    static List<String> getDefaultDndAccessPackages(Context context) {
-        ArrayList<String> packages = new ArrayList<>();
-        String defaultDndAccess = context.getResources().getString(
+        String defaultDndAccess = mContext.getResources().getString(
                 R.string.config_defaultDndAccessPackages);
         if (defaultDndAccess != null) {
             String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
@@ -218,10 +210,9 @@
                 if (TextUtils.isEmpty(dnds[i])) {
                     continue;
                 }
-                packages.add(dnds[i]);
+                addDefaultComponentOrPackage(dnds[i]);
             }
         }
-        return packages;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 6e5308e..3f4df1d 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -1002,8 +1002,7 @@
     private FullyQualifiedGroupKey getSectionGroupKeyWithFallback(final NotificationRecord record) {
         final NotificationSectioner sectioner = getSection(record);
         if (sectioner != null) {
-            return new FullyQualifiedGroupKey(record.getUserId(), record.getSbn().getPackageName(),
-                sectioner);
+            return FullyQualifiedGroupKey.forRecord(record, sectioner);
         } else {
             return getPreviousValidSectionKey(record);
         }
@@ -1105,6 +1104,49 @@
         }
     }
 
+    /**
+     * Called when a group summary is posted. If there are any ungrouped notifications that are
+     * in that group, remove them as they are no longer candidates for autogrouping.
+     *
+     * @param summaryRecord the NotificationRecord for the newly posted group summary
+     * @param notificationList the full notification list from NotificationManagerService
+     */
+    @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING)
+    protected void onGroupSummaryAdded(final NotificationRecord summaryRecord,
+            final List<NotificationRecord> notificationList) {
+        String groupKey = summaryRecord.getSbn().getGroup();
+        synchronized (mAggregatedNotifications) {
+            final NotificationSectioner sectioner = getSection(summaryRecord);
+            if (sectioner == null) {
+                Slog.w(TAG, "onGroupSummaryAdded " + summaryRecord + ": no valid section found");
+                return;
+            }
+
+            FullyQualifiedGroupKey aggregateGroupKey = FullyQualifiedGroupKey.forRecord(
+                    summaryRecord, sectioner);
+            ArrayMap<String, NotificationAttributes> ungrouped =
+                    mUngroupedAbuseNotifications.getOrDefault(aggregateGroupKey,
+                            new ArrayMap<>());
+            if (ungrouped.isEmpty()) {
+                // don't bother looking through the notification list if there are no pending
+                // ungrouped notifications in this section (likely to be the most common case)
+                return;
+            }
+
+            // Look through full notification list for any notifications belonging to this group;
+            // remove from ungrouped map if needed, as the presence of the summary means they will
+            // now be grouped
+            for (NotificationRecord r : notificationList) {
+                if (!r.getNotification().isGroupSummary()
+                        && groupKey.equals(r.getSbn().getGroup())
+                        && ungrouped.containsKey(r.getKey())) {
+                    ungrouped.remove(r.getKey());
+                }
+            }
+            mUngroupedAbuseNotifications.put(aggregateGroupKey, ungrouped);
+        }
+    }
+
     private record NotificationMoveOp(NotificationRecord record, FullyQualifiedGroupKey oldGroup,
                                       FullyQualifiedGroupKey newGroup) { }
 
@@ -1496,8 +1538,8 @@
 
     private boolean isNotificationAggregatedInSection(NotificationRecord record,
             NotificationSectioner sectioner) {
-        final FullyQualifiedGroupKey fullAggregateGroupKey = new FullyQualifiedGroupKey(
-                record.getUserId(), record.getSbn().getPackageName(), sectioner);
+        final FullyQualifiedGroupKey fullAggregateGroupKey = FullyQualifiedGroupKey.forRecord(
+                record, sectioner);
         return record.getGroupKey().equals(fullAggregateGroupKey.toString());
     }
 
@@ -1895,6 +1937,12 @@
             this(userId, pkg, AGGREGATE_GROUP_KEY + (sectioner != null ? sectioner.mName : ""));
         }
 
+        static FullyQualifiedGroupKey forRecord(NotificationRecord record,
+                @Nullable NotificationSectioner sectioner) {
+            return new FullyQualifiedGroupKey(record.getUserId(), record.getSbn().getPackageName(),
+                    sectioner);
+        }
+
         @Override
         public String toString() {
             return userId + "|" + pkg + "|" + "g:" + groupName;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0f1d28d..78554bd 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -764,6 +764,8 @@
     private long mLastOverRateLogTime;
     private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
 
+    private boolean mRedactOtpNotifications = true;
+
     private NotificationHistoryManager mHistoryManager;
     protected SnoozeHelper mSnoozeHelper;
     private TimeToLiveHelper mTtlHelper;
@@ -1075,6 +1077,7 @@
                         summary.getSbn().getNotification().getGroupAlertBehavior();
 
         if (notificationForceGrouping()) {
+            summary.getNotification().flags |= Notification.FLAG_SILENT;
             if (!summary.getChannel().getId().equals(summaryAttr.channelId)) {
                 NotificationChannel newChannel = mPreferencesHelper.getNotificationChannel(pkg,
                         summary.getUid(), summaryAttr.channelId, false);
@@ -2409,6 +2412,8 @@
                 = Secure.getUriFor(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
         private final Uri SHOW_NOTIFICATION_SNOOZE
                 = Secure.getUriFor(Secure.SHOW_NOTIFICATION_SNOOZE);
+        private final Uri REDACT_OTP_NOTIFICATIONS = Settings.Global.getUriFor(
+                Settings.Global.REDACT_OTP_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS);
 
         SettingsObserver(Handler handler) {
             super(handler);
@@ -2434,6 +2439,8 @@
 
             resolver.registerContentObserver(SHOW_NOTIFICATION_SNOOZE,
                     false, this, USER_ALL);
+            resolver.registerContentObserver(REDACT_OTP_NOTIFICATIONS,
+                    false, this, USER_ALL);
 
             update(null);
         }
@@ -2480,6 +2487,10 @@
                     unsnoozeAll();
                 }
             }
+            if (REDACT_OTP_NOTIFICATIONS.equals(uri)) {
+                mRedactOtpNotifications = Settings.Global.getInt(resolver,
+                        Settings.Global.REDACT_OTP_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS, 1) != 0;
+            }
         }
 
         public void update(Uri uri, int userId) {
@@ -6162,10 +6173,15 @@
         }
 
         @Override
-        public Map<String, AutomaticZenRule> getAutomaticZenRules() {
+        public ParceledListSlice getAutomaticZenRules() {
             int callingUid = Binder.getCallingUid();
             enforcePolicyAccess(callingUid, "getAutomaticZenRules");
-            return mZenModeHelper.getAutomaticZenRules(getCallingZenUser(), callingUid);
+            List<AutomaticZenRule.AzrWithId> ruleList = new ArrayList<>();
+            for (Map.Entry<String, AutomaticZenRule> rule : mZenModeHelper.getAutomaticZenRules(
+                    getCallingZenUser(), callingUid).entrySet()) {
+                ruleList.add(new AutomaticZenRule.AzrWithId(rule.getKey(), rule.getValue()));
+            }
+            return new ParceledListSlice<>(ruleList);
         }
 
         @Override
@@ -7450,6 +7466,7 @@
                     // Override group key early for forced grouped notifications
                     r.setOverrideGroupKey(groupName);
                 }
+                r.getNotification().flags |= Notification.FLAG_SILENT;
             }
 
             addAutoGroupAdjustment(r, groupName);
@@ -10196,6 +10213,12 @@
         }
         if (isSummary) {
             mSummaryByGroupKey.put(group, r);
+
+            if (notificationForceGrouping()) {
+                // If any formerly-ungrouped notifications will be grouped by this summary, update
+                // accordingly.
+                mGroupHelper.onGroupSummaryAdded(r, mNotificationList);
+            }
         }
 
         FlagChecker childrenFlagChecker = (flags) -> {
@@ -13445,13 +13468,13 @@
                 StatusBarNotification oldRedactedSbn = null;
                 boolean isNewSensitive = hasSensitiveContent(r);
                 boolean isOldSensitive = hasSensitiveContent(old);
+                boolean redactionEnabled = redactSensitiveNotificationsFromUntrustedListeners()
+                        && mRedactOtpNotifications;
 
                 for (final ManagedServiceInfo info : getServices()) {
                     boolean isTrusted = isUidTrusted(info.uid);
-                    boolean sendRedacted = redactSensitiveNotificationsFromUntrustedListeners()
-                            && isNewSensitive && !isTrusted;
-                    boolean sendOldRedacted = redactSensitiveNotificationsFromUntrustedListeners()
-                            && isOldSensitive && !isTrusted;
+                    boolean sendRedacted = redactionEnabled && isNewSensitive && !isTrusted;
+                    boolean sendOldRedacted = redactionEnabled && isOldSensitive && !isTrusted;
                     boolean sbnVisible = isVisibleToListener(sbn, r.getNotificationType(), info);
                     boolean oldSbnVisible = (oldSbn != null)
                             && isVisibleToListener(oldSbn, old.getNotificationType(), info);
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index bc987ed..ffc7c7b 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -45,6 +45,7 @@
 import android.os.RemoteException;
 import android.os.ShellCommand;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
@@ -81,6 +82,7 @@
             + "  snooze --for <msec> <notification-key>\n"
             + "  unsnooze <notification-key>\n"
             + "  set_exempt_th_force_grouping [true|false]\n"
+            + "  redact_otp_from_untrusted_listeners [true|false]\n"
             ;
 
     private static final String NOTIFY_USAGE =
@@ -431,6 +433,14 @@
                     mDirectService.setTestHarnessExempted(exemptTestHarnessFromForceGrouping);
                     break;
                 }
+                case "redact_otp_from_untrusted_listeners": {
+                    String arg = getNextArgRequired();
+                    final int allow = "true".equals(arg) || "1".equals(arg) ? 1 : 0;
+                    Settings.Global.putInt(mDirectService.getContext().getContentResolver(),
+                            Settings.Global.REDACT_OTP_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS,
+                            allow);
+                    break;
+                }
                 default:
                     return handleDefaultCommands(cmd);
             }
diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING
index dc7129cd..ea7ee4a 100644
--- a/services/core/java/com/android/server/notification/TEST_MAPPING
+++ b/services/core/java/com/android/server/notification/TEST_MAPPING
@@ -4,7 +4,10 @@
       "name": "CtsNotificationTestCases_notification"
     },
     {
-      "name": "FrameworksUiServicesTests_notification"
+      "name": "FrameworksUiServicesNotificationTests"
+    },
+    {
+      "name": "FrameworksUiServicesZenTests"
     }
   ],
   "postsubmit": [
diff --git a/services/core/java/com/android/server/notification/ZenConfigTrimmer.java b/services/core/java/com/android/server/notification/ZenConfigTrimmer.java
deleted file mode 100644
index d65954d..0000000
--- a/services/core/java/com/android/server/notification/ZenConfigTrimmer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.notification;
-
-import android.content.Context;
-import android.os.Parcel;
-import android.service.notification.SystemZenRules;
-import android.service.notification.ZenModeConfig;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-
-class ZenConfigTrimmer {
-
-    private static final String TAG = "ZenConfigTrimmer";
-    private static final int MAXIMUM_PARCELED_SIZE = 150_000; // bytes
-
-    private final HashSet<String> mTrustedPackages;
-
-    ZenConfigTrimmer(Context context) {
-        mTrustedPackages = new HashSet<>();
-        mTrustedPackages.add(SystemZenRules.PACKAGE_ANDROID);
-        mTrustedPackages.addAll(ConditionProviders.getDefaultDndAccessPackages(context));
-    }
-
-    void trimToMaximumSize(ZenModeConfig config) {
-        Map<String, PackageRules> rulesPerPackage = new HashMap<>();
-        for (ZenModeConfig.ZenRule rule : config.automaticRules.values()) {
-            PackageRules pkgRules = rulesPerPackage.computeIfAbsent(rule.pkg, PackageRules::new);
-            pkgRules.mRules.add(rule);
-        }
-
-        int totalSize = 0;
-        for (PackageRules pkgRules : rulesPerPackage.values()) {
-            totalSize += pkgRules.dataSize();
-        }
-
-        if (totalSize > MAXIMUM_PARCELED_SIZE) {
-            List<PackageRules> deletionCandidates = new ArrayList<>();
-            for (PackageRules pkgRules : rulesPerPackage.values()) {
-                if (!mTrustedPackages.contains(pkgRules.mPkg)) {
-                    deletionCandidates.add(pkgRules);
-                }
-            }
-            deletionCandidates.sort(Comparator.comparingInt(PackageRules::dataSize).reversed());
-
-            evictPackagesFromConfig(config, deletionCandidates, totalSize);
-        }
-    }
-
-    private static void evictPackagesFromConfig(ZenModeConfig config,
-            List<PackageRules> deletionCandidates, int currentSize) {
-        while (currentSize > MAXIMUM_PARCELED_SIZE && !deletionCandidates.isEmpty()) {
-            PackageRules rulesToDelete = deletionCandidates.removeFirst();
-            Slog.w(TAG, String.format("Evicting %s zen rules from package '%s' (%s bytes)",
-                    rulesToDelete.mRules.size(), rulesToDelete.mPkg, rulesToDelete.dataSize()));
-
-            for (ZenModeConfig.ZenRule rule : rulesToDelete.mRules) {
-                config.automaticRules.remove(rule.id);
-            }
-
-            currentSize -= rulesToDelete.dataSize();
-        }
-    }
-
-    private static class PackageRules {
-        private final String mPkg;
-        private final List<ZenModeConfig.ZenRule> mRules;
-        private int mParceledSize = -1;
-
-        PackageRules(String pkg) {
-            mPkg = pkg;
-            mRules = new ArrayList<>();
-        }
-
-        private int dataSize() {
-            if (mParceledSize >= 0) {
-                return mParceledSize;
-            }
-            Parcel parcel = Parcel.obtain();
-            try {
-                parcel.writeParcelableList(mRules, 0);
-                mParceledSize = parcel.dataSize();
-                return mParceledSize;
-            } finally {
-                parcel.recycle();
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8b09c2a..889df51 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -48,7 +48,6 @@
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.server.notification.Flags.preventZenDeviceEffectsWhileDriving;
-import static com.android.server.notification.Flags.limitZenConfigSize;
 
 import static java.util.Objects.requireNonNull;
 
@@ -193,7 +192,6 @@
     private final ConditionProviders.Config mServiceConfig;
     private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
     private final ZenModeEventLogger mZenModeEventLogger;
-    private final ZenConfigTrimmer mConfigTrimmer;
 
     @VisibleForTesting protected int mZenMode;
     @VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
@@ -228,7 +226,6 @@
         mClock = clock;
         addCallback(mMetrics);
         mAppOps = context.getSystemService(AppOpsManager.class);
-        mConfigTrimmer = new ZenConfigTrimmer(mContext);
 
         mDefaultConfig = Flags.modesUi()
                 ? ZenModeConfig.getDefaultConfig()
@@ -2064,20 +2061,20 @@
                 Log.w(TAG, "Invalid config in setConfigLocked; " + config);
                 return false;
             }
-            if (limitZenConfigSize() && (origin == ORIGIN_APP || origin == ORIGIN_USER_IN_APP)) {
-                mConfigTrimmer.trimToMaximumSize(config);
-            }
-
             if (config.user != mUser) {
                 // simply store away for background users
-                mConfigs.put(config.user, config);
+                synchronized (mConfigLock) {
+                    mConfigs.put(config.user, config);
+                }
                 if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
                 return true;
             }
             // handle CPS backed conditions - danger! may modify config
             mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
 
-            mConfigs.put(config.user, config);
+            synchronized (mConfigLock) {
+                mConfigs.put(config.user, config);
+            }
             if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
             ZenLog.traceConfig(origin, reason, triggeringComponent, mConfig, config, callingUid);
 
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 346d65a..76cd5c8 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -212,16 +212,6 @@
 }
 
 flag {
-  name: "limit_zen_config_size"
-  namespace: "systemui"
-  description: "Enforce a maximum (serialized) size for the Zen configuration"
-  bug: "387498139"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "managed_services_concurrent_multiuser"
   namespace: "systemui"
   description: "Enables ManagedServices to support Concurrent multi user environment"
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index ee29849..737d943 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -308,7 +308,9 @@
             Slog.d(TAG, "onPackageRemoved pkgName=" + pkgName + " userId=" + userId);
         }
         // Update the state of all overlays that target this package.
-        final Set<UserPackage> targets = updateOverlaysForTarget(pkgName, userId, 0 /* flags */);
+        Set<UserPackage> targets = Collections.emptySet();
+        targets = CollectionUtils.addAll(targets,
+                updateOverlaysForTarget(pkgName, userId, 0 /* flags */));
 
         // Remove all the overlays this package declares.
         return CollectionUtils.addAll(targets,
diff --git a/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java b/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
index 871d12e..7791f51 100644
--- a/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
+++ b/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java
@@ -38,6 +38,7 @@
 
 import dalvik.system.VMDebug;
 
+import java.lang.reflect.Executable;
 import java.lang.reflect.Method;
 import java.util.NoSuchElementException;
 import java.util.Objects;
@@ -95,10 +96,16 @@
                 }
             }
 
-            Method method = MethodDescriptorParser.parseMethodDescriptor(
+            Executable executable = MethodDescriptorParser.parseMethodDescriptor(
                     getClass().getClassLoader(), methodDescriptor);
-            VMDebug.ExecutableMethodFileOffsets location =
-                    VMDebug.getExecutableMethodFileOffsets(method);
+            VMDebug.ExecutableMethodFileOffsets location;
+            if (com.android.art.flags.Flags.executableMethodFileOffsetsV2()) {
+                location = VMDebug.getExecutableMethodFileOffsets(executable);
+            } else if (executable instanceof Method) {
+                location = VMDebug.getExecutableMethodFileOffsets((Method) executable);
+            } else {
+                throw new UnsupportedOperationException();
+            }
 
             try {
                 if (location == null) {
diff --git a/services/core/java/com/android/server/os/instrumentation/OWNERS b/services/core/java/com/android/server/os/instrumentation/OWNERS
new file mode 100644
index 0000000..2522426
--- /dev/null
+++ b/services/core/java/com/android/server/os/instrumentation/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/UprobeStats:/OWNERS
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 7349204..3361dbc 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -644,20 +644,6 @@
         return mScanResult.mPkgSetting;
     }
 
-    @Nullable
-    public PackageSetting getRealPackageSetting() {
-        // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous
-        //  setting needs to be passed to have a comparison, hide it behind an immutable
-        //  interface. There's no good reason to have 3 different ways to access the real
-        //  PackageSetting object, only one of which is actually correct.
-        PackageSetting realPkgSetting = isExistingSettingCopied()
-                ? getScanRequestPackageSetting() : getScannedPackageSetting();
-        if (realPkgSetting == null) {
-            realPkgSetting = getScannedPackageSetting();
-        }
-        return realPkgSetting;
-    }
-
     public boolean isExistingSettingCopied() {
         assertScanResultExists();
         return mScanResult.mExistingSettingCopied;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index cf598e8..62264dd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -96,6 +96,7 @@
 import android.os.ServiceSpecificException;
 import android.os.ShellCommand;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -1564,6 +1565,12 @@
     private int doRunInstall(final InstallParams params) throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
 
+        // Do not allow app installation if boot has not completed already
+        if (!SystemProperties.getBoolean("sys.boot_completed", false)) {
+            pw.println("Error: device is still booting.");
+            return 1;
+        }
+
         int requestUserId = params.userId;
         if (requestUserId != UserHandle.USER_ALL && requestUserId != UserHandle.USER_CURRENT) {
             UserManagerInternal umi =
@@ -2174,6 +2181,13 @@
 
     private int runUninstall() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
+
+        // Do not allow app uninstallation if boot has not completed already
+        if (!SystemProperties.getBoolean("sys.boot_completed", false)) {
+            pw.println("Error: device is still booting.");
+            return 1;
+        }
+
         int flags = 0;
         int userId = UserHandle.USER_ALL;
         long versionCode = PackageManager.VERSION_CODE_HIGHEST;
diff --git a/services/core/java/com/android/server/pm/ProtectedPackages.java b/services/core/java/com/android/server/pm/ProtectedPackages.java
index 524252c..e715881 100644
--- a/services/core/java/com/android/server/pm/ProtectedPackages.java
+++ b/services/core/java/com/android/server/pm/ProtectedPackages.java
@@ -20,6 +20,7 @@
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.SparseArray;
 
@@ -27,6 +28,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -164,4 +166,13 @@
         return hasDeviceOwnerOrProfileOwner(userId, packageName)
                 || isProtectedPackage(userId, packageName);
     }
+
+    /**
+     * Returns {@code true} if a given package is the device provisioning package. Otherwise,
+     * returns {@code false}.
+     */
+    public synchronized boolean isDeviceProvisioningPackage(String packageName) {
+        return !TextUtils.isEmpty(mDeviceProvisioningPackage) && Objects.equals(
+                mDeviceProvisioningPackage, packageName);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 1fda478..92e8eb9 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -133,6 +133,38 @@
       ]
     },
     {
+      "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+      "file_patterns": [
+          "core/java/.*Install.*",
+          "services/core/.*Install.*",
+          "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+      "file_patterns": [
+          "core/java/.*Install.*",
+          "services/core/.*Install.*",
+          "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
       "name": "CtsPackageInstallerCUJMultiUsersTestCases",
       "file_patterns": [
         "core/java/.*Install.*",
@@ -288,6 +320,38 @@
       ]
     },
     {
+      "name": "CtsPackageInstallerCUJInstallationViaIntentForResultTestCases",
+      "file_patterns": [
+          "core/java/.*Install.*",
+          "services/core/.*Install.*",
+          "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationViaSessionTestCases",
+      "file_patterns": [
+          "core/java/.*Install.*",
+          "services/core/.*Install.*",
+          "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
       "name": "CtsPackageInstallerCUJMultiUsersTestCases",
       "file_patterns": [
         "core/java/.*Install.*",
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 93837b3..233b577 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1078,7 +1078,7 @@
         mUserVisibilityMediator = new UserVisibilityMediator(mHandler);
         mUserDataPreparer = userDataPreparer;
         mUserTypes = UserTypeFactory.getUserTypes();
-        invalidateOwnerNameIfNecessary(context.getResources(), true /* forceUpdate */);
+        invalidateOwnerNameIfNecessary(getContextResources(), true /* forceUpdate */);
         synchronized (mPackagesLock) {
             mUsersDir = new File(dataDir, USER_INFO_DIR);
             mUsersDir.mkdirs();
@@ -1184,6 +1184,15 @@
                 && android.multiuser.Flags.enablePrivateSpaceFeatures();
     }
 
+    private Resources getSystemResources() {
+        return android.multiuser.Flags.useUnifiedResources()
+                ? getContextResources() : Resources.getSystem();
+    }
+
+    private Resources getContextResources() {
+        return mContext.getResources();
+    }
+
     /**
      * This method retrieves the  {@link UserManagerInternal} only for the purpose of
      * PackageManagerService construction.
@@ -1223,7 +1232,7 @@
             // Avoid marking pre-created users for removal.
             return;
         }
-        if (ui.lastLoggedInTime == 0 && ui.isGuest() && Resources.getSystem().getBoolean(
+        if (ui.lastLoggedInTime == 0 && ui.isGuest() && getSystemResources().getBoolean(
                 com.android.internal.R.bool.config_guestUserAutoCreated)) {
             // Avoid marking auto-created but not-yet-logged-in guest user for removal. Because a
             // new one will be created anyway, and this one doesn't have any personal data in it yet
@@ -1402,7 +1411,7 @@
         }
 
         if (isHeadlessSystemUserMode()) {
-            final int bootStrategy = mContext.getResources()
+            final int bootStrategy = getContextResources()
                     .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
             switch (bootStrategy) {
                 case BOOT_TO_PREVIOUS_OR_FIRST_SWITCHABLE_USER:
@@ -2983,7 +2992,7 @@
     boolean isUserSwitcherEnabled(@UserIdInt int userId) {
         boolean multiUserSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.USER_SWITCHER_ENABLED,
-                Resources.getSystem().getBoolean(com.android.internal
+                getSystemResources().getBoolean(com.android.internal
                         .R.bool.config_showUserSwitcherByDefault) ? 1 : 0) != 0;
 
         return UserManager.supportsMultipleUsers()
@@ -4672,7 +4681,7 @@
             UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM);
             if ("Primary".equals(userData.info.name)) {
                 userData.info.name =
-                        mContext.getResources().getString(com.android.internal.R.string.owner_name);
+                        getContextResources().getString(com.android.internal.R.string.owner_name);
                 userIdsToWrite.add(userData.info.id);
             }
             userVersion = 1;
@@ -5002,7 +5011,7 @@
 
         final Bundle restrictions = new Bundle();
         try {
-            final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(
+            final String[] defaultFirstUserRestrictions = getContextResources().getStringArray(
                     com.android.internal.R.array.config_defaultFirstUserRestrictions);
             for (String userRestriction : defaultFirstUserRestrictions) {
                 if (UserRestrictionsUtils.isValidRestriction(userRestriction)) {
@@ -6178,7 +6187,7 @@
             // If the user switch hasn't been explicitly toggled on or off by the user, turn it on.
             if (android.provider.Settings.Global.getString(mContext.getContentResolver(),
                     android.provider.Settings.Global.USER_SWITCHER_ENABLED) == null) {
-                if (Resources.getSystem().getBoolean(
+                if (getSystemResources().getBoolean(
                         com.android.internal.R.bool.config_enableUserSwitcherUponUserCreation)) {
                     android.provider.Settings.Global.putInt(mContext.getContentResolver(),
                             android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1);
@@ -7570,10 +7579,16 @@
 
         // Dump some capabilities
         pw.println();
-        pw.print("  Max users: " + UserManager.getMaxSupportedUsers());
+        int effectiveMaxSupportedUsers = UserManager.getMaxSupportedUsers();
+        pw.print("  Max users: " + effectiveMaxSupportedUsers);
+        int defaultMaxSupportedUsers = getSystemResources()
+                .getInteger(R.integer.config_multiuserMaximumUsers);
+        if (effectiveMaxSupportedUsers != defaultMaxSupportedUsers) {
+            pw.print(" (built-in value: " + defaultMaxSupportedUsers + ")");
+        }
         pw.println(" (limit reached: " + isUserLimitReached() + ")");
         pw.println("  Supports switchable users: " + UserManager.supportsMultipleUsers());
-        pw.println("  All guests ephemeral: " + Resources.getSystem().getBoolean(
+        pw.println("  All guests ephemeral: " + getSystemResources().getBoolean(
                 com.android.internal.R.bool.config_guestUserEphemeral));
         pw.println("  Force ephemeral users: " + mForceEphemeralUsers);
         final boolean isHeadlessSystemUserMode = isHeadlessSystemUserMode();
@@ -7588,7 +7603,7 @@
             }
         }
         if (isHeadlessSystemUserMode) {
-            pw.println("  Can switch to headless system user: " + Resources.getSystem()
+            pw.println("  Can switch to headless system user: " + getSystemResources()
                     .getBoolean(com.android.internal.R.bool.config_canSwitchToHeadlessSystemUser));
         }
         pw.println("  User version: " + mUserVersion);
@@ -8530,8 +8545,7 @@
      * or downgraded to non-admin status.
      */
     public boolean isMainUserPermanentAdmin() {
-        return Resources.getSystem()
-                .getBoolean(R.bool.config_isMainUserPermanentAdmin);
+        return getSystemResources().getBoolean(R.bool.config_isMainUserPermanentAdmin);
     }
 
     /**
@@ -8540,8 +8554,7 @@
      * it is not a full user.
      */
     public boolean canSwitchToHeadlessSystemUser() {
-        return Resources.getSystem()
-                .getBoolean(R.bool.config_canSwitchToHeadlessSystemUser);
+        return getSystemResources().getBoolean(R.bool.config_canSwitchToHeadlessSystemUser);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 58c5b1c..5798aa9 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -36,6 +36,7 @@
 import static android.os.UserManager.USER_TYPE_PROFILE_COMMUNAL;
 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
 import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
+import static android.os.UserManager.USER_TYPE_PROFILE_SUPERVISING;
 import static android.os.UserManager.USER_TYPE_PROFILE_TEST;
 import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
 
@@ -111,6 +112,7 @@
         builders.put(USER_TYPE_PROFILE_CLONE, getDefaultTypeProfileClone());
         builders.put(USER_TYPE_PROFILE_COMMUNAL, getDefaultTypeProfileCommunal());
         builders.put(USER_TYPE_PROFILE_PRIVATE, getDefaultTypeProfilePrivate());
+        builders.put(USER_TYPE_PROFILE_SUPERVISING, getDefaultTypeProfileSupervising());
         if (Build.IS_DEBUGGABLE) {
             builders.put(USER_TYPE_PROFILE_TEST, getDefaultTypeProfileTest());
         }
@@ -343,6 +345,29 @@
     }
 
     /**
+     * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_SUPERVISING}
+     * configuration.
+     */
+    private static UserTypeDetails.Builder getDefaultTypeProfileSupervising() {
+        return new UserTypeDetails.Builder()
+                .setName(USER_TYPE_PROFILE_SUPERVISING)
+                .setBaseType(FLAG_PROFILE)
+                .setMaxAllowed(1)
+                .setProfileParentRequired(false)
+                .setEnabled(android.multiuser.Flags.allowSupervisingProfile() ? 1 : 0)
+                .setLabels(R.string.profile_label_supervising)
+                .setDefaultRestrictions(getDefaultSupervisingProfileRestrictions())
+                .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings())
+                .setDefaultUserProperties(new UserProperties.Builder()
+                        .setStartWithParent(false)
+                        .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_NO)
+                        .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_NO)
+                        .setShowInQuietMode(UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
+                        .setCredentialShareableWithParent(false)
+                        .setAlwaysVisible(true));
+    }
+
+    /**
      * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SECONDARY}
      * configuration.
      */
@@ -449,6 +474,12 @@
         return restrictions;
     }
 
+    private static Bundle getDefaultSupervisingProfileRestrictions() {
+        final Bundle restrictions = getDefaultProfileRestrictions();
+        restrictions.putBoolean(UserManager.DISALLOW_INSTALL_APPS, true);
+        return restrictions;
+    }
+
     private static Bundle getDefaultManagedProfileSecureSettings() {
         // Only add String values to the bundle, settings are written as Strings eventually
         final Bundle settings = new Bundle();
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index c31c287..4ffdb11 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -235,15 +235,12 @@
                 this::synchronizeUidPermissionsAndAppOpsAsync);
 
         mAppOpsCallback = new IAppOpsCallback.Stub() {
-            public void opChanged(int op, int uid, @Nullable String packageName,
-                    String persistentDeviceId) {
+            public void opChanged(int op, int uid, String packageName, String persistentDeviceId) {
                 if (!Objects.equals(persistentDeviceId,
-                        VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) {
+                        VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) || uid < 0) {
                     return;
                 }
-                if (packageName != null) {
-                    synchronizeUidPermissionsAndAppOpsAsync(uid);
-                }
+                synchronizeUidPermissionsAndAppOpsAsync(uid);
                 resetAppOpPermissionsIfNotRequestedForUidAsync(uid);
             }
         };
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 46dc758..38d4587 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4240,66 +4240,14 @@
         if (!useKeyGestureEventHandler()) {
             return;
         }
-        mInputManager.registerKeyGestureEventHandler(new InputManager.KeyGestureEventHandler() {
-            @Override
-            public boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
-                    @Nullable IBinder focusedToken) {
-                boolean handled = PhoneWindowManager.this.handleKeyGestureEvent(event,
-                        focusedToken);
-                if (handled && !event.isCancelled() && Arrays.stream(event.getKeycodes()).anyMatch(
-                        (keycode) -> keycode == KeyEvent.KEYCODE_POWER)) {
-                    mPowerKeyHandled = true;
-                }
-                return handled;
+        mInputManager.registerKeyGestureEventHandler((event, focusedToken) -> {
+            boolean handled = PhoneWindowManager.this.handleKeyGestureEvent(event,
+                    focusedToken);
+            if (handled && !event.isCancelled() && Arrays.stream(event.getKeycodes()).anyMatch(
+                    (keycode) -> keycode == KeyEvent.KEYCODE_POWER)) {
+                mPowerKeyHandled = true;
             }
-
-            @Override
-            public boolean isKeyGestureSupported(int gestureType) {
-                switch (gestureType) {
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_HOME:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_BACK:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS:
-                        return true;
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
-                        return mAccessibilityShortcutController.isAccessibilityShortcutAvailable(
-                                isKeyguardLocked());
-                    case KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD:
-                        return mAccessibilityShortcutController.isAccessibilityShortcutAvailable(
-                                false);
-                    default:
-                        return false;
-                }
-            }
+            return handled;
         });
     }
 
@@ -4457,13 +4405,6 @@
                     cancelPendingScreenshotChordAction();
                 }
                 return true;
-            case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
-                if (start) {
-                    interceptAccessibilityShortcutChord();
-                } else {
-                    cancelPendingAccessibilityShortcutAction();
-                }
-                return true;
             case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
                 if (start) {
                     interceptRingerToggleChord();
@@ -4481,14 +4422,6 @@
                     cancelGlobalActionsAction();
                 }
                 return true;
-                // TODO (b/358569822): Consolidate TV and non-TV gestures into same KeyGestureEvent
-            case KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD:
-                if (start) {
-                    interceptAccessibilityGestureTv();
-                } else {
-                    cancelAccessibilityGestureTv();
-                }
-                return true;
             case KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT:
                 if (start) {
                     interceptBugreportGestureTv();
@@ -6033,7 +5966,7 @@
             long whenNanos, int policyFlags) {
         if ((policyFlags & FLAG_WAKE) != 0) {
             if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source,
-                    action == MotionEvent.ACTION_DOWN)) {
+                    action == MotionEvent.ACTION_DOWN, mDeviceGoingToSleep)) {
                 // Woke up. Pass motion events to user.
                 return ACTION_PASS_TO_USER;
             }
@@ -6048,7 +5981,7 @@
         // wake up in this case.
         if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
             if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source,
-                    action == MotionEvent.ACTION_DOWN)) {
+                    action == MotionEvent.ACTION_DOWN, mDeviceGoingToSleep)) {
                 // Woke up. Pass motion events to user.
                 return ACTION_PASS_TO_USER;
             }
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
index 04dbd1f..0b5ec6e4 100644
--- a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
@@ -149,16 +149,22 @@
      * @param displayId the id of the display to wake.
      * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
      * @param isDown {@code true} if the event's action is {@link MotionEvent#ACTION_DOWN}.
+     * @param deviceGoingToSleep {@code true} if the device is in the middle of going to sleep. This
+     *      will be {@code false} if the device is currently fully awake or is fully asleep
+     *      (i.e. not trying to go to sleep)
      * @return {@code true} if the policy allows the requested wake up and the request has been
      *      executed; {@code false} otherwise.
      */
-    boolean wakeUpFromMotion(int displayId, long eventTime, int source, boolean isDown) {
+    boolean wakeUpFromMotion(
+            int displayId, long eventTime, int source, boolean isDown,
+            boolean deviceGoingToSleep) {
         if (!canWakeUp(mAllowTheaterModeWakeFromMotion)) {
             if (DEBUG) Slog.d(TAG, "Unable to wake up from motion.");
             return false;
         }
         if (mInputWakeUpDelegate != null
-                && mInputWakeUpDelegate.wakeUpFromMotion(eventTime, source, isDown)) {
+                && mInputWakeUpDelegate.wakeUpFromMotion(
+                        eventTime, source, isDown, deviceGoingToSleep)) {
             return true;
         }
         if (perDisplayWakeByTouch()) {
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java
index 66a0035..962b5a7 100644
--- a/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java
@@ -52,10 +52,14 @@
          * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
          * @param source the {@link android.view.InputDevice} source that caused the event.
          * @param isDown {@code true} if the event's action is {@link MotionEvent#ACTION_DOWN}.
+         * @param deviceGoingToSleep {@code true} if the device is in the middle of going to sleep.
+         *      This will be {@code false} if the device is currently fully awake or is fully
+         *      asleep (i.e. not trying to go to sleep)
          * @return {@code true} if the delegate handled the wake up. {@code false} if the delegate
          *      decided not to handle the wake up. The policy will execute the wake up in this case.
          */
-        boolean wakeUpFromMotion(long eventTime, int source, boolean isDown);
+        boolean wakeUpFromMotion(
+                long eventTime, int source, boolean isDown, boolean deviceGoingToSleep);
     }
 
     /**
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 102dc07..417b5c7 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -16,6 +16,7 @@
 
 package com.android.server.power;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
@@ -36,8 +37,8 @@
 import android.os.BatteryStatsInternal;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IWakeLockCallback;
 import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
@@ -67,6 +68,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.input.InputManagerInternal;
@@ -147,7 +149,8 @@
     @Nullable private final StatusBarManagerInternal mStatusBarManagerInternal;
     private final TrustManager mTrustManager;
     private final Vibrator mVibrator;
-    private final WakeLockLog mWakeLockLog;
+    @NonNull private final WakeLockLog mPartialWakeLockLog;
+    @NonNull private final WakeLockLog mFullWakeLockLog;
     private final DisplayManagerInternal mDisplayManagerInternal;
 
     private final NotifierHandler mHandler;
@@ -250,7 +253,9 @@
         mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
 
-        mWakeLockLog = mInjector.getWakeLockLog(context);
+        mFullWakeLockLog = mInjector.getWakeLockLog(context);
+        mPartialWakeLockLog = mInjector.getWakeLockLog(context);
+
         // Initialize interactive state for battery stats.
         try {
             mBatteryStats.noteInteractive(true);
@@ -324,7 +329,8 @@
                     // Ignore
                 }
             }
-            mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, /*eventTime=*/ -1);
+            getWakeLockLog(flags).onWakeLockAcquired(tag,
+                    getUidForWakeLockLog(ownerUid, workSource), flags, /*eventTime=*/ -1);
         }
         mWakefulnessSessionObserver.onWakeLockAcquired(flags);
     }
@@ -473,7 +479,8 @@
                     // Ignore
                 }
             }
-            mWakeLockLog.onWakeLockReleased(tag, ownerUid, /*eventTime=*/ -1);
+            getWakeLockLog(flags).onWakeLockReleased(tag,
+                    getUidForWakeLockLog(ownerUid, workSource), /*eventTime=*/ -1);
         }
         mWakefulnessSessionObserver.onWakeLockReleased(flags, releaseReason);
     }
@@ -960,11 +967,18 @@
      * @param pw The stream to print to.
      */
     public void dump(PrintWriter pw) {
-        if (mWakeLockLog != null) {
-            mWakeLockLog.dump(pw);
-        }
+        pw.println("Notifier:");
 
-        mWakefulnessSessionObserver.dump(pw);
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println("Partial Wakelock Log:");
+        mPartialWakeLockLog.dump(ipw);
+
+        ipw.println("");
+        ipw.println("Full Wakelock Log:");
+        mFullWakeLockLog.dump(ipw);
+
+        ipw.println("");
+        mWakefulnessSessionObserver.dump(ipw);
     }
 
     private void updatePendingBroadcastLocked() {
@@ -1232,7 +1246,9 @@
                 // Do Nothing
             }
         }
-        mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);
+
+        getWakeLockLog(flags).onWakeLockAcquired(tag, getUidForWakeLockLog(ownerUid, workSource),
+                flags, currentTime);
     }
 
     @SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1253,7 +1269,8 @@
                 // Ignore
             }
         }
-        mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
+        getWakeLockLog(flags).onWakeLockReleased(tag, getUidForWakeLockLog(ownerUid, workSource),
+                currentTime);
     }
 
     @SuppressLint("AndroidFrameworkRequiresPermission")
@@ -1419,6 +1436,15 @@
         }
     }
 
+    private @NonNull WakeLockLog getWakeLockLog(int flags) {
+        return PowerManagerService.isScreenLock(flags) ? mFullWakeLockLog : mPartialWakeLockLog;
+    }
+
+    private int getUidForWakeLockLog(int ownerUid, WorkSource workSource) {
+        int attributionUid = workSource != null ? workSource.getAttributionUid() : -1;
+        return attributionUid != -1 ? attributionUid : ownerUid;
+    }
+
     private final class NotifierHandler extends Handler {
 
         public NotifierHandler(Looper looper) {
@@ -1501,7 +1527,7 @@
         /**
          * Gets the WakeLockLog object
          */
-        WakeLockLog getWakeLockLog(Context context);
+        @NonNull WakeLockLog getWakeLockLog(Context context);
 
         /**
          * Gets the AppOpsManager system service
@@ -1522,7 +1548,7 @@
         }
 
         @Override
-        public WakeLockLog getWakeLockLog(Context context) {
+        public @NonNull WakeLockLog getWakeLockLog(Context context) {
             return new WakeLockLog(context);
         }
 
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index dd454cd..3eac4b5 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1723,9 +1723,16 @@
         }
     }
 
-    @SuppressWarnings("deprecation")
     private static boolean isScreenLock(final WakeLock wakeLock) {
-        switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
+        return isScreenLock(wakeLock.mFlags);
+    }
+
+    /**
+     * Returns if a wakelock flag corresponds to a screen wake lock.
+     */
+    @SuppressWarnings("deprecation")
+    public static boolean isScreenLock(int flags) {
+        switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
             case PowerManager.FULL_WAKE_LOCK:
             case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
             case PowerManager.SCREEN_DIM_WAKE_LOCK:
diff --git a/services/core/java/com/android/server/power/ScreenUndimDetector.java b/services/core/java/com/android/server/power/ScreenUndimDetector.java
index c4929c2..b376417 100644
--- a/services/core/java/com/android/server/power/ScreenUndimDetector.java
+++ b/services/core/java/com/android/server/power/ScreenUndimDetector.java
@@ -30,11 +30,11 @@
 import android.util.Slog;
 import android.view.Display;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
 
 import java.util.Set;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Detects when user manually undims the screen (x times) and acquires a wakelock to keep the screen
@@ -48,7 +48,6 @@
 
     /** DeviceConfig flag: is keep screen on feature enabled. */
     static final String KEY_KEEP_SCREEN_ON_ENABLED = "keep_screen_on_enabled";
-    private static final boolean DEFAULT_KEEP_SCREEN_ON_ENABLED = true;
     private static final int OUTCOME_POWER_BUTTON =
             FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME__POWER_BUTTON;
     private static final int OUTCOME_TIMEOUT =
@@ -58,15 +57,11 @@
     /** DeviceConfig flag: how long should we keep the screen on. */
     @VisibleForTesting
     static final String KEY_KEEP_SCREEN_ON_FOR_MILLIS = "keep_screen_on_for_millis";
-    @VisibleForTesting
-    static final long DEFAULT_KEEP_SCREEN_ON_FOR_MILLIS = TimeUnit.MINUTES.toMillis(10);
     private long mKeepScreenOnForMillis;
 
     /** DeviceConfig flag: how many user undims required to trigger keeping the screen on. */
     @VisibleForTesting
     static final String KEY_UNDIMS_REQUIRED = "undims_required";
-    @VisibleForTesting
-    static final int DEFAULT_UNDIMS_REQUIRED = 2;
     private int mUndimsRequired;
 
     /**
@@ -76,8 +71,6 @@
     @VisibleForTesting
     static final String KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS =
             "max_duration_between_undims_millis";
-    @VisibleForTesting
-    static final long DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS = TimeUnit.MINUTES.toMillis(5);
     private long mMaxDurationBetweenUndimsMillis;
 
     @VisibleForTesting
@@ -92,6 +85,7 @@
     private long mUndimOccurredTime = -1;
     private long mInteractionAfterUndimTime = -1;
     private InternalClock mClock;
+    private Context mContext;
 
     public ScreenUndimDetector() {
         mClock = new InternalClock();
@@ -109,12 +103,13 @@
 
     /** Should be called in parent's systemReady() */
     public void systemReady(Context context) {
+        mContext = context;
         readValuesFromDeviceConfig();
         DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
-                context.getMainExecutor(),
+                mContext.getMainExecutor(),
                 (properties) -> onDeviceConfigChange(properties.getKeyset()));
 
-        final PowerManager powerManager = context.getSystemService(PowerManager.class);
+        final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
         mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
                         | PowerManager.ON_AFTER_RELEASE,
                 UNDIM_DETECTOR_WAKE_LOCK);
@@ -203,36 +198,44 @@
         }
     }
 
-    private boolean readKeepScreenOnNotificationEnabled() {
+    private boolean readKeepScreenOnEnabled() {
+        boolean defaultKeepScreenOnEnabled = mContext.getResources().getBoolean(
+                R.bool.config_defaultPreventScreenTimeoutEnabled);
         return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE,
                 KEY_KEEP_SCREEN_ON_ENABLED,
-                DEFAULT_KEEP_SCREEN_ON_ENABLED);
+                defaultKeepScreenOnEnabled);
     }
 
     private long readKeepScreenOnForMillis() {
+        long defaultKeepScreenOnDuration = mContext.getResources().getInteger(
+                R.integer.config_defaultPreventScreenTimeoutForMillis);
         return DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
                 KEY_KEEP_SCREEN_ON_FOR_MILLIS,
-                DEFAULT_KEEP_SCREEN_ON_FOR_MILLIS);
+                defaultKeepScreenOnDuration);
     }
 
     private int readUndimsRequired() {
+        int defaultUndimsRequired = mContext.getResources().getInteger(
+                R.integer.config_defaultUndimsRequired);
         int undimsRequired = DeviceConfig.getInt(NAMESPACE_ATTENTION_MANAGER_SERVICE,
                 KEY_UNDIMS_REQUIRED,
-                DEFAULT_UNDIMS_REQUIRED);
+                defaultUndimsRequired);
 
         if (undimsRequired < 1 || undimsRequired > 5) {
             Slog.e(TAG, "Provided undimsRequired=" + undimsRequired
-                    + " is not allowed [1, 5]; using the default=" + DEFAULT_UNDIMS_REQUIRED);
-            return DEFAULT_UNDIMS_REQUIRED;
+                    + " is not allowed [1, 5]; using the default=" + defaultUndimsRequired);
+            return defaultUndimsRequired;
         }
 
         return undimsRequired;
     }
 
     private long readMaxDurationBetweenUndimsMillis() {
+        long defaultMaxDurationBetweenUndimsMillis = mContext.getResources().getInteger(
+                R.integer.config_defaultMaxDurationBetweenUndimsMillis);
         return DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
                 KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS,
-                DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS);
+                defaultMaxDurationBetweenUndimsMillis);
     }
 
     private void onDeviceConfigChange(@NonNull Set<String> keys) {
@@ -253,15 +256,16 @@
 
     @VisibleForTesting
     void readValuesFromDeviceConfig() {
-        mKeepScreenOnEnabled = readKeepScreenOnNotificationEnabled();
+        mKeepScreenOnEnabled = readKeepScreenOnEnabled();
         mKeepScreenOnForMillis = readKeepScreenOnForMillis();
         mUndimsRequired = readUndimsRequired();
         mMaxDurationBetweenUndimsMillis = readMaxDurationBetweenUndimsMillis();
 
         Slog.i(TAG, "readValuesFromDeviceConfig():"
                 + "\nmKeepScreenOnForMillis=" + mKeepScreenOnForMillis
-                + "\nmKeepScreenOnNotificationEnabled=" + mKeepScreenOnEnabled
-                + "\nmUndimsRequired=" + mUndimsRequired);
+                + "\nmKeepScreenOnEnabled=" + mKeepScreenOnEnabled
+                + "\nmUndimsRequired=" + mUndimsRequired
+                + "\nmMaxDurationBetweenUndimsMillis=" + mMaxDurationBetweenUndimsMillis);
 
     }
 
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index eda222e..7f152d6 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -81,11 +81,12 @@
     private static final int TYPE_ACQUIRE = 0x1;
     private static final int TYPE_RELEASE = 0x2;
     private static final int MAX_LOG_ENTRY_BYTE_SIZE = 9;
-    private static final int LOG_SIZE = 1024 * 10;
+    private static final int LOG_SIZE = 1024 * 3;
     private static final int LOG_SIZE_MIN = MAX_LOG_ENTRY_BYTE_SIZE + 1;
 
-    private static final int TAG_DATABASE_SIZE = 128;
+    private static final int TAG_DATABASE_SIZE = 64;
     private static final int TAG_DATABASE_SIZE_MAX = 128;
+    private static final int TAG_DATABASE_STARTING_SIZE = 16;
 
     private static final int LEVEL_SCREEN_TIMEOUT_OVERRIDE_WAKE_LOCK = 0;
     private static final int LEVEL_PARTIAL_WAKE_LOCK = 1;
@@ -182,7 +183,7 @@
      * @param pw The {@code PrintWriter} to write to.
      */
     public void dump(PrintWriter pw) {
-        dump(pw, false);
+        dump(pw, /* includeTagDb= */ true);
     }
 
     @VisibleForTesting
@@ -1161,15 +1162,16 @@
      */
     static class TagDatabase {
         private final int mInvalidIndex;
-        private final TagData[] mArray;
+        private final int mMaxArraySize;
+        private TagData[] mArray;
         private Callback mCallback;
 
         TagDatabase(Injector injector) {
-            int size = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX);
-
-            // Largest possible index used as "INVALID", hence the (size - 1) sizing.
-            mArray = new TagData[size - 1];
-            mInvalidIndex = size - 1;
+            // Largest possible index used as "INVALID", hence the (size - 1) sizing
+            mMaxArraySize = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX - 1);
+            int startingSize = Math.min(mMaxArraySize, injector.getTagDatabaseStartingSize());
+            mArray = new TagData[startingSize];
+            mInvalidIndex = mMaxArraySize;
         }
 
         @Override
@@ -1195,8 +1197,10 @@
             sb.append(", entries: ").append(entries);
             sb.append(", Bytes used: ").append(byteEstimate);
             if (DEBUG) {
-                sb.append(", Avg tag size: ").append(tagSize / tags);
-                sb.append("\n    ").append(Arrays.toString(mArray));
+                sb.append(", Avg tag size: ").append(tags == 0 ? 0 : (tagSize / tags));
+                for (int i = 0; i < mArray.length; i++) {
+                    sb.append("\n  [").append(i).append("] ").append(mArray[i]);
+                }
             }
             return sb.toString();
         }
@@ -1284,6 +1288,18 @@
                 return null;
             }
 
+            // We don't have a spot available, see if we can still increase the array size
+            if (firstAvailable == -1) {
+                if (mArray.length < mMaxArraySize) {
+                    int oldSize = mArray.length;
+                    int newSize = Math.min(oldSize * 2, mMaxArraySize);
+                    TagData[] newArray = new TagData[newSize];
+                    System.arraycopy(mArray, 0, newArray, 0, oldSize);
+                    mArray = newArray;
+                    firstAvailable = oldSize;
+                }
+            }
+
             // If we need to remove an index, report to listeners that we are removing an index.
             boolean useOldest = firstAvailable == -1;
             if (useOldest && mCallback != null) {
@@ -1402,6 +1418,10 @@
             return TAG_DATABASE_SIZE;
         }
 
+        public int getTagDatabaseStartingSize() {
+            return TAG_DATABASE_STARTING_SIZE;
+        }
+
         public int getLogSize() {
             return LOG_SIZE;
         }
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index ebc50fd..52d4555 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -67,6 +67,10 @@
             new FlagState(Flags.FLAG_WAKELOCK_ATTRIBUTION_VIA_WORKCHAIN,
                     Flags::wakelockAttributionViaWorkchain);
 
+    private final FlagState mDisableFrozenProcessWakelocks =
+            new FlagState(Flags.FLAG_DISABLE_FROZEN_PROCESS_WAKELOCKS,
+                    Flags::disableFrozenProcessWakelocks);
+
     /** Returns whether early-screen-timeout-detector is enabled on not. */
     public boolean isEarlyScreenTimeoutDetectorEnabled() {
         return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
@@ -121,6 +125,13 @@
     }
 
     /**
+     * @return Whether the feature to disable the frozen process wakelocks is enabled
+     */
+    public boolean isDisableFrozenProcessWakelocksEnabled() {
+        return mDisableFrozenProcessWakelocks.isEnabled();
+    }
+
+    /**
      * dumps all flagstates
      * @param pw printWriter
      */
@@ -132,6 +143,7 @@
         pw.println(" " + mFrameworkWakelockInfo);
         pw.println(" " + mMoveWscLoggingToNotifier);
         pw.println(" " + mWakelockAttributionViaWorkchain);
+        pw.println(" " + mDisableFrozenProcessWakelocks);
     }
 
     private static class FlagState {
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 fefe195..ad8ec03 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
@@ -70,3 +70,10 @@
     description: "Feature flag to move logging of WakelockStateChanged atoms from BatteryStatsImpl to Notifier."
     bug: "352602149"
 }
+
+flag {
+    name: "disable_frozen_process_wakelocks"
+    namespace: "power"
+    description: "Feature flag to disable/enable wakelocks of a process when it is frozen/unfrozen"
+    bug: "291115867"
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java b/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
index 5563f98..7cd9bdb 100644
--- a/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
+++ b/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
@@ -374,6 +374,10 @@
     @SuppressWarnings("unchecked")
     @Override
     public List<BatteryHistoryFragment> getFragments() {
+        if (!mLock.isHeldByCurrentThread()) {
+            throw new IllegalStateException("Reading battery history without a lock");
+        }
+
         ensureInitialized();
         return (List<BatteryHistoryFragment>)
                 (List<? extends BatteryHistoryFragment>) mHistoryFiles;
@@ -443,44 +447,6 @@
     }
 
     @Override
-    public BatteryHistoryFragment getNextFragment(BatteryHistoryFragment current, long startTimeMs,
-            long endTimeMs) {
-        ensureInitialized();
-
-        if (!mLock.isHeldByCurrentThread()) {
-            throw new IllegalStateException("Iterating battery history without a lock");
-        }
-
-        int nextFileIndex = 0;
-        int firstFileIndex = 0;
-        // skip the last file because its data is in history buffer.
-        int lastFileIndex = mHistoryFiles.size() - 2;
-        for (int i = lastFileIndex; i >= 0; i--) {
-            BatteryHistoryFragment fragment = mHistoryFiles.get(i);
-            if (current != null && fragment.monotonicTimeMs == current.monotonicTimeMs) {
-                nextFileIndex = i + 1;
-            }
-            if (fragment.monotonicTimeMs > endTimeMs) {
-                lastFileIndex = i - 1;
-            }
-            if (fragment.monotonicTimeMs <= startTimeMs) {
-                firstFileIndex = i;
-                break;
-            }
-        }
-
-        if (nextFileIndex < firstFileIndex) {
-            nextFileIndex = firstFileIndex;
-        }
-
-        if (nextFileIndex <= lastFileIndex) {
-            return mHistoryFiles.get(nextFileIndex);
-        }
-
-        return null;
-    }
-
-    @Override
     public boolean hasCompletedFragments() {
         ensureInitialized();
 
diff --git a/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java b/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java
index 7991575..800fc7c 100644
--- a/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java
+++ b/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.os.Binder;
 import android.os.Environment;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -119,7 +120,7 @@
         } catch (IOException | JSONException ex) {
             Slog.d(TAG, "Fallback to check stored revocation status", ex);
             if (ex instanceof IOException && mShouldScheduleJob) {
-                scheduleJobToFetchRemoteRevocationJob();
+                Binder.withCleanCallingIdentity(this::scheduleJobToFetchRemoteRevocationJob);
             }
             try {
                 revocationList = getStoredRevocationList();
@@ -210,7 +211,7 @@
             return;
         }
         Slog.d(TAG, "Scheduling job to fetch remote CRL.");
-        jobScheduler.schedule(
+        jobScheduler.forNamespace(TAG).schedule(
                 new JobInfo.Builder(
                                 JOB_ID,
                                 new ComponentName(
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index 93fd276..f5daa80 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -23,7 +23,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -32,14 +34,21 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.security.advancedprotection.AdvancedProtectionManager;
+import android.security.advancedprotection.AdvancedProtectionManager.FeatureId;
+import android.security.advancedprotection.AdvancedProtectionManager.SupportDialogType;
 import android.security.advancedprotection.IAdvancedProtectionCallback;
 import android.security.advancedprotection.IAdvancedProtectionService;
+import android.security.advancedprotection.AdvancedProtectionProtoEnums;
 import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -50,8 +59,11 @@
 import com.android.server.security.advancedprotection.features.DisallowInstallUnknownSourcesAdvancedProtectionHook;
 import com.android.server.security.advancedprotection.features.MemoryTaggingExtensionHook;
 import com.android.server.security.advancedprotection.features.UsbDataAdvancedProtectionHook;
+import com.android.server.security.advancedprotection.features.DisallowWepAdvancedProtectionProvider;
 
+import java.io.File;
 import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -61,6 +73,15 @@
     private static final int MODE_CHANGED = 0;
     private static final int CALLBACK_ADDED = 1;
 
+    // Shared preferences keys
+    private static final String PREFERENCE = "advanced_protection_preference";
+    private static final String ENABLED_CHANGE_TIME = "enabled_change_time";
+    private static final String LAST_DIALOG_FEATURE_ID = "last_dialog_feature_id";
+    private static final String LAST_DIALOG_TYPE = "last_dialog_type";
+    private static final String LAST_DIALOG_HOURS_SINCE_ENABLED = "last_dialog_hours_since_enabled";
+    private static final String LAST_DIALOG_LEARN_MORE_CLICKED = "last_dialog_learn_more_clicked";
+    private static final long MILLIS_PER_HOUR = 60 * 60 * 1000;
+
     private final Context mContext;
     private final Handler mHandler;
     private final AdvancedProtectionStore mStore;
@@ -72,6 +93,10 @@
     // For tracking only - not called on state change
     private final ArrayList<AdvancedProtectionProvider> mProviders = new ArrayList<>();
 
+    // Used to store logging data
+    private SharedPreferences mSharedPreferences;
+    private boolean mEmitLogs = true;
+
     private AdvancedProtectionService(@NonNull Context context) {
         super(PermissionEnforcer.fromContext(context));
         mContext = context;
@@ -107,7 +132,9 @@
           } catch (Exception e) {
             Slog.e(TAG, "Failed to initialize UsbDataAdvancedProtection", e);
           }
-      }
+        }
+
+        mProviders.add(new DisallowWepAdvancedProtectionProvider());
     }
 
     // Only for tests
@@ -126,6 +153,8 @@
         if (provider != null) {
             mProviders.add(provider);
         }
+
+        mEmitLogs = false;
     }
 
     @Override
@@ -178,7 +207,7 @@
                 if (enabled != isAdvancedProtectionEnabledInternal()) {
                     mStore.store(enabled);
                     sendModeChanged(enabled);
-                    Slog.i(TAG, "Advanced protection is " + (enabled ? "enabled" : "disabled"));
+                    logAdvancedProtectionEnabled(enabled);
                 }
             }
         } finally {
@@ -188,11 +217,96 @@
 
     @Override
     @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
+    public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
+            boolean learnMoreClicked) {
+        logDialogShown_enforcePermission();
+
+        if (!mEmitLogs) {
+            return;
+        }
+
+        int hoursSinceEnabled = hoursSinceLastChange();
+        FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_SUPPORT_DIALOG_DISPLAYED,
+                /*feature_id*/ featureIdToLogEnum(featureId),
+                /*dialogue_type*/ dialogueTypeToLogEnum(type),
+                /*learn_more_clicked*/ learnMoreClicked,
+                /*hours_since_last_change*/ hoursSinceEnabled);
+
+        getSharedPreferences().edit()
+                .putInt(LAST_DIALOG_FEATURE_ID, featureId)
+                .putInt(LAST_DIALOG_TYPE, type)
+                .putBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, learnMoreClicked)
+                .putInt(LAST_DIALOG_HOURS_SINCE_ENABLED, hoursSinceEnabled)
+                .apply();
+    }
+
+    private int featureIdToLogEnum(@FeatureId int featureId) {
+        switch (featureId) {
+            case AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_CELLULAR_2G;
+            case AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
+            case AdvancedProtectionManager.FEATURE_ID_DISALLOW_USB:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_USB;
+            case AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_DISALLOW_WEP;
+            case AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_ENABLE_MTE;
+            default:
+                return AdvancedProtectionProtoEnums.FEATURE_ID_UNKNOWN;
+        }
+    }
+
+    private int dialogueTypeToLogEnum(@SupportDialogType int type) {
+        switch (type) {
+            case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN:
+                return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN;
+            case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION:
+                return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_BLOCKED_INTERACTION;
+            case AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING:
+                return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_DISABLED_SETTING;
+            default:
+                return AdvancedProtectionProtoEnums.DIALOGUE_TYPE_UNKNOWN;
+        }
+    }
+
+    private void logAdvancedProtectionEnabled(boolean enabled) {
+        if (!mEmitLogs) {
+            return;
+        }
+
+        Slog.i(TAG, "Advanced protection has been " + (enabled ? "enabled" : "disabled"));
+        SharedPreferences prefs = getSharedPreferences();
+        FrameworkStatsLog.write(FrameworkStatsLog.ADVANCED_PROTECTION_STATE_CHANGED,
+                /*enabled*/ enabled,
+                /*hours_since_enabled*/ hoursSinceLastChange(),
+                /*last_dialog_feature_id*/ featureIdToLogEnum(
+                    prefs.getInt(LAST_DIALOG_FEATURE_ID, -1)),
+                /*_type*/ dialogueTypeToLogEnum(prefs.getInt(LAST_DIALOG_TYPE, -1)),
+                /*_learn_more_clicked*/ prefs.getBoolean(LAST_DIALOG_LEARN_MORE_CLICKED, false),
+                /*_hours_since_enabled*/ prefs.getInt(LAST_DIALOG_HOURS_SINCE_ENABLED, -1));
+        prefs.edit()
+                .putLong(ENABLED_CHANGE_TIME, System.currentTimeMillis())
+                .apply();
+    }
+
+    private int hoursSinceLastChange() {
+        int hoursSinceEnabled = -1;
+        long lastChangeTimeMillis = getSharedPreferences().getLong(ENABLED_CHANGE_TIME, -1);
+        if (lastChangeTimeMillis != -1) {
+            hoursSinceEnabled = (int)
+                    ((System.currentTimeMillis() - lastChangeTimeMillis) / MILLIS_PER_HOUR);
+        }
+        return hoursSinceEnabled;
+    }
+
+    @Override
+    @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
     public List<AdvancedProtectionFeature> getAdvancedProtectionFeatures() {
         getAdvancedProtectionFeatures_enforcePermission();
         List<AdvancedProtectionFeature> features = new ArrayList<>();
         for (int i = 0; i < mProviders.size(); i++) {
-            features.addAll(mProviders.get(i).getFeatures());
+            features.addAll(mProviders.get(i).getFeatures(mContext));
         }
 
         for (int i = 0; i < mHooks.size(); i++) {
@@ -213,6 +327,30 @@
                 .exec(this, in, out, err, args, callback, resultReceiver);
     }
 
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+        writer.println("AdvancedProtectionService");
+        writer.println("  isAdvancedProtectionEnabled: " + isAdvancedProtectionEnabledInternal());
+        writer.println("  mHooks.size(): " + mHooks.size());
+        writer.println("  mCallbacks.size(): " + mCallbacks.size());
+        writer.println("  mProviders.size(): " + mProviders.size());
+
+        writer.println("Hooks: ");
+        mHooks.stream().forEach(hook -> {
+            writer.println("    " + hook.getClass().getSimpleName() +
+                                   " available: " + hook.isAvailable());
+        });
+        writer.println("  Providers: ");
+        mProviders.stream().forEach(provider -> {
+            writer.println("    " + provider.getClass().getSimpleName());
+            provider.getFeatures(mContext).stream().forEach(feature -> {
+                writer.println("      " + feature.getClass().getSimpleName());
+            });
+        });
+        writer.println("  mSharedPreferences: " + getSharedPreferences().getAll());
+    }
+
     void sendModeChanged(boolean enabled) {
         Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused */ -1)
                 .sendToTarget();
@@ -224,6 +362,22 @@
                 .sendToTarget();
     }
 
+    private SharedPreferences getSharedPreferences() {
+        if (mSharedPreferences == null) {
+            initSharedPreferences();
+        }
+        return mSharedPreferences;
+    }
+
+    private synchronized void initSharedPreferences() {
+        if (mSharedPreferences == null) {
+            Context deviceContext = mContext.createDeviceProtectedStorageContext();
+            File sharedPrefs = new File(Environment.getDataSystemDirectory(), PREFERENCE);
+            mSharedPreferences = deviceContext.getSharedPreferences(sharedPrefs,
+                    Context.MODE_PRIVATE);
+        }
+    }
+
     public static final class Lifecycle extends SystemService {
         private final AdvancedProtectionService mService;
 
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
index ed451f1..6498cfc 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.server.security.advancedprotection.features;
 
+import android.annotation.NonNull;
+import android.content.Context;
 import android.security.advancedprotection.AdvancedProtectionFeature;
 
 import java.util.List;
@@ -23,5 +25,5 @@
 /** @hide */
 public abstract class AdvancedProtectionProvider {
     /** The list of features provided */
-    public abstract List<AdvancedProtectionFeature> getFeatures();
+    public abstract List<AdvancedProtectionFeature> getFeatures(@NonNull Context context);
 }
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 be26334..bf464be 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
@@ -22,16 +22,11 @@
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.UserManager;
 import android.security.advancedprotection.AdvancedProtectionFeature;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
 import android.util.Slog;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /** @hide */
 public final class DisallowCellular2GAdvancedProtectionHook extends AdvancedProtectionHook {
     private static final String TAG = "AdvancedProtectionDisallowCellular2G";
@@ -39,14 +34,12 @@
     private final AdvancedProtectionFeature mFeature =
             new AdvancedProtectionFeature(FEATURE_ID_DISALLOW_CELLULAR_2G);
     private final DevicePolicyManager mDevicePolicyManager;
-    private final TelephonyManager mTelephonyManager;
-    private final SubscriptionManager mSubscriptionManager;
+    private final PackageManager mPackageManager;
 
     public DisallowCellular2GAdvancedProtectionHook(@NonNull Context context, boolean enabled) {
         super(context, enabled);
         mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
-        mTelephonyManager = context.getSystemService(TelephonyManager.class);
-        mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
+        mPackageManager = context.getPackageManager();
 
         onAdvancedProtectionChanged(enabled);
     }
@@ -57,40 +50,9 @@
         return mFeature;
     }
 
-    private static boolean isEmbeddedSubscriptionVisible(SubscriptionInfo subInfo) {
-        if (subInfo.isEmbedded()
-                && (subInfo.getProfileClass() == SubscriptionManager.PROFILE_CLASS_PROVISIONING
-                        || subInfo.isOnlyNonTerrestrialNetwork())) {
-            return false;
-        }
-
-        return true;
-    }
-
-    private List<TelephonyManager> getActiveTelephonyManagers() {
-        List<TelephonyManager> telephonyManagers = new ArrayList<>();
-
-        for (SubscriptionInfo subInfo : mSubscriptionManager.getActiveSubscriptionInfoList()) {
-            if (isEmbeddedSubscriptionVisible(subInfo)) {
-                telephonyManagers.add(
-                        mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()));
-            }
-        }
-
-        return telephonyManagers;
-    }
-
     @Override
     public boolean isAvailable() {
-        for (TelephonyManager telephonyManager : getActiveTelephonyManagers()) {
-            if (telephonyManager.isDataCapable()
-                    && telephonyManager.isRadioInterfaceCapabilitySupported(
-                            mTelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK)) {
-                return true;
-            }
-        }
-
-        return false;
+        return mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowWepAdvancedProtectionProvider.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowWepAdvancedProtectionProvider.java
new file mode 100644
index 0000000..1505f68
--- /dev/null
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowWepAdvancedProtectionProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.advancedprotection.features;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.security.advancedprotection.AdvancedProtectionFeature;
+
+import java.util.List;
+
+public class DisallowWepAdvancedProtectionProvider extends AdvancedProtectionProvider {
+    public List<AdvancedProtectionFeature> getFeatures(@NonNull Context context) {
+        WifiManager wifiManager = context.getSystemService(WifiManager.class);
+        return wifiManager.getAvailableAdvancedProtectionFeatures();
+    }
+}
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/UsbDataAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/UsbDataAdvancedProtectionHook.java
index 9a9c56f..55a8f7e 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/UsbDataAdvancedProtectionHook.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/UsbDataAdvancedProtectionHook.java
@@ -20,6 +20,8 @@
 import static android.content.Intent.ACTION_USER_PRESENT;
 import static android.hardware.usb.UsbManager.ACTION_USB_PORT_CHANGED;
 import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_USB;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_FORCE;
 
 import android.app.KeyguardManager;
 import android.app.Notification;
@@ -33,15 +35,18 @@
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbManager;
+import android.hardware.usb.IUsbManagerInternal;
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Slog;
 
+import com.android.server.LocalServices;
 import java.lang.Runnable;
 
 import java.util.function.Consumer;
@@ -52,6 +57,7 @@
 import com.android.internal.R;
 
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * AAPM Feature for managing and protecting USB data signal from attacks.
@@ -65,11 +71,13 @@
     private static final String CHANNEL_NAME = "BackgroundInstallUiNotificationChannel";
     private static final int APM_USB_FEATURE_CHANNEL_ID = 1;
     private static final int DELAY_DISABLE_MS = 1000;
+    private static final int OS_USB_DISABLE_REASON_LOCKDOWN_MODE = 1;
 
     private final Context mContext;
     private final Handler mDelayedDisableHandler = new Handler(Looper.getMainLooper());
 
     private UsbManager mUsbManager;
+    private IUsbManagerInternal mUsbManagerInternal;
     private BroadcastReceiver mUsbProtectionBroadcastReceiver;
     private KeyguardManager mKeyguardManager;
     private NotificationManager mNotificationManager;
@@ -85,6 +93,8 @@
         super(context, enabled);
         mContext = context;
         mUsbManager = mContext.getSystemService(UsbManager.class);
+        mUsbManagerInternal = Objects.requireNonNull(
+            LocalServices.getService(IUsbManagerInternal.class));
         onAdvancedProtectionChanged(enabled);
     }
 
@@ -117,9 +127,7 @@
             if (mBroadcastReceiverIsRegistered) {
                 unregisterReceiver();
             }
-            if (!mUsbManager.enableUsbDataSignal(true)) {
-                Slog.e(TAG, "USB Data protection toggle failed");
-            }
+            setUsbDataSignalIfPossible(true);
         }
     }
 
@@ -134,23 +142,41 @@
                             if (ACTION_USER_PRESENT.equals(intent.getAction())
                                     && !mKeyguardManager.isKeyguardLocked()) {
                                 mDelayedDisableHandler.removeCallbacksAndMessages(null);
-                                setUsbDataSignalIfNoConnectedDevices(true);
+                                setUsbDataSignalIfPossible(true);
+
                             } else if (ACTION_SCREEN_OFF.equals(intent.getAction())
                                     && mKeyguardManager.isKeyguardLocked()) {
-                                setUsbDataSignalIfNoConnectedDevices(false);
+                                setUsbDataSignalIfPossible(false);
+
                             } else if (ACTION_USB_PORT_CHANGED.equals(intent.getAction())) {
                                 if (Build.IS_DEBUGGABLE) {
                                     dumpUsbDevices();
                                 }
-                                setDelayedDisableTaskIfDisconnectedAndLocked(intent);
+                                if(mKeyguardManager.isKeyguardLocked()) {
+                                    updateDelayedDisableTask(intent);
+                                }
                                 sendNotificationIfDeviceLocked(intent);
+
                             }
                         } catch (Exception e) {
                             Slog.e(TAG, "USB Data protection failed with: " + e.getMessage());
                         }
                     }
 
-                    private boolean getUsbPortStatusIsConnectedAndDataEnabled(Intent intent) {
+                    private void updateDelayedDisableTask(Intent intent) {
+                        // For recovered intermittent/unreliable USB connections
+                        if(usbPortIsConnectedAndDataEnabled(intent)) {
+                            mDelayedDisableHandler.removeCallbacksAndMessages(null);
+                        } else if(!mDelayedDisableHandler.hasMessagesOrCallbacks()) {
+                            mDelayedDisableHandler.postDelayed(() -> {
+                                if (mKeyguardManager.isKeyguardLocked()) {
+                                    setUsbDataSignalIfPossible(false);
+                                }
+                            }, DELAY_DISABLE_MS);
+                        }
+                    }
+
+                    private boolean usbPortIsConnectedAndDataEnabled(Intent intent) {
                         UsbPortStatus portStatus =
                                 intent.getParcelableExtra(
                                         UsbManager.EXTRA_PORT_STATUS, UsbPortStatus.class);
@@ -160,40 +186,7 @@
                                         != UsbPortStatus.DATA_STATUS_DISABLED_FORCE;
                     }
 
-                    private void setDelayedDisableTaskIfDisconnectedAndLocked(Intent intent) {
-                        if(mKeyguardManager.isKeyguardLocked()) {
-                            if(getUsbPortStatusIsConnectedAndDataEnabled(intent)) {
-                                mDelayedDisableHandler.removeCallbacksAndMessages(null);
-                            } else if(!mDelayedDisableHandler.hasMessagesOrCallbacks()) {
-                                mDelayedDisableHandler.postDelayed(() -> {
-                                    disableChangedUsbPortIfDisconnected(intent);
-                                }, DELAY_DISABLE_MS);
-                            }
-                        }
-                    }
-
-                    private void disableChangedUsbPortIfDisconnected(Intent intent) {
-                        UsbPortStatus portStatus =
-                                intent.getParcelableExtra(
-                                        UsbManager.EXTRA_PORT_STATUS, UsbPortStatus.class);
-                        if (Build.IS_DEBUGGABLE) {
-                            Slog.i(
-                                    TAG,
-                                    "disableChangedUsbPortIfDisconnected: " + portStatus == null
-                                            ? "null"
-                                            : portStatus.toString());
-                        }
-
-                        if (mKeyguardManager.isKeyguardLocked()
-                                && portStatus != null && !portStatus.isConnected()
-                        ) {
-                            intent.getParcelableExtra(
-                                            UsbManager.EXTRA_PORT, ParcelableUsbPort.class)
-                                    .getUsbPort(mUsbManager)
-                                    .enableUsbData(false);
-                        }
-                    }
-
+                    // TODO: b/401540215 Remove this as part of pre-release cleanup
                     private void dumpUsbDevices() {
                         Slog.d(TAG, "dumpUsbDevices: ");
                         Map<String, UsbDevice> portStatusMap = mUsbManager.getDeviceList();
@@ -238,9 +231,7 @@
             UsbPortStatus portStatus =
                     intent.getParcelableExtra(UsbManager.EXTRA_PORT_STATUS, UsbPortStatus.class);
             if (mKeyguardManager.isKeyguardLocked()
-                    && portStatus != null
-                    && portStatus.isConnected()
-                    && portStatus.getUsbDataStatus() == UsbPortStatus.DATA_STATUS_DISABLED_FORCE) {
+                    && usbPortIsConnectedWithDataDisabled(portStatus)) {
                 sendNotification(
                         mContext.getString(
                                 R.string.usb_apm_usb_plugged_in_when_locked_notification_title),
@@ -251,39 +242,46 @@
         }
     }
 
-    private void setUsbDataSignalIfNoConnectedDevices(boolean status) {
-        // disable all ports that don't have an active data connection
-        if (!status) {
-            for (UsbPort usbPort : mUsbManager.getPorts()) {
-                if (Build.IS_DEBUGGABLE) {
-                    Slog.i(
-                            TAG,
-                            "setUsbDataSignal: false " + usbPort.getStatus() == null
-                                    ? "null"
-                                    : usbPort.getStatus().toString());
-                }
-                if (usbPort.getStatus() == null
-                        || !usbPort.getStatus().isConnected()
-                        || usbPort.getStatus().getCurrentDataRole()
-                                == UsbPortStatus.DATA_ROLE_NONE) {
-                    usbPort.enableUsbData(false);
-                }
-            }
+    private boolean usbPortIsConnectedWithDataDisabled(UsbPortStatus portStatus) {
+        return portStatus != null
+                && portStatus.isConnected()
+                && portStatus.getUsbDataStatus() == DATA_STATUS_DISABLED_FORCE;
+    }
+
+    private void setUsbDataSignalIfPossible(boolean status) {
+        if (!status && deviceHaveUsbDataConnection()) {
+            return;
         }
-        // Always re-enable all if true
-        else {
-            if (!mUsbManager.enableUsbDataSignal(status)) {
+        try {
+            if (!mUsbManagerInternal.enableUsbDataSignal(status,
+                    OS_USB_DISABLE_REASON_LOCKDOWN_MODE)) {
                 Slog.e(TAG, "USB Data protection toggle failed");
             }
-            for (UsbPort usbPort : mUsbManager.getPorts()) {
-                usbPort.resetUsbPort(mContext.getMainExecutor(),
-                new Consumer<Integer>() {
-                    public void accept(Integer status) {
-                        Slog.i(TAG, "Consumer status: " + status);
-                    }
-                });
+        } catch (RemoteException e) {
+            Slog.e(TAG, "RemoteException thrown when calling enableUsbDataSignal", e);
+        }
+    }
+
+    private boolean deviceHaveUsbDataConnection() {
+        for (UsbPort usbPort : mUsbManager.getPorts()) {
+            if (Build.IS_DEBUGGABLE) {
+                Slog.i(
+                        TAG,
+                        "setUsbDataSignal: false, Port status: " + usbPort.getStatus() == null
+                                ? "null"
+                                : usbPort.getStatus().toString());
+            }
+            if (usbPortIsConnectedWithDataEnabled(usbPort)) {
+                return true;
             }
         }
+        return false;
+    }
+
+    private boolean usbPortIsConnectedWithDataEnabled(UsbPort usbPort) {
+        return usbPort.getStatus() != null
+                && usbPort.getStatus().isConnected()
+                && usbPort.getStatus().getCurrentDataRole() != DATA_ROLE_NONE;
     }
 
     private void registerReceiver() {
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
index 54365ff..c5a43a5 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
@@ -72,14 +72,20 @@
     }
 
     SelinuxAuditLogsCollector(RateLimiter rateLimiter, QuotaLimiter quotaLimiter) {
-        this(
-                () ->
-                        DeviceConfig.getString(
-                                DeviceConfig.NAMESPACE_ADSERVICES,
-                                CONFIG_SELINUX_AUDIT_DOMAIN,
-                                DEFAULT_SELINUX_AUDIT_DOMAIN),
-                rateLimiter,
-                quotaLimiter);
+        this(new DefaultDomainSupplier(), rateLimiter, quotaLimiter);
+    }
+
+    private static class DefaultDomainSupplier implements Supplier<String> {
+        @Override
+        public String get() {
+            if (SelinuxAuditLogsService.enabledForAllDomains()) {
+                return "\\w+";
+            }
+            return DeviceConfig.getString(
+                    DeviceConfig.NAMESPACE_ADSERVICES,
+                    CONFIG_SELINUX_AUDIT_DOMAIN,
+                    DEFAULT_SELINUX_AUDIT_DOMAIN);
+        }
     }
 
     public void setStopRequested(boolean stopRequested) {
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsService.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsService.java
index d46e891..9dc457c 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsService.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsService.java
@@ -16,6 +16,7 @@
 package com.android.server.selinux;
 
 import static com.android.sdksandbox.flags.Flags.selinuxSdkSandboxAudit;
+import static com.android.server.selinux.flags.Flags.selinuxLogsCollect;
 
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
@@ -49,6 +50,9 @@
             "selinux_audit_job_frequency_hours";
     private static final String CONFIG_SELINUX_ENABLE_AUDIT_JOB = "selinux_enable_audit_job";
     private static final String CONFIG_SELINUX_AUDIT_CAP = "selinux_audit_cap";
+    private static final String DEVICE_CONFIG_SECURITY_NAMESPACE = "security";
+    private static final String CONFIG_SECURITY_SELINUX_AUDIT_JOB_ENABLED =
+            "selinux_audit_job_enabled";
     private static final int MAX_PERMITS_CAP_DEFAULT = 50000;
 
     private static final int SELINUX_AUDIT_JOB_ID = 25327386;
@@ -76,7 +80,7 @@
 
     /** Schedule jobs with the {@link JobScheduler}. */
     public static void schedule(Context context) {
-        if (!selinuxSdkSandboxAudit()) {
+        if (!selinuxSdkSandboxAudit() && !enabledForAllDomains()) {
             Slog.d(TAG, "SelinuxAuditLogsService not enabled");
             return;
         }
@@ -86,13 +90,20 @@
             return;
         }
 
-        LogsCollectorJobScheduler propertiesListener =
+        LogsCollectorJobScheduler scheduler =
                 new LogsCollectorJobScheduler(
                         context.getSystemService(JobScheduler.class)
                                 .forNamespace(SELINUX_AUDIT_NAMESPACE));
-        propertiesListener.schedule();
+        scheduler.schedule();
+
+        AdServicesPropertyMonitor adServicesProperties = new AdServicesPropertyMonitor(scheduler);
         DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_ADSERVICES, context.getMainExecutor(), propertiesListener);
+                DeviceConfig.NAMESPACE_ADSERVICES, context.getMainExecutor(), adServicesProperties);
+
+        SecurityPropertyMonitor securityProperties = new SecurityPropertyMonitor(scheduler);
+        DeviceConfig.addOnPropertiesChangedListener(
+                DEVICE_CONFIG_SECURITY_NAMESPACE, context.getMainExecutor(), securityProperties);
+
     }
 
     @Override
@@ -101,7 +112,7 @@
             Slog.e(TAG, "The job id does not match the expected selinux job id.");
             return false;
         }
-        if (!selinuxSdkSandboxAudit()) {
+        if (!selinuxSdkSandboxAudit() && !enabledForAllDomains()) {
             Slog.i(TAG, "Selinux audit job disabled.");
             return false;
         }
@@ -123,17 +134,33 @@
         return false;
     }
 
-    /**
-     * This class is in charge of scheduling the job service, and keeping the scheduling up to date
-     * when the parameters change.
-     */
-    private static final class LogsCollectorJobScheduler
+    /** Checks if the service is enabled for all domains */
+    public static final boolean enabledForAllDomains() {
+        if (selinuxLogsCollect()) {
+            return DeviceConfig.getBoolean(
+                    DEVICE_CONFIG_SECURITY_NAMESPACE,
+                    CONFIG_SECURITY_SELINUX_AUDIT_JOB_ENABLED,
+                    false);
+        }
+        return false;
+    }
+
+    /** Checks if the service is enabled for SDK Sandbox */
+    public static final boolean enabledForSdkSandbox() {
+        if (selinuxSdkSandboxAudit()) {
+            return DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_ADSERVICES, CONFIG_SELINUX_ENABLE_AUDIT_JOB, false);
+        }
+        return false;
+    }
+
+    private static final class AdServicesPropertyMonitor
             implements DeviceConfig.OnPropertiesChangedListener {
 
-        private final JobScheduler mJobScheduler;
+        private final LogsCollectorJobScheduler mScheduler;
 
-        private LogsCollectorJobScheduler(JobScheduler jobScheduler) {
-            mJobScheduler = jobScheduler;
+        private AdServicesPropertyMonitor(LogsCollectorJobScheduler scheduler) {
+            mScheduler = scheduler;
         }
 
         @Override
@@ -149,19 +176,65 @@
             if (keyset.contains(CONFIG_SELINUX_ENABLE_AUDIT_JOB)) {
                 boolean enabled =
                         changedProperties.getBoolean(
-                                CONFIG_SELINUX_ENABLE_AUDIT_JOB, /* defaultValue= */ false);
+                                CONFIG_SELINUX_ENABLE_AUDIT_JOB, /* defaultValue= */ false)
+                        || enabledForAllDomains();
                 if (enabled) {
-                    schedule();
+                    mScheduler.schedule();
                 } else {
-                    mJobScheduler.cancel(SELINUX_AUDIT_JOB_ID);
+                    mScheduler.cancel();
                 }
             } else if (keyset.contains(CONFIG_SELINUX_AUDIT_JOB_FREQUENCY_HOURS)) {
                 // The job frequency changed, reschedule.
-                schedule();
+                mScheduler.schedule();
             }
         }
+    }
 
-        private void schedule() {
+    private static final class SecurityPropertyMonitor
+            implements DeviceConfig.OnPropertiesChangedListener {
+
+        private final LogsCollectorJobScheduler mScheduler;
+
+        private SecurityPropertyMonitor(LogsCollectorJobScheduler scheduler) {
+            mScheduler = scheduler;
+        }
+
+        @Override
+        public void onPropertiesChanged(Properties changedProperties) {
+            Set<String> keyset = changedProperties.getKeyset();
+
+            if (keyset.contains(CONFIG_SECURITY_SELINUX_AUDIT_JOB_ENABLED)) {
+                boolean enabled =
+                        changedProperties.getBoolean(
+                                CONFIG_SECURITY_SELINUX_AUDIT_JOB_ENABLED,
+                                /* defaultValue= */ false)
+                        || enabledForSdkSandbox();
+                if (enabled) {
+                    mScheduler.schedule();
+                } else {
+                    mScheduler.cancel();
+                }
+            }
+        }
+    }
+
+    /**
+     * This class is in charge of scheduling the job service, and keeping the scheduling up to date
+     * when the parameters change.
+     */
+    private static final class LogsCollectorJobScheduler {
+
+        private final JobScheduler mJobScheduler;
+
+        private LogsCollectorJobScheduler(JobScheduler jobScheduler) {
+            mJobScheduler = jobScheduler;
+        }
+
+        public void cancel() {
+            mJobScheduler.cancel(SELINUX_AUDIT_JOB_ID);
+        }
+
+        public void schedule() {
             long frequencyMillis =
                     TimeUnit.HOURS.toMillis(
                             DeviceConfig.getInt(
diff --git a/services/core/java/com/android/server/selinux/flags.aconfig b/services/core/java/com/android/server/selinux/flags.aconfig
new file mode 100644
index 0000000..3bb5a6b
--- /dev/null
+++ b/services/core/java/com/android/server/selinux/flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.server.selinux.flags"
+container: "system"
+
+flag {
+    name: "selinux_logs_collect"
+    namespace: "network_security"
+    description: "Enable collection of SELinux denials based on selinux_audit_job_enabled"
+    bug: "372950125"
+}
diff --git a/services/core/java/com/android/server/sensors/OWNERS b/services/core/java/com/android/server/sensors/OWNERS
new file mode 100644
index 0000000..6b22473
--- /dev/null
+++ b/services/core/java/com/android/server/sensors/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/native:/services/sensorservice/OWNERS
diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
index 7ff4ade..9636cc6 100644
--- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java
+++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
@@ -17,6 +17,7 @@
 package com.android.server.sensors;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.SensorDirectChannel;
 import android.os.ParcelFileDescriptor;
 
@@ -71,7 +72,7 @@
     /**
      * Sends an event for the runtime sensor with the given handle to the framework.
      *
-     * Only relevant for sending runtime sensor events. @see #createRuntimeSensor.
+     * <p>Only relevant for sending runtime sensor events. @see #createRuntimeSensor.</p>
      *
      * @param handle The sensor handle.
      * @param type The type of the sensor.
@@ -83,6 +84,21 @@
             @NonNull float[] values);
 
     /**
+     * Sends an additional info event for the runtime sensor with the given handle to the framework.
+     *
+     * <p>Only relevant for runtime sensors. @see #createRuntimeSensor.</p>
+     *
+     * @param handle The sensor handle.
+     * @param type The type of payload data.
+     * @param serial The sequence number of this frame for this type.
+     * @param timestampNanos Timestamp of the event.
+     * @param values The payload data represented in float values.
+     * @return Whether the event injection was successful.
+     */
+    public abstract boolean sendSensorAdditionalInfo(int handle, int type, int serial,
+            long timestampNanos, @Nullable float[] values);
+
+    /**
      * Listener for proximity sensor state changes.
      */
     public interface ProximityActiveListener {
diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java
index 3de1910..0d31b22 100644
--- a/services/core/java/com/android/server/sensors/SensorService.java
+++ b/services/core/java/com/android/server/sensors/SensorService.java
@@ -19,6 +19,7 @@
 import static com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.util.ArrayMap;
 
@@ -62,6 +63,9 @@
     private static native void unregisterRuntimeSensorNative(long ptr, int handle);
     private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type,
             long timestampNanos, float[] values);
+    private static native boolean sendRuntimeSensorAdditionalInfoNative(long ptr, int handle,
+            int type, int serial, long timestampNanos, float[] values);
+
 
     public SensorService(Context ctx) {
         super(ctx);
@@ -129,6 +133,18 @@
         }
 
         @Override
+        public boolean sendSensorAdditionalInfo(int handle, int type, int serial,
+                long timestampNanos, @Nullable float[] values) {
+            synchronized (mLock) {
+                if (!mRuntimeSensorHandles.contains(handle)) {
+                    return false;
+                }
+                return sendRuntimeSensorAdditionalInfoNative(mPtr, handle, type, serial,
+                        timestampNanos, values);
+            }
+        }
+
+        @Override
         public void addProximityActiveListener(@NonNull Executor executor,
                 @NonNull ProximityActiveListener listener) {
             Objects.requireNonNull(executor, "executor must not be null");
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 27ca83a..126b3a7 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -351,18 +351,38 @@
             }
         }
 
+
         private void waitForAsyncVoid(AsyncStorageServiceCall asyncCall) throws Exception {
+            waitForAsyncVoid(asyncCall, /*bindIfNotConnected*/ true,
+                    DEFAULT_REMOTE_TIMEOUT_SECONDS);
+        }
+
+        private void waitForAsyncVoid(AsyncStorageServiceCall asyncCall,
+                boolean bindIfNotConnected, int timeoutSeconds) throws Exception {
             CompletableFuture<Void> opFuture = new CompletableFuture<>();
             RemoteCallback callback = new RemoteCallback(result -> setResult(result, opFuture));
 
-            waitForAsync(asyncCall, callback, opFuture, mOutstandingOps,
-                    DEFAULT_REMOTE_TIMEOUT_SECONDS);
+            waitForAsync(asyncCall, callback, opFuture, mOutstandingOps, bindIfNotConnected,
+                    timeoutSeconds);
         }
 
         private <T> T waitForAsync(AsyncStorageServiceCall asyncCall, RemoteCallback callback,
                 CompletableFuture<T> opFuture, ArrayList<CompletableFuture<T>> outstandingOps,
-                long timeoutSeconds) throws Exception {
-            CompletableFuture<IExternalStorageService> serviceFuture = connectIfNeeded();
+                boolean bindIfNotConnected, long timeoutSeconds) throws Exception {
+
+            CompletableFuture<IExternalStorageService> serviceFuture;
+            if (bindIfNotConnected) {
+                serviceFuture = connectIfNeeded();
+            } else {
+                synchronized (mLock) {
+                    if (mRemoteFuture == null || mRemoteFuture.getNow(null) == null) {
+                        Slog.w(TAG, "Dropping async request as service is not connected"
+                                + "and request doesn't require connecting");
+                        return null;
+                    }
+                    serviceFuture = mRemoteFuture;
+                }
+            }
 
             try {
                 synchronized (mLock) {
@@ -404,7 +424,11 @@
         public void endSession(Session session) throws ExternalStorageServiceException {
             try {
                 waitForAsyncVoid((service, callback) ->
-                        service.endSession(session.sessionId, callback));
+                        service.endSession(session.sessionId, callback),
+                        // endSession shouldn't be trying to bind to remote service if the service
+                        // isn't connected already as this means that no previous mounting has been
+                        // completed.
+                        /*bindIfNotConnected*/ false, /*timeoutSeconds*/ 10);
             } catch (Exception e) {
                 throw new ExternalStorageServiceException("Failed to end session: " + session, e);
             }
@@ -415,7 +439,11 @@
                 ExternalStorageServiceException {
             try {
                 waitForAsyncVoid((service, callback) ->
-                        service.notifyVolumeStateChanged(sessionId, vol, callback));
+                        service.notifyVolumeStateChanged(sessionId, vol, callback),
+                        // notifyVolumeStateChanged shouldn't be trying to bind to remote service
+                        // if the service isn't connected already as this means that
+                        // no previous mounting has been completed
+                        /*bindIfNotConnected*/ false, /*timeoutSeconds*/ 10);
             } catch (Exception e) {
                 throw new ExternalStorageServiceException("Failed to notify volume state changed "
                         + "for vol : " + vol, e);
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 31348cd..17980c0 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -177,6 +177,7 @@
     private final String mDefaultTextClassifierPackage;
     @Nullable
     private final String mSystemTextClassifierPackage;
+    private final MyPackageMonitor mPackageMonitor;
 
     private TextClassificationManagerService(Context context) {
         mContext = Objects.requireNonNull(context);
@@ -187,50 +188,50 @@
         mDefaultTextClassifierPackage = packageManager.getDefaultTextClassifierPackageName();
         mSystemTextClassifierPackage = packageManager.getSystemTextClassifierPackageName();
         mSessionCache = new SessionCache(mLock);
+        mPackageMonitor = new MyPackageMonitor();
     }
 
     private void startListenSettings() {
         mSettingsListener.registerObserver();
     }
 
+    private class MyPackageMonitor extends PackageMonitor {
+        @Override
+        public void onPackageAdded(String packageName, int uid) {
+            notifyPackageInstallStatusChange(packageName, /* installed*/ true);
+        }
+
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            notifyPackageInstallStatusChange(packageName, /* installed= */ false);
+        }
+
+        @Override
+        public void onPackageModified(String packageName) {
+            final int userId = getChangingUserId();
+            synchronized (mLock) {
+                final UserState userState = getUserStateLocked(userId);
+                final ServiceState serviceState = userState.getServiceStateLocked(packageName);
+                if (serviceState != null) {
+                    serviceState.onPackageModifiedLocked();
+                }
+            }
+        }
+
+        private void notifyPackageInstallStatusChange(String packageName, boolean installed) {
+            final int userId = getChangingUserId();
+            synchronized (mLock) {
+                final UserState userState = getUserStateLocked(userId);
+                final ServiceState serviceState = userState.getServiceStateLocked(packageName);
+                if (serviceState != null) {
+                    serviceState.onPackageInstallStatusChangeLocked(installed);
+                }
+            }
+        }
+    }
+
     void startTrackingPackageChanges() {
-        final PackageMonitor monitor = new PackageMonitor() {
-
-            @Override
-            public void onPackageAdded(String packageName, int uid) {
-                notifyPackageInstallStatusChange(packageName, /* installed*/ true);
-            }
-
-            @Override
-            public void onPackageRemoved(String packageName, int uid) {
-                notifyPackageInstallStatusChange(packageName, /* installed= */ false);
-            }
-
-            @Override
-            public void onPackageModified(String packageName) {
-                final int userId = getChangingUserId();
-                synchronized (mLock) {
-                    final UserState userState = getUserStateLocked(userId);
-                    final ServiceState serviceState = userState.getServiceStateLocked(packageName);
-                    if (serviceState != null) {
-                        serviceState.onPackageModifiedLocked();
-                    }
-                }
-            }
-
-            private void notifyPackageInstallStatusChange(String packageName, boolean installed) {
-                final int userId = getChangingUserId();
-                synchronized (mLock) {
-                    final UserState userState = getUserStateLocked(userId);
-                    final ServiceState serviceState = userState.getServiceStateLocked(packageName);
-                    if (serviceState != null) {
-                        serviceState.onPackageInstallStatusChangeLocked(installed);
-                    }
-                }
-            }
-        };
-
-        monitor.register(mContext, null,  UserHandle.ALL, true);
+       mPackageMonitor.register(mContext, null,  UserHandle.ALL, true);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 8bcf1a9..47d6879 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -183,7 +183,7 @@
     private final Map<String, SessionState> mSessionIdToSessionStateMap = new HashMap<>();
 
     private final MessageHandler mMessageHandler;
-
+    private final MyPackageMonitor mPackageMonitor;
     private final ActivityManager mActivityManager;
 
     private boolean mExternalInputLoggingDisplayNameFilterEnabled = false;
@@ -200,6 +200,7 @@
         mMessageHandler =
                 new MessageHandler(mContext.getContentResolver(), IoThread.get().getLooper());
         mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
+        mPackageMonitor = new MyPackageMonitor(/* supportsPackageRestartQuery */ true);
 
         mActivityManager =
                 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
@@ -298,74 +299,79 @@
         mExternalInputLoggingDeviceBrandNames.addAll(Arrays.asList(deviceBrandNames));
     }
 
+    private class MyPackageMonitor extends PackageMonitor {
+        MyPackageMonitor(boolean supportsPackageRestartQuery) {
+            super(supportsPackageRestartQuery);
+        }
+
+        private void buildTvInputList(String[] packages) {
+            int userId = getChangingUserId();
+            synchronized (mLock) {
+                if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
+                    buildTvInputListLocked(userId, packages);
+                    buildTvContentRatingSystemListLocked(userId);
+                }
+            }
+        }
+
+        @Override
+        public void onPackageUpdateFinished(String packageName, int uid) {
+            if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
+            // This callback is invoked when the TV input is reinstalled.
+            // In this case, isReplacing() always returns true.
+            buildTvInputList(new String[] { packageName });
+        }
+
+        @Override
+        public void onPackagesAvailable(String[] packages) {
+            if (DEBUG) {
+                Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
+            }
+            // This callback is invoked when the media on which some packages exist become
+            // available.
+            if (isReplacing()) {
+                buildTvInputList(packages);
+            }
+        }
+
+        @Override
+        public void onPackagesUnavailable(String[] packages) {
+            // This callback is invoked when the media on which some packages exist become
+            // unavailable.
+            if (DEBUG)  {
+                Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
+                        + ")");
+            }
+            if (isReplacing()) {
+                buildTvInputList(packages);
+            }
+        }
+
+        @Override
+        public void onSomePackagesChanged() {
+            // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage
+            // the TV inputs.
+            if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()");
+            if (isReplacing()) {
+                if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing");
+                // When the package is updated, buildTvInputListLocked is called in other
+                // methods instead.
+                return;
+            }
+            buildTvInputList(null);
+        }
+
+        @Override
+        public boolean onPackageChanged(String packageName, int uid, String[] components) {
+            // The input list needs to be updated in any cases, regardless of whether
+            // it happened to the whole package or a specific component. Returning true so that
+            // the update can be handled in {@link #onSomePackagesChanged}.
+            return true;
+        }
+    }
+
     private void registerBroadcastReceivers() {
-        PackageMonitor monitor = new PackageMonitor(/* supportsPackageRestartQuery */ true) {
-            private void buildTvInputList(String[] packages) {
-                int userId = getChangingUserId();
-                synchronized (mLock) {
-                    if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
-                        buildTvInputListLocked(userId, packages);
-                        buildTvContentRatingSystemListLocked(userId);
-                    }
-                }
-            }
-
-            @Override
-            public void onPackageUpdateFinished(String packageName, int uid) {
-                if (DEBUG) Slog.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
-                // This callback is invoked when the TV input is reinstalled.
-                // In this case, isReplacing() always returns true.
-                buildTvInputList(new String[] { packageName });
-            }
-
-            @Override
-            public void onPackagesAvailable(String[] packages) {
-                if (DEBUG) {
-                    Slog.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
-                }
-                // This callback is invoked when the media on which some packages exist become
-                // available.
-                if (isReplacing()) {
-                    buildTvInputList(packages);
-                }
-            }
-
-            @Override
-            public void onPackagesUnavailable(String[] packages) {
-                // This callback is invoked when the media on which some packages exist become
-                // unavailable.
-                if (DEBUG)  {
-                    Slog.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
-                            + ")");
-                }
-                if (isReplacing()) {
-                    buildTvInputList(packages);
-                }
-            }
-
-            @Override
-            public void onSomePackagesChanged() {
-                // TODO: Use finer-grained methods(e.g. onPackageAdded, onPackageRemoved) to manage
-                // the TV inputs.
-                if (DEBUG) Slog.d(TAG, "onSomePackagesChanged()");
-                if (isReplacing()) {
-                    if (DEBUG) Slog.d(TAG, "Skipped building TV input list due to replacing");
-                    // When the package is updated, buildTvInputListLocked is called in other
-                    // methods instead.
-                    return;
-                }
-                buildTvInputList(null);
-            }
-
-            @Override
-            public boolean onPackageChanged(String packageName, int uid, String[] components) {
-                // The input list needs to be updated in any cases, regardless of whether
-                // it happened to the whole package or a specific component. Returning true so that
-                // the update can be handled in {@link #onSomePackagesChanged}.
-                return true;
-            }
-        };
-        monitor.register(mContext, null, UserHandle.ALL, true);
+        mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
 
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index 6a7fc6d..42013fa 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -105,6 +105,7 @@
     // A global lock.
     private final Object mLock = new Object();
     private final Context mContext;
+    private final MyPackageMonitor mPackageMonitor;
     // ID of the current user.
     @GuardedBy("mLock")
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
@@ -138,6 +139,7 @@
         super(context);
         mContext = context;
         mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+        mPackageMonitor = new MyPackageMonitor(/* supportsPackageRestartQuery */ true);
     }
 
     @GuardedBy("mLock")
@@ -518,86 +520,91 @@
         }
     }
 
-    private void registerBroadcastReceivers() {
-        PackageMonitor monitor = new PackageMonitor(/* supportsPackageRestartQuery */ true) {
-            private void buildTvInteractiveAppServiceList(String[] packages) {
-                int userId = getChangingUserId();
-                synchronized (mLock) {
-                    if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
-                        buildTvInteractiveAppServiceListLocked(userId, packages);
-                        buildAppLinkInfoLocked(userId);
-                    }
+    private class MyPackageMonitor extends PackageMonitor {
+        MyPackageMonitor(boolean supportsPackageRestartQuery) {
+            super(supportsPackageRestartQuery);
+        }
+
+        private void buildTvInteractiveAppServiceList(String[] packages) {
+            int userId = getChangingUserId();
+            synchronized (mLock) {
+                if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
+                    buildTvInteractiveAppServiceListLocked(userId, packages);
+                    buildAppLinkInfoLocked(userId);
                 }
             }
-            private void buildTvAdServiceList(String[] packages) {
-                int userId = getChangingUserId();
-                synchronized (mLock) {
-                    if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
-                        buildTvAdServiceListLocked(userId, packages);
-                    }
+        }
+        private void buildTvAdServiceList(String[] packages) {
+            int userId = getChangingUserId();
+            synchronized (mLock) {
+                if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
+                    buildTvAdServiceListLocked(userId, packages);
                 }
             }
+        }
 
-            @Override
-            public void onPackageUpdateFinished(String packageName, int uid) {
-                if (DEBUG) Slogf.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
-                // This callback is invoked when the TV interactive App service is reinstalled.
-                // In this case, isReplacing() always returns true.
-                buildTvInteractiveAppServiceList(new String[] { packageName });
-                buildTvAdServiceList(new String[] { packageName });
+        @Override
+        public void onPackageUpdateFinished(String packageName, int uid) {
+            if (DEBUG) Slogf.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
+            // This callback is invoked when the TV interactive App service is reinstalled.
+            // In this case, isReplacing() always returns true.
+            buildTvInteractiveAppServiceList(new String[] { packageName });
+            buildTvAdServiceList(new String[] { packageName });
+        }
+
+        @Override
+        public void onPackagesAvailable(String[] packages) {
+            if (DEBUG) {
+                Slogf.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
             }
+            // This callback is invoked when the media on which some packages exist become
+            // available.
+            if (isReplacing()) {
+                buildTvInteractiveAppServiceList(packages);
+                buildTvAdServiceList(packages);
+            }
+        }
 
-            @Override
-            public void onPackagesAvailable(String[] packages) {
+        @Override
+        public void onPackagesUnavailable(String[] packages) {
+            // This callback is invoked when the media on which some packages exist become
+            // unavailable.
+            if (DEBUG)  {
+                Slogf.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
+                        + ")");
+            }
+            if (isReplacing()) {
+                buildTvInteractiveAppServiceList(packages);
+                buildTvAdServiceList(packages);
+            }
+        }
+
+        @Override
+        public void onSomePackagesChanged() {
+            if (DEBUG) Slogf.d(TAG, "onSomePackagesChanged()");
+            if (isReplacing()) {
                 if (DEBUG) {
-                    Slogf.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
+                    Slogf.d(TAG, "Skipped building TV interactive App list due to replacing");
                 }
-                // This callback is invoked when the media on which some packages exist become
-                // available.
-                if (isReplacing()) {
-                    buildTvInteractiveAppServiceList(packages);
-                    buildTvAdServiceList(packages);
-                }
+                // When the package is updated, buildTvInteractiveAppServiceListLocked is called
+                // in other methods instead.
+                return;
             }
+            buildTvInteractiveAppServiceList(null);
+            buildTvAdServiceList(null);
+        }
 
-            @Override
-            public void onPackagesUnavailable(String[] packages) {
-                // This callback is invoked when the media on which some packages exist become
-                // unavailable.
-                if (DEBUG)  {
-                    Slogf.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
-                            + ")");
-                }
-                if (isReplacing()) {
-                    buildTvInteractiveAppServiceList(packages);
-                    buildTvAdServiceList(packages);
-                }
-            }
+        @Override
+        public boolean onPackageChanged(String packageName, int uid, String[] components) {
+            // The interactive App list needs to be updated in any cases, regardless of whether
+            // it happened to the whole package or a specific component. Returning true so that
+            // the update can be handled in {@link #onSomePackagesChanged}.
+            return true;
+        }
+    }
 
-            @Override
-            public void onSomePackagesChanged() {
-                if (DEBUG) Slogf.d(TAG, "onSomePackagesChanged()");
-                if (isReplacing()) {
-                    if (DEBUG) {
-                        Slogf.d(TAG, "Skipped building TV interactive App list due to replacing");
-                    }
-                    // When the package is updated, buildTvInteractiveAppServiceListLocked is called
-                    // in other methods instead.
-                    return;
-                }
-                buildTvInteractiveAppServiceList(null);
-                buildTvAdServiceList(null);
-            }
-
-            @Override
-            public boolean onPackageChanged(String packageName, int uid, String[] components) {
-                // The interactive App list needs to be updated in any cases, regardless of whether
-                // it happened to the whole package or a specific component. Returning true so that
-                // the update can be handled in {@link #onSomePackagesChanged}.
-                return true;
-            }
-        };
-        monitor.register(mContext, null, UserHandle.ALL, true);
+    private void registerBroadcastReceivers() {
+        mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
 
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
diff --git a/services/core/java/com/android/server/updates/CertPinInstallReceiver.java b/services/core/java/com/android/server/updates/CertPinInstallReceiver.java
index c8e7a8d..250e99b 100644
--- a/services/core/java/com/android/server/updates/CertPinInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/CertPinInstallReceiver.java
@@ -19,10 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 
-import java.io.File;
-
 public class CertPinInstallReceiver extends ConfigUpdateInstallReceiver {
-    private static final String KEYCHAIN_DIR = "/data/misc/keychain/";
 
     public CertPinInstallReceiver() {
         super("/data/misc/keychain/", "pins", "metadata/", "version");
@@ -30,22 +27,7 @@
 
     @Override
     public void onReceive(final Context context, final Intent intent) {
-        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
-            if (com.android.server.flags.Flags.certpininstallerRemoval()) {
-                File pins = new File(KEYCHAIN_DIR + "pins");
-                if (pins.exists()) {
-                    pins.delete();
-                }
-                File version = new File(KEYCHAIN_DIR + "metadata/version");
-                if (version.exists()) {
-                    version.delete();
-                }
-                File metadata = new File(KEYCHAIN_DIR + "metadata");
-                if (metadata.exists()) {
-                    metadata.delete();
-                }
-            }
-        } else if (!com.android.server.flags.Flags.certpininstallerRemoval()) {
+        if (!com.android.server.flags.Flags.certpininstallerRemoval()) {
             super.onReceive(context, intent);
         }
     }
diff --git a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
index 7c2ce64..458cb02 100644
--- a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
+++ b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
@@ -58,6 +58,7 @@
 
     private final Object mLock;
     private final Context mContext;
+    private final PackageMonitor mPackageMonitor;
     private final String mSettingName;
     private final String mServiceName;
     private final String mServicePermission;
@@ -78,13 +79,39 @@
 
     private EnabledComponentsObserver(@NonNull Context context, @NonNull String settingName,
             @NonNull String servicePermission, @NonNull String serviceName, @NonNull Object lock,
-            @NonNull Collection<EnabledComponentChangeListener> listeners) {
+            @NonNull Collection<EnabledComponentChangeListener> listeners,
+            @NonNull Looper looper) {
         mLock = lock;
         mContext = context;
         mSettingName = settingName;
         mServiceName = serviceName;
         mServicePermission = servicePermission;
         mEnabledComponentListeners.addAll(listeners);
+        mPackageMonitor = new PackageMonitor(true) {
+            @Override
+            public void onSomePackagesChanged() {
+                onPackagesChanged();
+            }
+
+            @Override
+            public void onPackageDisappeared(String packageName, int reason) {
+                onPackagesChanged();
+            }
+
+            @Override
+            public void onPackageModified(String packageName) {
+                onPackagesChanged();
+            }
+
+            @Override
+            public boolean onHandleForceStop(Intent intent, String[] packages, int uid,
+                    boolean doit) {
+                onPackagesChanged();
+                return super.onHandleForceStop(intent, packages, uid, doit);
+            }
+        };
+
+        mPackageMonitor.register(context, looper, UserHandle.ALL, true);;
     }
 
     /**
@@ -108,38 +135,7 @@
         SettingsObserver s = SettingsObserver.build(context, handler, settingName);
 
         final EnabledComponentsObserver o = new EnabledComponentsObserver(context, settingName,
-                servicePermission, serviceName, lock, listeners);
-
-        PackageMonitor packageMonitor = new PackageMonitor(true) {
-            @Override
-            public void onSomePackagesChanged() {
-                o.onPackagesChanged();
-
-            }
-
-            @Override
-            public void onPackageDisappeared(String packageName, int reason) {
-                o.onPackagesChanged();
-
-            }
-
-            @Override
-            public void onPackageModified(String packageName) {
-                o.onPackagesChanged();
-
-            }
-
-            @Override
-            public boolean onHandleForceStop(Intent intent, String[] packages, int uid,
-                    boolean doit) {
-                o.onPackagesChanged();
-
-                return super.onHandleForceStop(intent, packages, uid, doit);
-            }
-        };
-
-        packageMonitor.register(context, looper, UserHandle.ALL, true);
-
+                servicePermission, serviceName, lock, listeners, looper);
         s.addListener(o);
 
         return o;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 8e8455a..424439d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -17,7 +17,6 @@
 package com.android.server.wallpaper;
 
 import static android.app.WallpaperManager.ORIENTATION_LANDSCAPE;
-import static android.app.WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE;
 import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
 import static android.app.WallpaperManager.getOrientation;
 import static android.app.WallpaperManager.getRotatedOrientation;
@@ -85,20 +84,11 @@
 
     private final WallpaperDisplayHelper mWallpaperDisplayHelper;
 
-    /**
-     * Helpers exposed to the window manager part (WallpaperController)
-     */
-    public interface WallpaperCropUtils {
-
-        /**
-         * Equivalent to {@link WallpaperCropper#getCrop(Point, Point, SparseArray, boolean)}
-         */
-        Rect getCrop(Point displaySize, Point bitmapSize,
-                SparseArray<Rect> suggestedCrops, boolean rtl);
-    }
+    private final WallpaperDefaultDisplayInfo mDefaultDisplayInfo;
 
     WallpaperCropper(WallpaperDisplayHelper wallpaperDisplayHelper) {
         mWallpaperDisplayHelper = wallpaperDisplayHelper;
+        mDefaultDisplayInfo = mWallpaperDisplayHelper.getDefaultDisplayInfo();
     }
 
     /**
@@ -116,16 +106,16 @@
      *     {@link #getAdjustedCrop}.
      * </ul>
      *
-     * @param displaySize     The dimensions of the surface where we want to render the wallpaper
-     * @param bitmapSize      The dimensions of the wallpaper bitmap
-     * @param rtl             Whether the device is right-to-left
-     * @param suggestedCrops  An optional list of user-defined crops for some orientations.
-     *                        If there is a suggested crop for
+     * @param displaySize        The dimensions of the surface where we want to render the wallpaper
+     * @param defaultDisplayInfo The default display info
+     * @param bitmapSize         The dimensions of the wallpaper bitmap
+     * @param rtl                Whether the device is right-to-left
+     * @param suggestedCrops     An optional list of user-defined crops for some orientations.
      *
      * @return  A Rect indicating how to crop the bitmap for the current display.
      */
-    public Rect getCrop(Point displaySize, Point bitmapSize,
-            SparseArray<Rect> suggestedCrops, boolean rtl) {
+    public static Rect getCrop(Point displaySize, WallpaperDefaultDisplayInfo defaultDisplayInfo,
+            Point bitmapSize, SparseArray<Rect> suggestedCrops, boolean rtl) {
 
         int orientation = getOrientation(displaySize);
 
@@ -135,23 +125,24 @@
 
             // The first exception is if the device is a foldable and we're on the folded screen.
             // In that case, show the center of what's on the unfolded screen.
-            int unfoldedOrientation = mWallpaperDisplayHelper.getUnfoldedOrientation(orientation);
+            int unfoldedOrientation = defaultDisplayInfo.getUnfoldedOrientation(orientation);
             if (unfoldedOrientation != ORIENTATION_UNKNOWN) {
                 // Let the system know that we're showing the full image on the unfolded screen
                 SparseArray<Rect> newSuggestedCrops = new SparseArray<>();
                 newSuggestedCrops.put(unfoldedOrientation, crop);
                 // This will fall into "Case 4" of this function and center the folded screen
-                return getCrop(displaySize, bitmapSize, newSuggestedCrops, rtl);
+                return getCrop(displaySize, defaultDisplayInfo, bitmapSize, newSuggestedCrops,
+                        rtl);
             }
 
             // The second exception is if we're on tablet and we're on portrait mode.
             // In that case, center the wallpaper relatively to landscape and put some parallax.
-            boolean isTablet = mWallpaperDisplayHelper.isLargeScreen()
-                    && !mWallpaperDisplayHelper.isFoldable();
+            boolean isTablet = defaultDisplayInfo.isLargeScreen && !defaultDisplayInfo.isFoldable;
             if (isTablet && displaySize.x < displaySize.y) {
                 Point rotatedDisplaySize = new Point(displaySize.y, displaySize.x);
                 // compute the crop on landscape (without parallax)
-                Rect landscapeCrop = getCrop(rotatedDisplaySize, bitmapSize, suggestedCrops, rtl);
+                Rect landscapeCrop = getCrop(rotatedDisplaySize, defaultDisplayInfo, bitmapSize,
+                        suggestedCrops, rtl);
                 landscapeCrop = noParallax(landscapeCrop, rotatedDisplaySize, bitmapSize, rtl);
                 // compute the crop on portrait at the center of the landscape crop
                 crop = getAdjustedCrop(landscapeCrop, bitmapSize, displaySize, false, rtl, ADD);
@@ -173,7 +164,8 @@
             if (testCrop == null || testCrop.left < 0 || testCrop.top < 0
                     || testCrop.right > bitmapSize.x || testCrop.bottom > bitmapSize.y) {
                 Slog.w(TAG, "invalid crop: " + testCrop + " for bitmap size: " + bitmapSize);
-                return getCrop(displaySize, bitmapSize, new SparseArray<>(), rtl);
+                return getCrop(displaySize, defaultDisplayInfo, bitmapSize, new SparseArray<>(),
+                        rtl);
             }
         }
 
@@ -185,10 +177,9 @@
 
         // Case 3: if we have the 90° rotated orientation in the suggested crops, reuse it and
         // trying to preserve the zoom level and the center of the image
-        SparseArray<Point> defaultDisplaySizes = mWallpaperDisplayHelper.getDefaultDisplaySizes();
         int rotatedOrientation = getRotatedOrientation(orientation);
         suggestedCrop = suggestedCrops.get(rotatedOrientation);
-        Point suggestedDisplaySize = defaultDisplaySizes.get(rotatedOrientation);
+        Point suggestedDisplaySize = defaultDisplayInfo.defaultDisplaySizes.get(rotatedOrientation);
         if (suggestedCrop != null) {
             // only keep the visible part (without parallax)
             Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
@@ -197,9 +188,9 @@
 
         // Case 4: if the device is a foldable, if we're looking for a folded orientation and have
         // the suggested crop of the relative unfolded orientation, reuse it by removing content.
-        int unfoldedOrientation = mWallpaperDisplayHelper.getUnfoldedOrientation(orientation);
+        int unfoldedOrientation = defaultDisplayInfo.getUnfoldedOrientation(orientation);
         suggestedCrop = suggestedCrops.get(unfoldedOrientation);
-        suggestedDisplaySize = defaultDisplaySizes.get(unfoldedOrientation);
+        suggestedDisplaySize = defaultDisplayInfo.defaultDisplaySizes.get(unfoldedOrientation);
         if (suggestedCrop != null) {
             // compute the visible part (without parallax) of the unfolded screen
             Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
@@ -207,8 +198,11 @@
             Rect res = getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, REMOVE);
             // if we removed some width, add it back to add a parallax effect
             if (res.width() < adjustedCrop.width()) {
-                if (rtl) res.left = Math.min(res.left, adjustedCrop.left);
-                else res.right = Math.max(res.right, adjustedCrop.right);
+                if (rtl) {
+                    res.left = Math.min(res.left, adjustedCrop.left);
+                } else {
+                    res.right = Math.max(res.right, adjustedCrop.right);
+                }
                 // use getAdjustedCrop(parallax=true) to make sure we don't exceed MAX_PARALLAX
                 res = getAdjustedCrop(res, bitmapSize, displaySize, true, rtl, ADD);
             }
@@ -218,9 +212,9 @@
 
         // Case 5: if the device is a foldable, if we're looking for an unfolded orientation and
         // have the suggested crop of the relative folded orientation, reuse it by adding content.
-        int foldedOrientation = mWallpaperDisplayHelper.getFoldedOrientation(orientation);
+        int foldedOrientation = defaultDisplayInfo.getFoldedOrientation(orientation);
         suggestedCrop = suggestedCrops.get(foldedOrientation);
-        suggestedDisplaySize = defaultDisplaySizes.get(foldedOrientation);
+        suggestedDisplaySize = defaultDisplayInfo.defaultDisplaySizes.get(foldedOrientation);
         if (suggestedCrop != null) {
             // only keep the visible part (without parallax)
             Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
@@ -229,17 +223,19 @@
 
         // Case 6: for a foldable device, try to combine case 3 + case 4 or 5:
         // rotate, then fold or unfold
-        Point rotatedDisplaySize = defaultDisplaySizes.get(rotatedOrientation);
+        Point rotatedDisplaySize = defaultDisplayInfo.defaultDisplaySizes.get(rotatedOrientation);
         if (rotatedDisplaySize != null) {
-            int rotatedFolded = mWallpaperDisplayHelper.getFoldedOrientation(rotatedOrientation);
-            int rotateUnfolded = mWallpaperDisplayHelper.getUnfoldedOrientation(rotatedOrientation);
+            int rotatedFolded = defaultDisplayInfo.getFoldedOrientation(rotatedOrientation);
+            int rotateUnfolded = defaultDisplayInfo.getUnfoldedOrientation(rotatedOrientation);
             for (int suggestedOrientation : new int[]{rotatedFolded, rotateUnfolded}) {
                 suggestedCrop = suggestedCrops.get(suggestedOrientation);
                 if (suggestedCrop != null) {
-                    Rect rotatedCrop = getCrop(rotatedDisplaySize, bitmapSize, suggestedCrops, rtl);
+                    Rect rotatedCrop = getCrop(rotatedDisplaySize, defaultDisplayInfo, bitmapSize,
+                            suggestedCrops, rtl);
                     SparseArray<Rect> rotatedCropMap = new SparseArray<>();
                     rotatedCropMap.put(rotatedOrientation, rotatedCrop);
-                    return getCrop(displaySize, bitmapSize, rotatedCropMap, rtl);
+                    return getCrop(displaySize, defaultDisplayInfo, bitmapSize, rotatedCropMap,
+                            rtl);
                 }
             }
         }
@@ -248,8 +244,8 @@
         Slog.w(TAG, "Could not find a proper default crop for display: " + displaySize
                 + ", bitmap size: " + bitmapSize + ", suggested crops: " + suggestedCrops
                 + ", orientation: " + orientation + ", rtl: " + rtl
-                + ", defaultDisplaySizes: " + defaultDisplaySizes);
-        return getCrop(displaySize, bitmapSize, new SparseArray<>(), rtl);
+                + ", defaultDisplaySizes: " + defaultDisplayInfo.defaultDisplaySizes);
+        return getCrop(displaySize, defaultDisplayInfo, bitmapSize, new SparseArray<>(), rtl);
     }
 
     /**
@@ -445,7 +441,7 @@
             Rect suggestedCrop = suggestedCrops.get(orientation);
             if (suggestedCrop != null) {
                 adjustedSuggestedCrops.put(orientation,
-                        getCrop(displaySize, bitmapSize, suggestedCrops, rtl));
+                        getCrop(displaySize, mDefaultDisplayInfo, bitmapSize, suggestedCrops, rtl));
             }
         }
 
@@ -455,7 +451,8 @@
             int orientation = defaultDisplaySizes.keyAt(i);
             if (result.contains(orientation)) continue;
             Point displaySize = defaultDisplaySizes.valueAt(i);
-            Rect newCrop = getCrop(displaySize, bitmapSize, adjustedSuggestedCrops, rtl);
+            Rect newCrop = getCrop(displaySize, mDefaultDisplayInfo, bitmapSize,
+                    adjustedSuggestedCrops, rtl);
             result.put(orientation, newCrop);
         }
         return result;
@@ -859,10 +856,14 @@
             BitmapFactory.decodeFile(wallpaperFile.getAbsolutePath(), options);
             wallpaperImageSize.set(options.outWidth, options.outHeight);
         }
+        boolean isRtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
+                == View.LAYOUT_DIRECTION_RTL;
+        Rect croppedImageBound = getCrop(displaySize, mDefaultDisplayInfo, wallpaperImageSize,
+                getRelativeCropHints(wallpaperData), isRtl);
 
-        double maxDisplayToImageRatio = Math.max((double) displaySize.x / wallpaperImageSize.x,
-                (double) displaySize.y / wallpaperImageSize.y);
-        if (maxDisplayToImageRatio > 1.5) {
+        double maxDisplayToImageRatio = Math.max((double) displaySize.x / croppedImageBound.width(),
+                (double) displaySize.y / croppedImageBound.height());
+        if (maxDisplayToImageRatio > 1.3) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
index ba0262a..69f0ef7 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
@@ -542,9 +542,11 @@
                 // to support back compatibility in B&R, save the crops for one orientation in the
                 // legacy "cropLeft", "cropTop", "cropRight", "cropBottom" entries
                 int orientationToPutInLegacyCrop = wallpaper.mOrientationWhenSet;
-                if (mWallpaperDisplayHelper.isFoldable()) {
-                    int unfoldedOrientation = mWallpaperDisplayHelper
-                            .getUnfoldedOrientation(orientationToPutInLegacyCrop);
+                WallpaperDefaultDisplayInfo defaultDisplayInfo =
+                        mWallpaperDisplayHelper.getDefaultDisplayInfo();
+                if (defaultDisplayInfo.isFoldable) {
+                    int unfoldedOrientation = defaultDisplayInfo.getUnfoldedOrientation(
+                            orientationToPutInLegacyCrop);
                     if (unfoldedOrientation != ORIENTATION_UNKNOWN) {
                         orientationToPutInLegacyCrop = unfoldedOrientation;
                     }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDefaultDisplayInfo.java b/services/core/java/com/android/server/wallpaper/WallpaperDefaultDisplayInfo.java
new file mode 100644
index 0000000..dabe919
--- /dev/null
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDefaultDisplayInfo.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.wallpaper;
+
+import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
+import static android.app.WallpaperManager.getRotatedOrientation;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
+
+import android.app.WallpaperManager;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.SparseArray;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+
+/**  A data class for the default display attributes used in wallpaper related operations. */
+public final class WallpaperDefaultDisplayInfo {
+    /**
+     * A data class representing the screen orientations for a foldable device in the folded and
+     * unfolded states.
+     */
+    @VisibleForTesting
+    static final class FoldableOrientations {
+        @WallpaperManager.ScreenOrientation
+        public final int foldedOrientation;
+        @WallpaperManager.ScreenOrientation
+        public final int unfoldedOrientation;
+
+        FoldableOrientations(int foldedOrientation, int unfoldedOrientation) {
+            this.foldedOrientation = foldedOrientation;
+            this.unfoldedOrientation = unfoldedOrientation;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) return true;
+            if (!(other instanceof FoldableOrientations that)) return false;
+            return foldedOrientation == that.foldedOrientation
+                    && unfoldedOrientation == that.unfoldedOrientation;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(foldedOrientation, unfoldedOrientation);
+        }
+    }
+
+    public final SparseArray<Point> defaultDisplaySizes;
+    public final boolean isLargeScreen;
+    public final boolean isFoldable;
+    @VisibleForTesting
+    final List<FoldableOrientations> foldableOrientations;
+
+    public WallpaperDefaultDisplayInfo() {
+        this.defaultDisplaySizes = new SparseArray<>();
+        this.isLargeScreen = false;
+        this.isFoldable = false;
+        this.foldableOrientations = Collections.emptyList();
+    }
+
+    public WallpaperDefaultDisplayInfo(WindowManager windowManager, Resources resources) {
+        Set<WindowMetrics> metrics = windowManager.getPossibleMaximumWindowMetrics(DEFAULT_DISPLAY);
+        boolean isFoldable = resources.getIntArray(R.array.config_foldedDeviceStates).length > 0;
+        if (isFoldable) {
+            this.foldableOrientations = getFoldableOrientations(metrics);
+        } else {
+            this.foldableOrientations = Collections.emptyList();
+        }
+        this.defaultDisplaySizes = getDisplaySizes(metrics);
+        this.isLargeScreen = isLargeScreen(metrics);
+        this.isFoldable = isFoldable;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) return true;
+        if (!(other instanceof WallpaperDefaultDisplayInfo that)) return false;
+        return isLargeScreen == that.isLargeScreen && isFoldable == that.isFoldable
+                && defaultDisplaySizes.contentEquals(that.defaultDisplaySizes)
+                && Objects.equals(foldableOrientations, that.foldableOrientations);
+    }
+
+    @Override
+    public int hashCode() {
+        return 31 * Objects.hash(isLargeScreen, isFoldable, foldableOrientations)
+                + defaultDisplaySizes.contentHashCode();
+    }
+
+    /**
+     * Returns the folded orientation corresponds to the {@code unfoldedOrientation} found in
+     * {@link #foldableOrientations}. If not found, returns
+     * {@link WallpaperManager.ORIENTATION_UNKNOWN}.
+     */
+    public int getFoldedOrientation(int unfoldedOrientation) {
+        for (FoldableOrientations orientations : foldableOrientations) {
+            if (orientations.unfoldedOrientation == unfoldedOrientation) {
+                return orientations.foldedOrientation;
+            }
+        }
+        return ORIENTATION_UNKNOWN;
+    }
+
+    /**
+     * Returns the unfolded orientation corresponds to the {@code foldedOrientation} found in
+     * {@link #foldableOrientations}. If not found, returns
+     * {@link WallpaperManager.ORIENTATION_UNKNOWN}.
+     */
+    public int getUnfoldedOrientation(int foldedOrientation) {
+        for (FoldableOrientations orientations : foldableOrientations) {
+            if (orientations.foldedOrientation == foldedOrientation) {
+                return orientations.unfoldedOrientation;
+            }
+        }
+        return ORIENTATION_UNKNOWN;
+    }
+
+    private static SparseArray<Point> getDisplaySizes(Set<WindowMetrics> displayMetrics) {
+        SparseArray<Point> displaySizes = new SparseArray<>();
+        for (WindowMetrics metric : displayMetrics) {
+            Rect bounds = metric.getBounds();
+            Point displaySize = new Point(bounds.width(), bounds.height());
+            Point reversedDisplaySize = new Point(displaySize.y, displaySize.x);
+            for (Point point : List.of(displaySize, reversedDisplaySize)) {
+                int orientation = WallpaperManager.getOrientation(point);
+                // don't add an entry if there is already a larger display of the same orientation
+                Point display = displaySizes.get(orientation);
+                if (display == null || display.x * display.y < point.x * point.y) {
+                    displaySizes.put(orientation, point);
+                }
+            }
+        }
+        return displaySizes;
+    }
+
+    private static boolean isLargeScreen(Set<WindowMetrics> displayMetrics) {
+        float smallestWidth = Float.MAX_VALUE;
+        for (WindowMetrics metric : displayMetrics) {
+            Rect bounds = metric.getBounds();
+            smallestWidth = Math.min(smallestWidth, bounds.width() / metric.getDensity());
+        }
+        return smallestWidth >= LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
+    }
+
+    /**
+     * Determines all potential foldable orientations, populating {@code
+     * outFoldableOrientationPairs} with pairs of (folded orientation, unfolded orientation). If
+     * {@code defaultDisplayMetrics} isn't for foldable, {@code outFoldableOrientationPairs} will
+     * not be populated.
+     */
+    private static List<FoldableOrientations> getFoldableOrientations(
+            Set<WindowMetrics> defaultDisplayMetrics) {
+        if (defaultDisplayMetrics.size() != 2) {
+            return Collections.emptyList();
+        }
+        List<FoldableOrientations> foldableOrientations = new ArrayList<>();
+        float surface = 0;
+        int firstOrientation = -1;
+        for (WindowMetrics metric : defaultDisplayMetrics) {
+            Rect bounds = metric.getBounds();
+            Point displaySize = new Point(bounds.width(), bounds.height());
+
+            int orientation = WallpaperManager.getOrientation(displaySize);
+            float newSurface = displaySize.x * displaySize.y
+                    / (metric.getDensity() * metric.getDensity());
+            if (surface <= 0) {
+                surface = newSurface;
+                firstOrientation = orientation;
+            } else {
+                FoldableOrientations orientations = (newSurface > surface)
+                        ? new FoldableOrientations(firstOrientation, orientation)
+                        : new FoldableOrientations(orientation, firstOrientation);
+                FoldableOrientations rotatedOrientations = new FoldableOrientations(
+                        getRotatedOrientation(orientations.foldedOrientation),
+                        getRotatedOrientation(orientations.unfoldedOrientation));
+                foldableOrientations.add(orientations);
+                foldableOrientations.add(rotatedOrientations);
+            }
+        }
+        return Collections.unmodifiableList(foldableOrientations);
+    }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
index 3636f5a..bff5fc9 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
@@ -16,31 +16,25 @@
 
 package com.android.server.wallpaper;
 
-import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
-import static android.app.WallpaperManager.getRotatedOrientation;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.window.flags.Flags.multiCrop;
 
 import android.app.WallpaperManager;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Binder;
 import android.os.Debug;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.WindowManager;
-import android.view.WindowMetrics;
 
 import com.android.server.wm.WindowManagerInternal;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
 import java.util.function.Consumer;
 
 /**
@@ -59,65 +53,25 @@
     }
 
     private static final String TAG = WallpaperDisplayHelper.class.getSimpleName();
-    private static final float LARGE_SCREEN_MIN_DP = 600f;
 
     private final SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
     private final DisplayManager mDisplayManager;
     private final WindowManagerInternal mWindowManagerInternal;
-    private final SparseArray<Point> mDefaultDisplaySizes = new SparseArray<>();
 
-    // related orientations pairs for foldable (folded orientation, unfolded orientation)
-    private final List<Pair<Integer, Integer>> mFoldableOrientationPairs = new ArrayList<>();
-
-    private final boolean mIsFoldable;
-    private boolean mIsLargeScreen = false;
+    private final WallpaperDefaultDisplayInfo mDefaultDisplayInfo;
 
     WallpaperDisplayHelper(
             DisplayManager displayManager,
             WindowManager windowManager,
             WindowManagerInternal windowManagerInternal,
-            boolean isFoldable) {
+            Resources resources) {
         mDisplayManager = displayManager;
         mWindowManagerInternal = windowManagerInternal;
-        mIsFoldable = isFoldable;
-        if (!multiCrop()) return;
-        Set<WindowMetrics> metrics = windowManager.getPossibleMaximumWindowMetrics(DEFAULT_DISPLAY);
-        boolean populateOrientationPairs = isFoldable && metrics.size() == 2;
-        float surface = 0;
-        int firstOrientation = -1;
-        for (WindowMetrics metric: metrics) {
-            Rect bounds = metric.getBounds();
-            Point displaySize = new Point(bounds.width(), bounds.height());
-            Point reversedDisplaySize = new Point(displaySize.y, displaySize.x);
-            for (Point point : List.of(displaySize, reversedDisplaySize)) {
-                int orientation = WallpaperManager.getOrientation(point);
-                // don't add an entry if there is already a larger display of the same orientation
-                Point display = mDefaultDisplaySizes.get(orientation);
-                if (display == null || display.x * display.y < point.x * point.y) {
-                    mDefaultDisplaySizes.put(orientation, point);
-                }
-            }
-
-            mIsLargeScreen |= (displaySize.x / metric.getDensity() >= LARGE_SCREEN_MIN_DP);
-
-            if (populateOrientationPairs) {
-                int orientation = WallpaperManager.getOrientation(displaySize);
-                float newSurface = displaySize.x * displaySize.y
-                        / (metric.getDensity() * metric.getDensity());
-                if (surface <= 0) {
-                    surface = newSurface;
-                    firstOrientation = orientation;
-                } else {
-                    Pair<Integer, Integer> pair = (newSurface > surface)
-                            ? new Pair<>(firstOrientation, orientation)
-                            : new Pair<>(orientation, firstOrientation);
-                    Pair<Integer, Integer> rotatedPair = new Pair<>(
-                            getRotatedOrientation(pair.first), getRotatedOrientation(pair.second));
-                    mFoldableOrientationPairs.add(pair);
-                    mFoldableOrientationPairs.add(rotatedPair);
-                }
-            }
+        if (!multiCrop()) {
+            mDefaultDisplayInfo = new WallpaperDefaultDisplayInfo();
+            return;
         }
+        mDefaultDisplayInfo = new WallpaperDefaultDisplayInfo(windowManager, resources);
     }
 
     DisplayData getDisplayDataOrCreate(int displayId) {
@@ -203,51 +157,21 @@
     }
 
     SparseArray<Point> getDefaultDisplaySizes() {
-        return mDefaultDisplaySizes;
+        return mDefaultDisplayInfo.defaultDisplaySizes;
     }
 
     /** Return the number of pixel of the largest dimension of the default display */
     int getDefaultDisplayLargestDimension() {
+        SparseArray<Point> defaultDisplaySizes = mDefaultDisplayInfo.defaultDisplaySizes;
         int result = -1;
-        for (int i = 0; i < mDefaultDisplaySizes.size(); i++) {
-            Point size = mDefaultDisplaySizes.valueAt(i);
+        for (int i = 0; i < defaultDisplaySizes.size(); i++) {
+            Point size = defaultDisplaySizes.valueAt(i);
             result = Math.max(result, Math.max(size.x, size.y));
         }
         return result;
     }
 
-    boolean isFoldable() {
-        return mIsFoldable;
-    }
-
-    /**
-     * Return true if any of the screens of the default display is considered large (DP >= 600)
-     */
-    boolean isLargeScreen() {
-        return mIsLargeScreen;
-    }
-
-    /**
-     * If a given orientation corresponds to an unfolded orientation on foldable, return the
-     * corresponding folded orientation. Otherwise, return UNKNOWN. Always return UNKNOWN if the
-     * device is not a foldable.
-     */
-    int getFoldedOrientation(int orientation) {
-        for (Pair<Integer, Integer> pair : mFoldableOrientationPairs) {
-            if (pair.second.equals(orientation)) return pair.first;
-        }
-        return ORIENTATION_UNKNOWN;
-    }
-
-    /**
-     * If a given orientation corresponds to a folded orientation on foldable, return the
-     * corresponding unfolded orientation. Otherwise, return UNKNOWN. Always return UNKNOWN if the
-     * device is not a foldable.
-     */
-    int getUnfoldedOrientation(int orientation) {
-        for (Pair<Integer, Integer> pair : mFoldableOrientationPairs) {
-            if (pair.first.equals(orientation)) return pair.second;
-        }
-        return ORIENTATION_UNKNOWN;
+    public WallpaperDefaultDisplayInfo getDefaultDisplayInfo() {
+        return mDefaultDisplayInfo;
     }
 }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index bac7326..e7da33d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1666,12 +1666,9 @@
         DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
         displayManager.registerDisplayListener(mDisplayListener, null /* handler */);
         WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        boolean isFoldable = mContext.getResources()
-                .getIntArray(R.array.config_foldedDeviceStates).length > 0;
         mWallpaperDisplayHelper = new WallpaperDisplayHelper(
-                displayManager, windowManager, mWindowManagerInternal, isFoldable);
+                displayManager, windowManager, mWindowManagerInternal, mContext.getResources());
         mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
-        mWindowManagerInternal.setWallpaperCropUtils(mWallpaperCropper::getCrop);
         mActivityManager = mContext.getSystemService(ActivityManager.class);
 
         if (mContext.getResources().getBoolean(
@@ -2510,9 +2507,11 @@
             List<Rect> result = new ArrayList<>();
             boolean rtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
                     == View.LAYOUT_DIRECTION_RTL;
+            WallpaperDefaultDisplayInfo defaultDisplayInfo =
+                    mWallpaperDisplayHelper.getDefaultDisplayInfo();
             for (Point displaySize : displaySizes) {
-                result.add(mWallpaperCropper.getCrop(
-                        displaySize, croppedBitmapSize, adjustedRelativeSuggestedCrops, rtl));
+                result.add(WallpaperCropper.getCrop(displaySize, defaultDisplayInfo,
+                        croppedBitmapSize, adjustedRelativeSuggestedCrops, rtl));
             }
             if (originalBitmap) result = WallpaperCropper.getOriginalCropHints(wallpaper, result);
             return result;
@@ -2548,8 +2547,11 @@
         List<Rect> result = new ArrayList<>();
         boolean rtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
                 == View.LAYOUT_DIRECTION_RTL;
+        WallpaperDefaultDisplayInfo defaultDisplayInfo =
+                mWallpaperDisplayHelper.getDefaultDisplayInfo();
         for (Point displaySize : displaySizes) {
-            result.add(mWallpaperCropper.getCrop(displaySize, bitmapSize, defaultCrops, rtl));
+            result.add(WallpaperCropper.getCrop(displaySize, defaultDisplayInfo, bitmapSize,
+                    defaultCrops, rtl));
         }
         return result;
     }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 1299a4d..f243d4f 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -153,7 +153,7 @@
             final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
             if (dc != null) {
                 final Display display = dc.getDisplay();
-                if (display != null && display.getType() != Display.TYPE_OVERLAY) {
+                if (display != null) {
                     final DisplayMagnifier magnifier = new DisplayMagnifier(
                             mService, dc, display, callbacks);
                     magnifier.notifyImeWindowVisibilityChanged(
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index a941838..e2b47b9 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -112,7 +112,6 @@
 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;
@@ -807,14 +806,8 @@
             }
             final Task otherTask = otherInfo.mLastLaunchedActivity.getTask();
             // The adjacent task is the split root in which activities are started
-            final boolean isDescendantOfAdjacent;
-            if (Flags.allowMultipleAdjacentTaskFragments()) {
-                isDescendantOfAdjacent = launchedSplitRootTask.forOtherAdjacentTasks(
-                        otherTask::isDescendantOf);
-            } else {
-                isDescendantOfAdjacent = otherTask.isDescendantOf(
-                        launchedSplitRootTask.getAdjacentTask());
-            }
+            final boolean isDescendantOfAdjacent = launchedSplitRootTask.forOtherAdjacentTasks(
+                    otherTask::isDescendantOf);
             if (isDescendantOfAdjacent) {
                 if (DEBUG_METRICS) {
                     Slog.i(TAG, "Found adjacent tasks t1=" + launchedActivityTask.mTaskId
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3cd4db7..b76b231 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -625,7 +625,7 @@
     @VisibleForTesting
     final TaskFragment.ConfigOverrideHint mResolveConfigHint;
 
-    private final boolean mOptOutEdgeToEdge;
+    final boolean mOptOutEdgeToEdge;
 
     private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig;
 
@@ -1716,6 +1716,7 @@
         }
 
         mAppCompatController.getLetterboxPolicy().onMovedToDisplay(mDisplayContent.getDisplayId());
+        mAppCompatController.getDisplayCompatModePolicy().onMovedToDisplay();
     }
 
     void layoutLetterboxIfNeeded(WindowState winHint) {
@@ -3801,19 +3802,10 @@
         final TaskFragment taskFragment = getTaskFragment();
         if (next != null && taskFragment != null && taskFragment.isEmbedded()) {
             final TaskFragment organized = taskFragment.getOrganizedTaskFragment();
-            if (Flags.allowMultipleAdjacentTaskFragments()) {
-                delayRemoval = organized != null
-                        && organized.topRunningActivity() == null
-                        && organized.isDelayLastActivityRemoval()
-                        && organized.forOtherAdjacentTaskFragments(next::isDescendantOf);
-            } else {
-                final TaskFragment adjacent =
-                        organized != null ? organized.getAdjacentTaskFragment() : null;
-                if (adjacent != null && next.isDescendantOf(adjacent)
-                        && organized.topRunningActivity() == null) {
-                    delayRemoval = organized.isDelayLastActivityRemoval();
-                }
-            }
+            delayRemoval = organized != null
+                    && organized.topRunningActivity() == null
+                    && organized.isDelayLastActivityRemoval()
+                    && organized.forOtherAdjacentTaskFragments(next::isDescendantOf);
         }
 
         // isNextNotYetVisible is to check if the next activity is invisible, or it has been
@@ -4787,11 +4779,6 @@
         }
 
         // Make sure the embedded adjacent can also be shown.
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            final ActivityRecord adjacentActivity = taskFragment.getAdjacentTaskFragment()
-                    .getTopNonFinishingActivity();
-            return canShowWhenLocked(adjacentActivity);
-        }
         final boolean hasAdjacentNotAllowToShow = taskFragment.forOtherAdjacentTaskFragments(
                 adjacentTF -> !canShowWhenLocked(adjacentTF.getTopNonFinishingActivity()));
         return !hasAdjacentNotAllowToShow;
@@ -5596,6 +5583,13 @@
         // called for updating snapshot states.
         if (!fromTransition) {
             mWmService.mSnapshotController.notifyAppVisibilityChanged(this, visible);
+            if (visible) {
+                // In case the activity becomes visible without transition, the client still expects
+                // to receive Activity#onEnterAnimationComplete.
+                mEnteringAnimation = true;
+                mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
+                        token);
+            }
         }
     }
 
@@ -5603,6 +5597,14 @@
         commitVisibility(visible, performLayout, false /* fromTransition */);
     }
 
+    /**
+     * Sets whether safe region bounds are needed for the Activity. This is called from
+     * {@link ActivityStarter} after the source record is created.
+     */
+    void setNeedsSafeRegionBounds(boolean needsSafeRegionBounds) {
+        mAppCompatController.getSafeRegionPolicy().setNeedsSafeRegionBounds(needsSafeRegionBounds);
+    }
+
     /** Updates draw state and shows drawn windows. */
     void commitFinishDrawing(SurfaceControl.Transaction t) {
         boolean committed = false;
@@ -7766,9 +7768,11 @@
         final AppCompatAspectRatioPolicy aspectRatioPolicy =
                 mAppCompatController.getAspectRatioPolicy();
         aspectRatioPolicy.reset();
+        final AppCompatSafeRegionPolicy safeRegionPolicy =
+                mAppCompatController.getSafeRegionPolicy();
         mAppCompatController.getLetterboxPolicy().resetFixedOrientationLetterboxEligibility();
         mResolveConfigHint.resolveTmpOverrides(mDisplayContent, newParentConfiguration,
-                isFixedRotationTransforming());
+                isFixedRotationTransforming(), safeRegionPolicy.getLatestSafeRegionBounds());
 
         // Can't use resolvedConfig.windowConfiguration.getWindowingMode() because it can be
         // different from windowing mode of the task (PiP) during transition from fullscreen to PiP
@@ -7814,6 +7818,13 @@
             }
         }
 
+        // If activity can be letterboxed due to a safe region only, use the safe region bounds
+        // as the resolved bounds. We ignore cases where the letterboxing can happen due to other
+        // app compat conditions and a safe region since the safe region app compat is sandboxed
+        // earlier in TaskFragment.ConfigOverrideHint.resolveTmpOverrides.
+        mAppCompatController.getSafeRegionPolicy().resolveSafeRegionBoundsConfigurationIfNeeded(
+                resolvedConfig, newParentConfiguration);
+
         if (isFixedOrientationLetterboxAllowed
                 || scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()
                 // In fullscreen, can be letterboxed for aspect ratio.
@@ -7993,7 +8004,7 @@
                 mAppCompatController.getSizeCompatModePolicy();
         final Rect screenResolvedBounds = scmPolicy.replaceResolvedBoundsIfNeeded(resolvedBounds);
         final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
-        final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
+        final Rect parentBounds = mResolveConfigHint.mParentBoundsOverride;
         final float screenResolvedBoundsWidth = screenResolvedBounds.width();
         final float parentAppBoundsWidth = parentAppBounds.width();
         final boolean isImmersiveMode = isImmersiveMode(parentBounds);
@@ -8180,7 +8191,7 @@
      * in this method.
      */
     private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) {
-        final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+        final Rect parentBounds = mResolveConfigHint.mParentBoundsOverride;
         final Rect stableBounds = new Rect();
         final Rect outNonDecorBounds = mTmpBounds;
         // If orientation is respected when insets are applied, then stableBounds will be empty.
@@ -8980,6 +8991,7 @@
         // Reset the existing override configuration so it can be updated according to the latest
         // configuration.
         mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
+        mAppCompatController.getDisplayCompatModePolicy().onProcessRestarted();
 
         if (!attachedToProcess()) {
             return;
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 2162834..0f1939b 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -34,7 +34,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
-import com.android.window.flags.Flags;
 
 import java.io.File;
 import java.io.PrintWriter;
@@ -107,8 +106,7 @@
                 && !ActivityManager.isLowRamDeviceStatic(); // Don't support Android Go
         setSnapshotEnabled(snapshotEnabled);
         mSnapshotPersistQueue = persistQueue;
-        mPersistInfoProvider = createPersistInfoProvider(service,
-                Environment::getDataSystemCeDirectory);
+        mPersistInfoProvider = createPersistInfoProvider(service);
         mPersister = new TaskSnapshotPersister(
                 persistQueue,
                 mPersistInfoProvider,
@@ -117,6 +115,11 @@
         initialize(new ActivitySnapshotCache());
     }
 
+    @VisibleForTesting
+    PersistInfoProvider createPersistInfoProvider(WindowManagerService service) {
+        return createPersistInfoProvider(service, Environment::getDataSystemCeDirectory);
+    }
+
     @Override
     protected float initSnapshotScale() {
         final float config = mService.mContext.getResources().getFloat(
@@ -528,26 +531,6 @@
         final int currentIndex = currTF.asTask() != null
                 ? currentTask.mChildren.indexOf(currentActivity)
                 : currentTask.mChildren.indexOf(currTF);
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            final int prevAdjacentIndex = currentTask.mChildren.indexOf(
-                    prevTF.getAdjacentTaskFragment());
-            if (prevAdjacentIndex > currentIndex) {
-                // PrevAdjacentTF already above currentActivity
-                return;
-            }
-            // Add both the one below, and its adjacent.
-            if (!inTransition || isInParticipant(initPrev, mTmpTransitionParticipants)) {
-                result.add(initPrev);
-            }
-            final ActivityRecord prevAdjacentActivity = prevTF.getAdjacentTaskFragment()
-                    .getTopMostActivity();
-            if (prevAdjacentActivity != null && (!inTransition
-                    || isInParticipant(prevAdjacentActivity, mTmpTransitionParticipants))) {
-                result.add(prevAdjacentActivity);
-            }
-            return;
-        }
-
         final boolean hasAdjacentAboveCurrent = prevTF.forOtherAdjacentTaskFragments(
                 prevAdjacentTF -> {
                     final int prevAdjacentIndex = currentTask.mChildren.indexOf(prevAdjacentTF);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 3f24da9..51025d2 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -60,6 +60,7 @@
 import com.android.server.wm.ActivityStarter.Factory;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -97,6 +98,9 @@
     /** Whether an {@link ActivityStarter} is currently executing (starting an Activity). */
     private boolean mInExecution = false;
 
+    /** The {@link TaskDisplayArea}s that are currently starting home activity. */
+    private ArrayList<TaskDisplayArea> mHomeLaunchingTaskDisplayAreas = new ArrayList<>();
+
     /**
      * TODO(b/64750076): Capture information necessary for dump and
      * {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object
@@ -162,6 +166,11 @@
 
     void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason,
             TaskDisplayArea taskDisplayArea) {
+        if (mHomeLaunchingTaskDisplayAreas.contains(taskDisplayArea)) {
+            Slog.e(TAG, "Abort starting home on " + taskDisplayArea + " recursively.");
+            return;
+        }
+
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
         if (!ActivityRecord.isResolverActivity(aInfo.name)) {
@@ -186,13 +195,18 @@
             mSupervisor.endDeferResume();
         }
 
-        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
-                .setOutActivity(tmpOutRecord)
-                .setCallingUid(0)
-                .setActivityInfo(aInfo)
-                .setActivityOptions(options.toBundle(),
-                        Binder.getCallingPid(), Binder.getCallingUid())
-                .execute();
+        try {
+            mHomeLaunchingTaskDisplayAreas.add(taskDisplayArea);
+            mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
+                    .setOutActivity(tmpOutRecord)
+                    .setCallingUid(0)
+                    .setActivityInfo(aInfo)
+                    .setActivityOptions(options.toBundle(),
+                            Binder.getCallingPid(), Binder.getCallingUid())
+                    .execute();
+        } finally {
+            mHomeLaunchingTaskDisplayAreas.remove(taskDisplayArea);
+        }
         mLastHomeActivityStartRecord = tmpOutRecord[0];
         if (rootHomeTask.mInResumeTopActivity) {
             // If we are in resume section already, home activity will be initialized, but not
@@ -479,9 +493,9 @@
                             }
                         } catch (SecurityException securityException) {
                             ActivityStarter.logAndThrowExceptionForIntentRedirect(mService.mContext,
-                                    "Creator URI Grant Caused Exception.", intent, creatorUid,
-                                    creatorPackage, filterCallingUid, callingPackage,
-                                    securityException);
+                                    ActivityStarter.INTENT_REDIRECT_EXCEPTION_GRANT_URI_PERMISSION,
+                                    intent, creatorUid, creatorPackage, filterCallingUid,
+                                    callingPackage, securityException);
                         }
                     }
                     if ((aInfo.applicationInfo.privateFlags
@@ -720,6 +734,12 @@
             }
         }
 
+        if (!mHomeLaunchingTaskDisplayAreas.isEmpty()) {
+            dumped = true;
+            pw.print(prefix);
+            pw.println("mHomeLaunchingTaskDisplayAreas:" + mHomeLaunchingTaskDisplayAreas);
+        }
+
         if (!dumped) {
             pw.print(prefix);
             pw.println("(nothing)");
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 233f913..92f51be 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.internal.util.FrameworkStatsLog.INTENT_REDIRECT_BLOCKED;
 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;
@@ -140,6 +141,7 @@
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.protolog.ProtoLog;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.UiThread;
 import com.android.server.am.ActivityManagerService.IntentCreatorToken;
 import com.android.server.am.PendingIntentRecord;
@@ -623,7 +625,7 @@
             if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN)
                     != 0) {
                 logAndThrowExceptionForIntentRedirect(supervisor.mService.mContext,
-                        "Unparceled intent does not have a creator token set.", intent,
+                        ActivityStarter.INTENT_REDIRECT_EXCEPTION_MISSING_OR_INVALID_TOKEN, intent,
                         intentCreatorUid, intentCreatorPackage, resolvedCallingUid,
                         resolvedCallingPackage, null);
             }
@@ -659,9 +661,9 @@
                             }
                         } catch (SecurityException securityException) {
                             logAndThrowExceptionForIntentRedirect(supervisor.mService.mContext,
-                                    "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
-                                    intentCreatorPackage, resolvedCallingUid,
-                                    resolvedCallingPackage, securityException);
+                                    ActivityStarter.INTENT_REDIRECT_EXCEPTION_GRANT_URI_PERMISSION,
+                                    intent, intentCreatorUid, intentCreatorPackage,
+                                    resolvedCallingUid, resolvedCallingPackage, securityException);
                         }
                     }
                 } else {
@@ -683,9 +685,9 @@
                             }
                         } catch (SecurityException securityException) {
                             logAndThrowExceptionForIntentRedirect(supervisor.mService.mContext,
-                                    "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
-                                    intentCreatorPackage, resolvedCallingUid,
-                                    resolvedCallingPackage, securityException);
+                                    ActivityStarter.INTENT_REDIRECT_EXCEPTION_GRANT_URI_PERMISSION,
+                                    intent, intentCreatorUid, intentCreatorPackage,
+                                    resolvedCallingUid, resolvedCallingPackage, securityException);
                         }
                     }
                 }
@@ -1109,8 +1111,11 @@
             if (sourceRecord != null) {
                 if (requestCode >= 0 && !sourceRecord.finishing) {
                     resultRecord = sourceRecord;
+                    request.logMessage.append(" (rr=");
+                } else {
+                    request.logMessage.append(" (sr=");
                 }
-                request.logMessage.append(" (sr=" + System.identityHashCode(sourceRecord) + ")");
+                request.logMessage.append(System.identityHashCode(sourceRecord) + ")");
             }
         }
 
@@ -1261,27 +1266,27 @@
                         request.ignoreTargetSecurity, inTask != null, null, resultRecord,
                         resultRootTask)) {
                     abort = logAndAbortForIntentRedirect(mService.mContext,
-                            "Creator checkStartAnyActivityPermission Caused abortion.",
+                            ActivityStarter.INTENT_REDIRECT_ABORT_START_ANY_ACTIVITY_PERMISSION,
                             intent, intentCreatorUid, intentCreatorPackage, callingUid,
                             callingPackage);
                 }
             } catch (SecurityException e) {
                 logAndThrowExceptionForIntentRedirect(mService.mContext,
-                        "Creator checkStartAnyActivityPermission Caused Exception.",
+                        ActivityStarter.INTENT_REDIRECT_EXCEPTION_START_ANY_ACTIVITY_PERMISSION,
                         intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage,
                         e);
             }
             if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid,
                     0, resolvedType, aInfo.applicationInfo)) {
                 abort = logAndAbortForIntentRedirect(mService.mContext,
-                        "Creator IntentFirewall.checkStartActivity Caused abortion.",
+                        ActivityStarter.INTENT_REDIRECT_ABORT_INTENT_FIREWALL_START_ACTIVITY,
                         intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
             }
 
             if (!mService.getPermissionPolicyInternal().checkStartActivity(intent,
                     intentCreatorUid, intentCreatorPackage)) {
                 abort = logAndAbortForIntentRedirect(mService.mContext,
-                        "Creator PermissionPolicyService.checkStartActivity Caused abortion.",
+                        ActivityStarter.INTENT_REDIRECT_ABORT_PERMISSION_POLICY_START_ACTIVITY,
                         intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
             }
         }
@@ -2198,6 +2203,9 @@
                 ? mLaunchParams.mPreferredTaskDisplayArea
                 : mRootWindowContainer.getDefaultTaskDisplayArea();
         mPreferredWindowingMode = mLaunchParams.mWindowingMode;
+        if (mLaunchParams.mNeedsSafeRegionBounds != null) {
+            r.setNeedsSafeRegionBounds(mLaunchParams.mNeedsSafeRegionBounds);
+        }
     }
 
     private TaskDisplayArea computeSuggestedLaunchDisplayArea(
@@ -3626,13 +3634,41 @@
         pw.println(mInTaskFragment);
     }
 
+    /**
+     * Error codes for intent redirect.
+     *
+     * @hide
+     */
+    @IntDef(prefix = {"INTENT_REDIRECT_"}, value = {
+            INTENT_REDIRECT_EXCEPTION_MISSING_OR_INVALID_TOKEN,
+            INTENT_REDIRECT_EXCEPTION_GRANT_URI_PERMISSION,
+            INTENT_REDIRECT_EXCEPTION_START_ANY_ACTIVITY_PERMISSION,
+            INTENT_REDIRECT_ABORT_START_ANY_ACTIVITY_PERMISSION,
+            INTENT_REDIRECT_ABORT_INTENT_FIREWALL_START_ACTIVITY,
+            INTENT_REDIRECT_ABORT_PERMISSION_POLICY_START_ACTIVITY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface IntentRedirectErrorCode {
+    }
+
+    /**
+     * Error codes for intent redirect issues
+     */
+    static final int INTENT_REDIRECT_EXCEPTION_MISSING_OR_INVALID_TOKEN = 1;
+    static final int INTENT_REDIRECT_EXCEPTION_GRANT_URI_PERMISSION = 2;
+    static final int INTENT_REDIRECT_EXCEPTION_START_ANY_ACTIVITY_PERMISSION = 3;
+    static final int INTENT_REDIRECT_ABORT_START_ANY_ACTIVITY_PERMISSION = 4;
+    static final int INTENT_REDIRECT_ABORT_INTENT_FIREWALL_START_ACTIVITY = 5;
+    static final int INTENT_REDIRECT_ABORT_PERMISSION_POLICY_START_ACTIVITY = 6;
+
     static void logAndThrowExceptionForIntentRedirect(@NonNull Context context,
-            @NonNull String message, @NonNull Intent intent, int intentCreatorUid,
+            @IntentRedirectErrorCode int errorCode, @NonNull Intent intent, int intentCreatorUid,
             @Nullable String intentCreatorPackage, int callingUid, @Nullable String callingPackage,
             @Nullable SecurityException originalException) {
-        String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
+        String msg = getIntentRedirectPreventedLogMessage(errorCode, intent, intentCreatorUid,
                 intentCreatorPackage, callingUid, callingPackage);
         Slog.wtf(TAG, msg);
+        FrameworkStatsLog.write(INTENT_REDIRECT_BLOCKED, intentCreatorUid, callingUid, errorCode);
         if (preventIntentRedirectShowToast()) {
             UiThread.getHandler().post(
                     () -> Toast.makeText(context,
@@ -3646,12 +3682,13 @@
     }
 
     private static boolean logAndAbortForIntentRedirect(@NonNull Context context,
-            @NonNull String message, @NonNull Intent intent, int intentCreatorUid,
+            @IntentRedirectErrorCode int errorCode, @NonNull Intent intent, int intentCreatorUid,
             @Nullable String intentCreatorPackage, int callingUid,
             @Nullable String callingPackage) {
-        String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
+        String msg = getIntentRedirectPreventedLogMessage(errorCode, intent, intentCreatorUid,
                 intentCreatorPackage, callingUid, callingPackage);
         Slog.wtf(TAG, msg);
+        FrameworkStatsLog.write(INTENT_REDIRECT_BLOCKED, intentCreatorUid, callingUid, errorCode);
         if (preventIntentRedirectShowToast()) {
             UiThread.getHandler().post(
                     () -> Toast.makeText(context,
@@ -3662,11 +3699,38 @@
                 ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid);
     }
 
-    private static String getIntentRedirectPreventedLogMessage(@NonNull String message,
+    private static String getIntentRedirectPreventedLogMessage(
+            @IntentRedirectErrorCode int errorCode,
             @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
             int callingUid, @Nullable String callingPackage) {
+        String message = getIntentRedirectErrorMessageFromCode(errorCode);
         return "[IntentRedirect Hardening] " + message + " intentCreatorUid: " + intentCreatorUid
                 + "; intentCreatorPackage: " + intentCreatorPackage + "; callingUid: " + callingUid
                 + "; callingPackage: " + callingPackage + "; intent: " + intent;
     }
+
+    private static String getIntentRedirectErrorMessageFromCode(
+            @IntentRedirectErrorCode int errorCode) {
+        return switch (errorCode) {
+            case INTENT_REDIRECT_EXCEPTION_MISSING_OR_INVALID_TOKEN ->
+                    "INTENT_REDIRECT_EXCEPTION_MISSING_OR_INVALID_TOKEN"
+                        + " (Unparceled intent does not have a creator token set, throw exception.)";
+            case INTENT_REDIRECT_EXCEPTION_GRANT_URI_PERMISSION ->
+                    "INTENT_REDIRECT_EXCEPTION_GRANT_URI_PERMISSION"
+                            + " (Creator URI permission grant throw exception.)";
+            case INTENT_REDIRECT_EXCEPTION_START_ANY_ACTIVITY_PERMISSION ->
+                    "INTENT_REDIRECT_ABORT_START_ANY_ACTIVITY_PERMISSION"
+                            + " (Creator checkStartAnyActivityPermission, throw exception)";
+            case INTENT_REDIRECT_ABORT_START_ANY_ACTIVITY_PERMISSION ->
+                    "INTENT_REDIRECT_ABORT_START_ANY_ACTIVITY_PERMISSION"
+                            + " (Creator checkStartAnyActivityPermission, abort)";
+            case INTENT_REDIRECT_ABORT_INTENT_FIREWALL_START_ACTIVITY ->
+                    "INTENT_REDIRECT_ABORT_INTENT_FIREWALL_START_ACTIVITY"
+                            + " (Creator IntentFirewall.checkStartActivity, abort)";
+            case INTENT_REDIRECT_ABORT_PERMISSION_POLICY_START_ACTIVITY ->
+                    "INTENT_REDIRECT_ABORT_PERMISSION_POLICY_START_ACTIVITY"
+                            + " (Creator PermissionPolicyService.checkStartActivity, abort)";
+            default -> "Unknown error code: " + errorCode;
+        };
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index a7f2153..0e14f83 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -36,6 +36,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.ACTION_VIEW;
@@ -164,7 +165,6 @@
 import com.android.server.pm.SaferIntentUtils;
 import com.android.server.utils.Slogf;
 import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
-import com.android.window.flags.Flags;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -1635,16 +1635,19 @@
         }
     }
 
-    private void moveHomeRootTaskToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea,
+    @VisibleForTesting
+    void moveHomeRootTaskToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea,
             String reason) {
         final Task focusedRootTask = taskDisplayArea.getFocusedRootTask();
 
         if ((taskDisplayArea.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                 && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
-                || (focusedRootTask != null && focusedRootTask.isActivityTypeRecents())) {
+                || (focusedRootTask != null && focusedRootTask.isActivityTypeRecents()
+                && focusedRootTask.getWindowingMode() != WINDOWING_MODE_MULTI_WINDOW)) {
             // We move root home task to front when we are on a fullscreen display area and
             // caller has requested the home activity to move with it. Or the previous root task
-            // is recents.
+            // is recents and we are not on multi-window mode.
+
             taskDisplayArea.moveHomeRootTaskToFront(reason);
         }
     }
@@ -2647,9 +2650,13 @@
      */
     void endDeferResume() {
         mDeferResumeCount--;
-        if (readyToResume() && mLastReportedTopResumedActivity != null
-                && mTopResumedActivity != mLastReportedTopResumedActivity) {
-            scheduleTopResumedActivityStateLossIfNeeded();
+        if (readyToResume()) {
+            if (mLastReportedTopResumedActivity != null
+                    && mTopResumedActivity != mLastReportedTopResumedActivity) {
+                scheduleTopResumedActivityStateLossIfNeeded();
+            } else if (mLastReportedTopResumedActivity == null) {
+                scheduleTopResumedActivityStateIfNeeded();
+            }
         }
     }
 
@@ -2831,6 +2838,13 @@
                             "startActivityFromRecents: Task " + taskId + " not found.");
                 }
 
+
+                if (task.getRootTask() != null
+                        && task.getRootTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+                    // Don't move home forward if task is in multi window mode
+                    moveHomeTaskForward = false;
+                }
+
                 if (moveHomeTaskForward) {
                     // We always want to return to the home activity instead of the recents
                     // activity from whatever is started from the recents activity, so move
@@ -2991,17 +3005,9 @@
 
                 if (child.asTaskFragment() != null
                         && child.asTaskFragment().hasAdjacentTaskFragment()) {
-                    final boolean isAnyTranslucent;
-                    if (Flags.allowMultipleAdjacentTaskFragments()) {
-                        final TaskFragment.AdjacentSet set =
-                                child.asTaskFragment().getAdjacentTaskFragments();
-                        isAnyTranslucent = set.forAllTaskFragments(
-                                tf -> !isOpaque(tf), null);
-                    } else {
-                        final TaskFragment adjacent = child.asTaskFragment()
-                                .getAdjacentTaskFragment();
-                        isAnyTranslucent = !isOpaque(child) || !isOpaque(adjacent);
-                    }
+                    final boolean isAnyTranslucent = !isOpaque(child)
+                            || child.asTaskFragment().forOtherAdjacentTaskFragments(
+                                    tf -> !isOpaque(tf));
                     if (!isAnyTranslucent) {
                         // This task fragment and all its adjacent task fragments are opaque,
                         // consider it opaque even if it doesn't fill its parent.
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index 3535a96..d6f058a 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -240,7 +240,7 @@
         final Configuration resolvedConfig = mActivityRecord.getResolvedOverrideConfiguration();
         final Rect parentAppBounds =
                 mActivityRecord.mResolveConfigHint.mParentAppBoundsOverride;
-        final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
+        final Rect parentBounds = mActivityRecord.mResolveConfigHint.mParentBoundsOverride;
         final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
         // Use tmp bounds to calculate aspect ratio so we can know whether the activity should
         // use restricted size (resolved bounds may be the requested override bounds).
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 48f08e9..28f9a33 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -33,6 +33,8 @@
     @NonNull
     private final AppCompatAspectRatioPolicy mAspectRatioPolicy;
     @NonNull
+    private final AppCompatSafeRegionPolicy mSafeRegionPolicy;
+    @NonNull
     private final AppCompatReachabilityPolicy mReachabilityPolicy;
     @NonNull
     private final DesktopAppCompatAspectRatioPolicy mDesktopAspectRatioPolicy;
@@ -46,6 +48,8 @@
     private final AppCompatSizeCompatModePolicy mSizeCompatModePolicy;
     @NonNull
     private final AppCompatSandboxingPolicy mSandboxingPolicy;
+    @NonNull
+    private final AppCompatDisplayCompatModePolicy mDisplayCompatModePolicy;
 
     AppCompatController(@NonNull WindowManagerService wmService,
                         @NonNull ActivityRecord activityRecord) {
@@ -60,6 +64,7 @@
         mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord, mAppCompatOverrides);
         mAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord,
                 mTransparentPolicy, mAppCompatOverrides);
+        mSafeRegionPolicy = new AppCompatSafeRegionPolicy(activityRecord, packageManager);
         mReachabilityPolicy = new AppCompatReachabilityPolicy(activityRecord,
                 wmService.mAppCompatConfiguration);
         mLetterboxPolicy = new AppCompatLetterboxPolicy(activityRecord,
@@ -69,6 +74,7 @@
         mSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(activityRecord,
                 mAppCompatOverrides);
         mSandboxingPolicy = new AppCompatSandboxingPolicy(activityRecord);
+        mDisplayCompatModePolicy = new AppCompatDisplayCompatModePolicy();
     }
 
     @NonNull
@@ -87,6 +93,11 @@
     }
 
     @NonNull
+    AppCompatSafeRegionPolicy getSafeRegionPolicy() {
+        return mSafeRegionPolicy;
+    }
+
+    @NonNull
     DesktopAppCompatAspectRatioPolicy getDesktopAspectRatioPolicy() {
         return mDesktopAspectRatioPolicy;
     }
@@ -151,10 +162,15 @@
         return mSandboxingPolicy;
     }
 
+    @NonNull
+    AppCompatDisplayCompatModePolicy getDisplayCompatModePolicy() {
+        return mDisplayCompatModePolicy;
+    }
+
     void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
         getTransparentPolicy().dump(pw, prefix);
         getLetterboxPolicy().dump(pw, prefix);
         getSizeCompatModePolicy().dump(pw, prefix);
+        getSafeRegionPolicy().dump(pw, prefix);
     }
-
 }
diff --git a/services/core/java/com/android/server/wm/AppCompatDisplayCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatDisplayCompatModePolicy.java
new file mode 100644
index 0000000..acf5170
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatDisplayCompatModePolicy.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import com.android.window.flags.Flags;
+
+/**
+ * Encapsulate app-compat logic for multi-display environments.
+ */
+class AppCompatDisplayCompatModePolicy {
+
+    private boolean mIsRestartMenuEnabledForDisplayMove;
+
+    boolean isRestartMenuEnabledForDisplayMove() {
+        return Flags.enableRestartMenuForConnectedDisplays() && mIsRestartMenuEnabledForDisplayMove;
+    }
+
+    void onMovedToDisplay() {
+        mIsRestartMenuEnabledForDisplayMove = true;
+    }
+
+    void onProcessRestarted() {
+        mIsRestartMenuEnabledForDisplayMove = false;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java b/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
new file mode 100644
index 0000000..9596093
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import java.io.PrintWriter;
+import java.util.function.BooleanSupplier;
+
+/**
+ * Encapsulate app compat policy logic related to a safe region.
+ */
+class AppCompatSafeRegionPolicy {
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+    @NonNull
+    final PackageManager mPackageManager;
+    // Whether the Activity needs to be in the safe region bounds.
+    private boolean mNeedsSafeRegionBounds = false;
+    // Denotes the latest safe region bounds. Can be empty if the activity or the ancestors do
+    // not have any safe region bounds.
+    @NonNull
+    private final Rect mLatestSafeRegionBounds = new Rect();
+    // Whether the activity has allowed safe region letterboxing. This can be set through the
+    // manifest and the default value is true.
+    @NonNull
+    private final BooleanSupplier mAllowSafeRegionLetterboxing;
+
+    AppCompatSafeRegionPolicy(@NonNull ActivityRecord activityRecord,
+            @NonNull PackageManager packageManager) {
+        mActivityRecord = activityRecord;
+        mPackageManager = packageManager;
+        mAllowSafeRegionLetterboxing = AppCompatUtils.asLazy(() -> {
+            // Application level property.
+            if (allowSafeRegionLetterboxing(packageManager)) {
+                return true;
+            }
+            // Activity level property.
+            try {
+                return packageManager.getPropertyAsUser(
+                        PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING,
+                        mActivityRecord.mActivityComponent.getPackageName(),
+                        mActivityRecord.mActivityComponent.getClassName(),
+                        mActivityRecord.mUserId).getBoolean();
+            } catch (PackageManager.NameNotFoundException e) {
+                return true;
+            }
+        });
+    }
+
+    private boolean allowSafeRegionLetterboxing(PackageManager pm) {
+        try {
+            return pm.getPropertyAsUser(
+                    PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING,
+                    mActivityRecord.packageName,
+                    /* className */ null,
+                    mActivityRecord.mUserId).getBoolean();
+        } catch (PackageManager.NameNotFoundException e) {
+            return true;
+        }
+    }
+
+    /**
+     * Computes the latest safe region bounds in
+     * {@link ActivityRecord#resolveOverrideConfiguration(Configuration)} since the activity has not
+     * been attached to the parent container when the ActivityRecord is instantiated. Note that the
+     * latest safe region bounds will be empty if activity has not allowed safe region letterboxing.
+     *
+     * @return latest safe region bounds as set on an ancestor window container.
+     */
+    public Rect getLatestSafeRegionBounds() {
+        if (!allowSafeRegionLetterboxing()) {
+            mLatestSafeRegionBounds.setEmpty();
+            return null;
+        }
+        // Get the latest safe region bounds since the bounds could have changed
+        final Rect latestSafeRegionBounds = mActivityRecord.getSafeRegionBounds();
+        if (latestSafeRegionBounds != null) {
+            mLatestSafeRegionBounds.set(latestSafeRegionBounds);
+        } else {
+            mLatestSafeRegionBounds.setEmpty();
+        }
+        return latestSafeRegionBounds;
+    }
+
+    /**
+     * Computes bounds when letterboxing is required only for the safe region bounds if needed.
+     */
+    public void resolveSafeRegionBoundsConfigurationIfNeeded(@NonNull Configuration resolvedConfig,
+            @NonNull Configuration newParentConfig) {
+        if (mLatestSafeRegionBounds.isEmpty()) {
+            return;
+        }
+        // If activity can not be letterboxed for a safe region only or it has not been attached
+        // to a WindowContainer yet.
+        if (!isLetterboxedForSafeRegionOnlyAllowed() || mActivityRecord.getParent() == null) {
+            return;
+        }
+        resolvedConfig.windowConfiguration.setBounds(mLatestSafeRegionBounds);
+        mActivityRecord.computeConfigByResolveHint(resolvedConfig, newParentConfig);
+    }
+
+    /**
+     * Safe region bounds can either be applied along with size compat, fixed orientation or
+     * aspect ratio conditions by sandboxing them to the safe region bounds. Or it can be applied
+     * independently when no other letterboxing condition is triggered. This method helps detecting
+     * the latter case.
+     *
+     * @return {@code true} if this application or activity has allowed safe region letterboxing and
+     * can be letterboxed only due to the safe region being set on the current or ancestor window
+     * container.
+     */
+    boolean isLetterboxedForSafeRegionOnlyAllowed() {
+        return !mActivityRecord.areBoundsLetterboxed() && getNeedsSafeRegionBounds()
+                && getLatestSafeRegionBounds() != null;
+    }
+
+    /**
+     * Set {@code true} if this activity needs to be within the safe region bounds, else false.
+     */
+    public void setNeedsSafeRegionBounds(boolean needsSafeRegionBounds) {
+        mNeedsSafeRegionBounds = needsSafeRegionBounds;
+    }
+
+    /**
+     * @return {@code true} if this activity needs to be within the safe region bounds.
+     */
+    public boolean getNeedsSafeRegionBounds() {
+        return mNeedsSafeRegionBounds;
+    }
+
+    /** @see android.view.WindowManager#PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING */
+    boolean allowSafeRegionLetterboxing() {
+        return mAllowSafeRegionLetterboxing.getAsBoolean();
+    }
+
+    void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+        if (mNeedsSafeRegionBounds) {
+            pw.println(prefix + " mNeedsSafeRegionBounds=true");
+        }
+        if (isLetterboxedForSafeRegionOnlyAllowed()) {
+            pw.println(prefix + " isLetterboxForSafeRegionOnlyAllowed=true");
+        }
+        if (!allowSafeRegionLetterboxing()) {
+            pw.println(prefix + " allowSafeRegionLetterboxing=false");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index b03aa52..0f1e36d 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -361,7 +361,10 @@
         if (enableSizeCompatModeImprovementsForConnectedDisplays()) {
             overrideConfig.touchscreen = fullConfig.touchscreen;
             overrideConfig.navigation = fullConfig.navigation;
-            overrideConfig.fontScale = fullConfig.fontScale;
+            overrideConfig.keyboard = fullConfig.keyboard;
+            overrideConfig.keyboardHidden = fullConfig.keyboardHidden;
+            overrideConfig.hardKeyboardHidden = fullConfig.hardKeyboardHidden;
+            overrideConfig.navigationHidden = fullConfig.navigationHidden;
         }
         // The smallest screen width is the short side of screen bounds. Because the bounds
         // and density won't be changed, smallestScreenWidthDp is also fixed.
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 1460440..f872286 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -161,6 +161,9 @@
                 top.mAppCompatController.getLetterboxOverrides()
                         .isLetterboxEducationEnabled());
 
+        appCompatTaskInfo.setRestartMenuEnabledForDisplayMove(top.mAppCompatController
+                .getDisplayCompatModePolicy().isRestartMenuEnabledForDisplayMove());
+
         final AppCompatAspectRatioOverrides aspectRatioOverrides =
                 top.mAppCompatController.getAspectRatioOverrides();
         appCompatTaskInfo.setUserFullscreenOverrideEnabled(
@@ -213,6 +216,7 @@
                 AppCompatCameraPolicy.getCameraCompatFreeformMode(top);
         appCompatTaskInfo.setHasMinAspectRatioOverride(top.mAppCompatController
                 .getDesktopAspectRatioPolicy().hasMinAspectRatioOverride(task));
+        appCompatTaskInfo.setOptOutEdgeToEdge(top.mOptOutEdgeToEdge);
     }
 
     /**
@@ -238,6 +242,10 @@
         if (aspectRatioPolicy.isLetterboxedForAspectRatioOnly()) {
             return "ASPECT_RATIO";
         }
+        if (activityRecord.mAppCompatController.getSafeRegionPolicy()
+                .isLetterboxedForSafeRegionOnlyAllowed()) {
+            return "SAFE_REGION";
+        }
         return "UNKNOWN_REASON";
     }
 
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index e9b7649..dfe323c 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -479,21 +479,16 @@
                 }
             } else {
                 // If adjacent TF has companion to current TF, those two TF will be closed together.
-                final TaskFragment adjacentTF;
-                if (Flags.allowMultipleAdjacentTaskFragments()) {
-                    if (currTF.getAdjacentTaskFragments().size() > 2) {
-                        throw new IllegalStateException(
-                                "Not yet support 3+ adjacent for non-Task TFs");
-                    }
-                    final TaskFragment[] tmpAdjacent = new TaskFragment[1];
-                    currTF.forOtherAdjacentTaskFragments(tf -> {
-                        tmpAdjacent[0] = tf;
-                        return true;
-                    });
-                    adjacentTF = tmpAdjacent[0];
-                } else {
-                    adjacentTF = currTF.getAdjacentTaskFragment();
+                if (currTF.getAdjacentTaskFragments().size() > 2) {
+                    throw new IllegalStateException(
+                            "Not yet support 3+ adjacent for non-Task TFs");
                 }
+                final TaskFragment[] tmpAdjacent = new TaskFragment[1];
+                currTF.forOtherAdjacentTaskFragments(tf -> {
+                    tmpAdjacent[0] = tf;
+                    return true;
+                });
+                final TaskFragment adjacentTF = tmpAdjacent[0];
                 if (isSecondCompanionToFirst(currTF, adjacentTF)) {
                     // The two TFs are adjacent (visually displayed side-by-side), search if any
                     // activity below the lowest one.
@@ -553,15 +548,6 @@
         if (!prevTF.hasAdjacentTaskFragment()) {
             return;
         }
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            final TaskFragment prevTFAdjacent = prevTF.getAdjacentTaskFragment();
-            final ActivityRecord prevActivityAdjacent =
-                    prevTFAdjacent.getTopNonFinishingActivity();
-            if (prevActivityAdjacent != null) {
-                outPrevActivities.add(prevActivityAdjacent);
-            }
-            return;
-        }
         prevTF.forOtherAdjacentTaskFragments(prevTFAdjacent -> {
             final ActivityRecord prevActivityAdjacent =
                     prevTFAdjacent.getTopNonFinishingActivity();
@@ -577,14 +563,6 @@
         if (mainTF == null || !mainTF.hasAdjacentTaskFragment()) {
             return;
         }
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            final TaskFragment adjacentTF = mainTF.getAdjacentTaskFragment();
-            final ActivityRecord topActivity = adjacentTF.getTopNonFinishingActivity();
-            if (topActivity != null) {
-                outList.add(topActivity);
-            }
-            return;
-        }
         mainTF.forOtherAdjacentTaskFragments(adjacentTF -> {
             final ActivityRecord topActivity = adjacentTF.getTopNonFinishingActivity();
             if (topActivity != null) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 2287a68..f50a68c 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -49,8 +49,8 @@
 import static com.android.window.flags.Flags.balImprovedMetrics;
 import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator;
 import static com.android.window.flags.Flags.balShowToastsBlocked;
-import static com.android.window.flags.Flags.balStrictModeRo;
 import static com.android.window.flags.Flags.balStrictModeGracePeriod;
+import static com.android.window.flags.Flags.balStrictModeRo;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 import static java.util.Objects.requireNonNull;
@@ -91,7 +91,6 @@
 import com.android.server.UiThread;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfiguration;
-import com.android.window.flags.Flags;
 
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
@@ -1687,14 +1686,6 @@
         }
 
         // Check the adjacent fragment.
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
-            topActivity = adjacentTaskFragment.getActivity(topOfStackPredicate);
-            if (topActivity == null) {
-                return bas;
-            }
-            return checkCrossUidActivitySwitchFromBelow(topActivity, uid, bas);
-        }
         final BlockActivityStart[] out = { bas };
         taskFragment.forOtherAdjacentTaskFragments(adjacentTaskFragment -> {
             final ActivityRecord top = adjacentTaskFragment.getActivity(topOfStackPredicate);
diff --git a/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java b/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java
index 5db02df..aaa5a00 100644
--- a/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/BaseAppSnapshotPersister.java
@@ -16,10 +16,20 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.AbsAppSnapshotController.TAG;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.window.TaskSnapshot;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
+
 import java.io.File;
+import java.util.UUID;
 
 class BaseAppSnapshotPersister {
     static final String LOW_RES_FILE_POSTFIX = "_reduced";
@@ -29,6 +39,7 @@
     // Shared with SnapshotPersistQueue
     protected final Object mLock;
     protected final SnapshotPersistQueue mSnapshotPersistQueue;
+    @VisibleForTesting
     protected final PersistInfoProvider mPersistInfoProvider;
 
     BaseAppSnapshotPersister(SnapshotPersistQueue persistQueue,
@@ -79,6 +90,8 @@
         private final boolean mEnableLowResSnapshots;
         private final float mLowResScaleFactor;
         private final boolean mUse16BitFormat;
+        private final SparseBooleanArray mInitializedUsers = new SparseBooleanArray();
+        private final SparseArray<File> mScrambleDirectories = new SparseArray<>();
 
         PersistInfoProvider(DirectoryResolver directoryResolver, String dirName,
                 boolean enableLowResSnapshots, float lowResScaleFactor, boolean use16BitFormat) {
@@ -91,9 +104,80 @@
 
         @NonNull
         File getDirectory(int userId) {
+            if (Flags.scrambleSnapshotFileName()) {
+                final File directory = getOrInitScrambleDirectory(userId);
+                if (directory != null) {
+                    return directory;
+                }
+            }
+            return getBaseDirectory(userId);
+        }
+
+        @NonNull
+        private File getBaseDirectory(int userId) {
             return new File(mDirectoryResolver.getSystemDirectoryForUser(userId), mDirName);
         }
 
+        @Nullable
+        private File getOrInitScrambleDirectory(int userId) {
+            synchronized (mScrambleDirectories) {
+                if (mInitializedUsers.get(userId)) {
+                    return mScrambleDirectories.get(userId);
+                }
+                mInitializedUsers.put(userId, true);
+                final File scrambledDirectory = getScrambleDirectory(userId);
+                final File baseDir = getBaseDirectory(userId);
+                String newName = null;
+                // If directory exists, rename
+                if (scrambledDirectory.exists()) {
+                    newName = UUID.randomUUID().toString();
+                    final File scrambleTo = new File(baseDir, newName);
+                    if (!scrambledDirectory.renameTo(scrambleTo)) {
+                        Slog.w(TAG, "SnapshotPersister rename scramble folder fail.");
+                        return null;
+                    }
+                } else {
+                    // If directory not exists, mkDir.
+                    if (!baseDir.exists() && !baseDir.mkdir()) {
+                        Slog.w(TAG, "SnapshotPersister make base folder fail.");
+                        return null;
+                    }
+                    if (!scrambledDirectory.mkdir()) {
+                        Slog.e(TAG, "SnapshotPersister make scramble folder fail");
+                        return null;
+                    }
+                    // Move any existing files to this folder.
+                    final String[] files = baseDir.list();
+                    if (files != null) {
+                        for (String file : files) {
+                            final File original = new File(baseDir, file);
+                            if (original.isDirectory()) {
+                                newName = file;
+                            } else {
+                                File to = new File(scrambledDirectory, file);
+                                original.renameTo(to);
+                            }
+                        }
+                    }
+                }
+                final File newFolder = new File(baseDir, newName);
+                mScrambleDirectories.put(userId, newFolder);
+                return newFolder;
+            }
+        }
+
+        @NonNull
+        private File getScrambleDirectory(int userId) {
+            final File dir = getBaseDirectory(userId);
+            final String[] directories = dir.list(
+                    (current, name) -> new File(current, name).isDirectory());
+            if (directories != null && directories.length > 0) {
+                return new File(dir, directories[0]);
+            } else {
+                return new File(dir, UUID.randomUUID().toString());
+            }
+        }
+
         /**
          * Return if task snapshots are stored in 16 bit pixel format.
          *
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index c26acec..1c25692 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -38,6 +38,7 @@
 import android.os.ServiceManager;
 import android.view.ContentRecordingSession;
 import android.view.ContentRecordingSession.RecordContent;
+import android.window.DesktopExperienceFlags;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.SurfaceControl;
@@ -353,6 +354,13 @@
             return;
         }
 
+        // Recording should not be started on displays that are eligible for hosting tasks.
+        // See android.view.Display#canHostTasks().
+        if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()
+                && mDisplayContent.mDisplay.canHostTasks()) {
+            return;
+        }
+
         if (mContentRecordingSession.isWaitingForConsent() || !isDisplayReadyForMirroring()) {
             ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Content Recording: waiting to record, so do "
                     + "nothing");
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index f473b7b..b802754 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -18,7 +18,7 @@
 
 import static android.view.WindowManager.TRANSIT_CHANGE;
 
-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.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
 import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS;
 import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
@@ -38,7 +38,6 @@
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.protolog.ProtoLog;
 import com.android.server.wm.utils.DisplayInfoOverrides.DisplayInfoFieldsUpdater;
-import com.android.window.flags.Flags;
 
 import java.util.Arrays;
 import java.util.Objects;
@@ -140,8 +139,9 @@
         if (displayInfoDiff == DIFF_EVERYTHING
                 || !mDisplayContent.getLastHasContent()
                 || !mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
-            ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS,
-                    "DeferredDisplayUpdater: applying DisplayInfo immediately");
+            ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
+                    "DeferredDisplayUpdater: applying DisplayInfo(%d x %d) immediately",
+                    displayInfo.logicalWidth, displayInfo.logicalHeight);
 
             mLastWmDisplayInfo = displayInfo;
             applyLatestDisplayInfo();
@@ -151,17 +151,23 @@
 
         // If there are non WM-specific display info changes, apply only these fields immediately
         if ((displayInfoDiff & DIFF_NOT_WM_DEFERRABLE) > 0) {
-            ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS,
-                    "DeferredDisplayUpdater: partially applying DisplayInfo immediately");
+            ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
+                    "DeferredDisplayUpdater: partially applying DisplayInfo(%d x %d) immediately",
+                    displayInfo.logicalWidth, displayInfo.logicalHeight);
             applyLatestDisplayInfo();
         }
 
         // If there are WM-specific display info changes, apply them through a Shell transition
         if ((displayInfoDiff & DIFF_WM_DEFERRABLE) > 0) {
-            ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS,
-                    "DeferredDisplayUpdater: deferring DisplayInfo update");
+            ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
+                    "DeferredDisplayUpdater: deferring DisplayInfo(%d x %d) update",
+                    displayInfo.logicalWidth, displayInfo.logicalHeight);
 
             requestDisplayChangeTransition(physicalDisplayUpdated, () -> {
+                ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
+                        "DeferredDisplayUpdater: applying DisplayInfo(%d x %d) after deferring",
+                        displayInfo.logicalWidth, displayInfo.logicalHeight);
+
                 // Apply deferrable fields to DisplayContent only when the transition
                 // starts collecting, non-deferrable fields are ignored in mLastWmDisplayInfo
                 mLastWmDisplayInfo = displayInfo;
@@ -199,7 +205,7 @@
                         mDisplayContent.getDisplayPolicy().getNotificationShade();
                 if (notificationShade != null && notificationShade.isVisible()
                         && mDisplayContent.mAtmService.mKeyguardController.isKeyguardOrAodShowing(
-                                mDisplayContent.mDisplayId)) {
+                        mDisplayContent.mDisplayId)) {
                     Slog.i(TAG, notificationShade + " uses blast for display switch");
                     notificationShade.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST;
                 }
@@ -209,9 +215,6 @@
             try {
                 onStartCollect.run();
 
-                ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS,
-                        "DeferredDisplayUpdater: applied DisplayInfo after deferring");
-
                 if (physicalDisplayUpdated) {
                     onDisplayUpdated(transition, fromRotation, startBounds);
                 } else {
@@ -332,7 +335,6 @@
 
     /** Returns {@code true} if the transition will control when to turn on the screen. */
     boolean waitForTransition(@NonNull Message screenUnblocker) {
-        if (!Flags.waitForTransitionOnDisplaySwitch()) return false;
         if (!mShouldWaitForTransitionWhenScreenOn) {
             return false;
         }
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index 7a959c1..d935432 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -24,6 +24,8 @@
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
+import static com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightPx;
+import static com.android.internal.policy.DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds;
 import static com.android.server.wm.LaunchParamsUtil.applyLayoutGravity;
 import static com.android.server.wm.LaunchParamsUtil.calculateLayoutBounds;
 
@@ -64,49 +66,61 @@
      */
     static void updateInitialBounds(@NonNull Task task, @Nullable WindowLayout layout,
             @Nullable ActivityRecord activity, @Nullable ActivityOptions options,
-            @NonNull Rect outBounds, @NonNull Consumer<String> logger) {
+            @NonNull LaunchParamsController.LaunchParams outParams,
+            @NonNull Consumer<String> logger) {
         // Use stable frame instead of raw frame to avoid launching freeform windows on top of
         // stable insets, which usually are system widgets such as sysbar & navbar.
         final Rect stableBounds = new Rect();
         task.getDisplayArea().getStableRect(stableBounds);
 
-        // If the options bounds size is flexible, update size with calculated desired size.
+        final boolean hasFullscreenOverride = activity != null
+                && activity.mAppCompatController.getAspectRatioOverrides().hasFullscreenOverride();
+        // If the options bounds size is flexible and no fullscreen override has been applied,
+        // update size with calculated desired size.
         final boolean updateOptionBoundsSize = options != null
-                && options.getFlexibleLaunchSize();
+                && options.getFlexibleLaunchSize() && !hasFullscreenOverride;
         // If cascading is also enabled, the position of the options bounds must be respected
         // during the size update.
         final boolean shouldRespectOptionPosition =
                 updateOptionBoundsSize && DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue();
+        final int captionHeight = activity != null && shouldExcludeCaptionFromAppBounds(
+                activity.info, task.isResizeable(), activity.mOptOutEdgeToEdge)
+                        ? getDesktopViewAppHeaderHeightPx(activity.mWmService.mContext) : 0;
 
         if (options != null && options.getLaunchBounds() != null
                 && !updateOptionBoundsSize) {
-            outBounds.set(options.getLaunchBounds());
-            logger.accept("inherit-from-options=" + outBounds);
+            outParams.mBounds.set(options.getLaunchBounds());
+            logger.accept("inherit-from-options=" + outParams.mBounds);
         } else if (layout != null) {
             final int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
             final int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
             if (layout.hasSpecifiedSize()) {
-                calculateLayoutBounds(stableBounds, layout, outBounds,
+                calculateLayoutBounds(stableBounds, layout, outParams.mBounds,
                         calculateIdealSize(stableBounds, DESKTOP_MODE_INITIAL_BOUNDS_SCALE));
-                applyLayoutGravity(verticalGravity, horizontalGravity, outBounds,
+                applyLayoutGravity(verticalGravity, horizontalGravity, outParams.mBounds,
                         stableBounds);
                 logger.accept("layout specifies sizes, inheriting size and applying gravity");
             } else if (verticalGravity > 0 || horizontalGravity > 0) {
-                outBounds.set(calculateInitialBounds(task, activity, stableBounds, options,
-                        shouldRespectOptionPosition));
-                applyLayoutGravity(verticalGravity, horizontalGravity, outBounds,
+                outParams.mBounds.set(calculateInitialBounds(task, activity, stableBounds, options,
+                        shouldRespectOptionPosition, captionHeight));
+                applyLayoutGravity(verticalGravity, horizontalGravity, outParams.mBounds,
                         stableBounds);
                 logger.accept("layout specifies gravity, applying desired bounds and gravity");
                 logger.accept("respecting option bounds cascaded position="
                         + shouldRespectOptionPosition);
             }
         } else {
-            outBounds.set(calculateInitialBounds(task, activity, stableBounds, options,
-                    shouldRespectOptionPosition));
+            outParams.mBounds.set(calculateInitialBounds(task, activity, stableBounds, options,
+                    shouldRespectOptionPosition, captionHeight));
             logger.accept("layout not specified, applying desired bounds");
             logger.accept("respecting option bounds cascaded position="
                     + shouldRespectOptionPosition);
         }
+        if (updateOptionBoundsSize && captionHeight != 0) {
+            outParams.mAppBounds.set(outParams.mBounds);
+            outParams.mAppBounds.top += captionHeight;
+            logger.accept("excluding caption height from app bounds");
+        }
     }
 
     /**
@@ -119,7 +133,8 @@
     @NonNull
     private static Rect calculateInitialBounds(@NonNull Task task,
             @NonNull ActivityRecord activity, @NonNull Rect stableBounds,
-            @Nullable ActivityOptions options, boolean shouldRespectOptionPosition
+            @Nullable ActivityOptions options, boolean shouldRespectOptionPosition,
+            int captionHeight
     ) {
         // Display bounds not taking into account insets.
         final TaskDisplayArea displayArea = task.getDisplayArea();
@@ -160,7 +175,8 @@
                 }
                 // If activity is unresizeable, regardless of orientation, calculate maximum size
                 // (within the ideal size) maintaining original aspect ratio.
-                yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio);
+                yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio,
+                        captionHeight);
             }
             case ORIENTATION_PORTRAIT -> {
                 // Device in portrait orientation.
@@ -188,11 +204,12 @@
                     // ratio.
                     yield maximizeSizeGivenAspectRatio(activityOrientation,
                             new Size(customPortraitWidthForLandscapeApp, idealSize.getHeight()),
-                            appAspectRatio);
+                            appAspectRatio, captionHeight);
                 }
                 // For portrait unresizeable activities, calculate maximum size (within the ideal
                 // size) maintaining original aspect ratio.
-                yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio);
+                yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio,
+                        captionHeight);
             }
             default -> idealSize;
         };
@@ -232,13 +249,15 @@
      * Calculates the largest size that can fit in a given area while maintaining a specific aspect
      * ratio.
      */
+    // TODO(b/400617906): Merge duplicate initial bounds calculations to shared class.
     @NonNull
     private static Size maximizeSizeGivenAspectRatio(
             @ScreenOrientation int orientation,
             @NonNull Size targetArea,
-            float aspectRatio
+            float aspectRatio,
+            int captionHeight
     ) {
-        final int targetHeight = targetArea.getHeight();
+        final int targetHeight = targetArea.getHeight() - captionHeight;
         final int targetWidth = targetArea.getWidth();
         final int finalHeight;
         final int finalWidth;
@@ -275,7 +294,7 @@
                 finalHeight = (int) (finalWidth / aspectRatio);
             }
         }
-        return new Size(finalWidth, finalHeight);
+        return new Size(finalWidth, finalHeight + captionHeight);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java
index dc42b32..d91fca9 100644
--- a/services/core/java/com/android/server/wm/DesktopModeHelper.java
+++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.Flags.enableConnectedDisplaysWallpaper;
+import static android.window.DesktopExperienceFlags.ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -66,7 +67,7 @@
      * Return {@code true} if the current device can hosts desktop sessions on its internal display.
      */
     @VisibleForTesting
-    static boolean canInternalDisplayHostDesktops(@NonNull Context context) {
+    private static boolean canInternalDisplayHostDesktops(@NonNull Context context) {
         return context.getResources().getBoolean(R.bool.config_canInternalDisplayHostDesktops);
     }
 
@@ -83,8 +84,11 @@
         if (!shouldEnforceDeviceRestrictions()) {
             return true;
         }
-        final boolean desktopModeSupported = isDesktopModeSupported(context)
-                && canInternalDisplayHostDesktops(context);
+        // If projected display is enabled, #canInternalDisplayHostDesktops is no longer a
+        // requirement.
+        final boolean desktopModeSupported = ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE.isTrue()
+                ? isDesktopModeSupported(context) : (isDesktopModeSupported(context)
+                && canInternalDisplayHostDesktops(context));
         final boolean desktopModeSupportedByDevOptions =
                 Flags.enableDesktopModeThroughDevOption()
                         && isDesktopModeDevOptionsSupported(context);
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index d466a64..03ba1a5 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -19,6 +19,12 @@
 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_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -107,14 +113,23 @@
         // Copy over any values
         outParams.set(currentParams);
 
-        // In Proto2, trampoline task launches of an existing background task can result in the
-        // previous windowing mode to be restored even if the desktop mode state has changed.
-        // Let task launches inherit the windowing mode from the source task if available, which
-        // should have the desired windowing mode set by WM Shell. See b/286929122.
         if (source != null && source.getTask() != null) {
             final Task sourceTask = source.getTask();
-            outParams.mWindowingMode = sourceTask.getWindowingMode();
-            appendLog("inherit-from-source=" + outParams.mWindowingMode);
+            if (DesktopModeFlags.DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX.isTrue()
+                    && isEnteringDesktopMode(sourceTask, options, currentParams)) {
+                // If trampoline source is not freeform but we are entering or in desktop mode,
+                // ignore the source windowing mode and set the windowing mode to freeform
+                outParams.mWindowingMode = WINDOWING_MODE_FREEFORM;
+                appendLog("freeform window mode applied to task trampoline");
+            } else {
+                // In Proto2, trampoline task launches of an existing background task can result in
+                // the previous windowing mode to be restored even if the desktop mode state has
+                // changed. Let task launches inherit the windowing mode from the source task if
+                // available, which should have the desired windowing mode set by WM Shell.
+                // See b/286929122.
+                outParams.mWindowingMode = sourceTask.getWindowingMode();
+                appendLog("inherit-from-source=" + outParams.mWindowingMode);
+            }
         }
 
         if (phase == PHASE_WINDOWING_MODE) {
@@ -127,12 +142,29 @@
         }
 
         if ((options == null || options.getLaunchBounds() == null) && task.hasOverrideBounds()) {
+            if (DesktopModeFlags.DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX.isTrue()) {
+                // We are in desktop, return result done to prevent other modifiers from modifying
+                // exiting task bounds or resolved windowing mode.
+                return RESULT_DONE;
+            }
             appendLog("current task has bounds set, not overriding");
             return RESULT_SKIP;
         }
 
+        if (DesktopModeFlags.INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES.isTrue()) {
+            ActivityRecord topVisibleFreeformActivity =
+                    task.getDisplayContent().getTopMostVisibleFreeformActivity();
+            if (shouldInheritExistingTaskBounds(topVisibleFreeformActivity, activity, task)) {
+                appendLog("inheriting bounds from existing closing instance");
+                outParams.mBounds.set(topVisibleFreeformActivity.getBounds());
+                appendLog("final desktop mode task bounds set to %s", outParams.mBounds);
+                // Return result done to prevent other modifiers from changing or cascading bounds.
+                return RESULT_DONE;
+            }
+        }
+
         DesktopModeBoundsCalculator.updateInitialBounds(task, layout, activity, options,
-                outParams.mBounds, this::appendLog);
+                outParams, this::appendLog);
         appendLog("final desktop mode task bounds set to %s", outParams.mBounds);
         if (options != null && options.getFlexibleLaunchSize()) {
             // Return result done to prevent other modifiers from respecting option bounds and
@@ -159,7 +191,7 @@
         //  activity will also enter desktop mode. On this same relationship, we can also assume
         //  if there are not visible freeform tasks but a freeform activity is now launching, it
         //  will force the device into desktop mode.
-        return (task.getDisplayContent().getTopMostVisibleFreeformActivity() != null
+        return (task.getDisplayContent().getTopMostFreeformActivity() != null
                     && checkSourceWindowModesCompatible(task, options, currentParams))
                 || isRequestingFreeformWindowMode(task, options, currentParams);
     }
@@ -201,6 +233,40 @@
         };
     }
 
+    /**
+     * Whether the launching task should inherit the task bounds of an existing closing instance.
+     */
+    private boolean shouldInheritExistingTaskBounds(
+            @Nullable ActivityRecord existingTaskActivity,
+            @Nullable ActivityRecord launchingActivity,
+            @NonNull Task launchingTask) {
+        if (existingTaskActivity == null || launchingActivity == null) return false;
+        return (existingTaskActivity.packageName == launchingActivity.packageName)
+                && isLaunchingNewTask(launchingActivity.launchMode,
+                    launchingTask.getBaseIntent().getFlags())
+                && isClosingExitingInstance(launchingTask.getBaseIntent().getFlags());
+    }
+
+    /**
+     * Returns true if the launch mode or intent will result in a new task being created for the
+     * activity.
+     */
+    private boolean isLaunchingNewTask(int launchMode, int intentFlags) {
+        return launchMode == LAUNCH_SINGLE_TASK
+                || launchMode == LAUNCH_SINGLE_INSTANCE
+                || launchMode == LAUNCH_SINGLE_INSTANCE_PER_TASK
+                || (intentFlags & FLAG_ACTIVITY_NEW_TASK) != 0;
+    }
+
+    /**
+     * Returns true if the intent will result in an existing task instance being closed if a new
+     * one appears.
+     */
+    private boolean isClosingExitingInstance(int intentFlags) {
+        return (intentFlags & FLAG_ACTIVITY_CLEAR_TASK) != 0
+            || (intentFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
+    }
+
     private void initLogBuilder(Task task, ActivityRecord activity) {
         if (DEBUG) {
             mLogBuilder = new StringBuilder(
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 25fdf89..ab87459 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -211,14 +211,22 @@
      * child should call setAppearance again to request the Dim to continue.
      * If multiple containers call this method, only the changes relative to the topmost will be
      * applied.
+     * The creation of the dim layer is delayed if the requested dim and blur are 0.
      * @param dimmingContainer  Container requesting the dim
      * @param alpha      Dim amount
      * @param blurRadius Blur amount
      */
     protected void adjustAppearance(@NonNull WindowState dimmingContainer,
                                     float alpha, int blurRadius) {
-        final DimState d = obtainDimState(dimmingContainer);
-        d.prepareLookChange(alpha, blurRadius);
+        if (!mHost.isVisibleRequested()) {
+            // If the host is already going away, there is no point in keeping dimming
+            return;
+        }
+
+        if (mDimState != null || (alpha != 0 || blurRadius != 0)) {
+            final DimState d = obtainDimState(dimmingContainer);
+            d.prepareLookChange(alpha, blurRadius);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 353ccd5..2f9242f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -41,6 +41,7 @@
 import static android.util.TypedValue.COMPLEX_UNIT_MASK;
 import static android.util.TypedValue.COMPLEX_UNIT_SHIFT;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
 import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
 import static android.view.Display.FLAG_PRIVATE;
 import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
@@ -2161,7 +2162,7 @@
     /** Re-show the previously hidden windows if all seamless rotated windows are done. */
     void finishAsyncRotationIfPossible() {
         final AsyncRotationController controller = mAsyncRotationController;
-        if (controller != null && !mDisplayRotation.hasSeamlessRotatingWindow()) {
+        if (controller != null) {
             controller.completeAll();
             mAsyncRotationController = null;
         }
@@ -2238,11 +2239,7 @@
      */
     private void applyRotation(final int oldRotation, final int rotation) {
         mDisplayRotation.applyCurrentRotation(rotation);
-        final boolean shellTransitions = mTransitionController.getTransitionPlayer() != null;
-        final boolean rotateSeamlessly =
-                mDisplayRotation.isRotatingSeamlessly() && !shellTransitions;
-        final Transaction transaction =
-                shellTransitions ? getSyncTransaction() : getPendingTransaction();
+
         // We need to update our screen size information to match the new rotation. If the rotation
         // has actually changed then this method will return true and, according to the comment at
         // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -2250,25 +2247,13 @@
         // #computeScreenConfiguration() later.
         updateDisplayAndOrientation(null /* outConfig */);
 
-        if (!shellTransitions) {
-            forAllWindows(w -> {
-                w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
-            }, true /* traverseTopToBottom */);
-            mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
-            if (!mDisplayRotation.hasSeamlessRotatingWindow()) {
-                // Make sure DisplayRotation#isRotatingSeamlessly() will return false.
-                mDisplayRotation.cancelSeamlessRotation();
-            }
-        }
+        // Before setDisplayProjection is applied by the start transaction of transition,
+        // set the transform hint to avoid using surface in old rotation.
+        setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
+        // The sync transaction should already contains setDisplayProjection, so unset the
+        // hint to restore the natural state when the transaction is applied.
+        getSyncTransaction().unsetFixedTransformHint(mSurfaceControl);
 
-        if (shellTransitions) {
-            // Before setDisplayProjection is applied by the start transaction of transition,
-            // set the transform hint to avoid using surface in old rotation.
-            setFixedTransformHint(getPendingTransaction(), mSurfaceControl, rotation);
-            // The sync transaction should already contains setDisplayProjection, so unset the
-            // hint to restore the natural state when the transaction is applied.
-            transaction.unsetFixedTransformHint(mSurfaceControl);
-        }
         scheduleAnimation();
 
         mWmService.mRotationWatcherController.dispatchDisplayRotationChange(mDisplayId, rotation);
@@ -2870,8 +2855,7 @@
         // If the transition finished callback cannot match the token for some reason, make sure the
         // rotated state is cleared if it is already invisible.
         if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.isVisibleRequested()
-                && !mFixedRotationLaunchingApp.isVisible()
-                && !mDisplayRotation.isRotatingSeamlessly()) {
+                && !mFixedRotationLaunchingApp.isVisible()) {
             clearFixedRotationLaunchingApp();
         }
         // If there won't be a transition to notify the launch is done, then it should be ready to
@@ -3283,26 +3267,30 @@
                 /* inTopology= */ shouldShowContent);
     }
 
-     /**
-      * Whether the display is allowed to switch the content mode between extended and mirroring.
-      * If the content mode is extended, the display will start home activity and show system
-      * decorations, such as wallpapaer, status bar and navigation bar.
-      * If the content mode is mirroring, the display will not show home activity or system
-      * decorations.
-      * The content mode is switched when {@link Display#canHostTasks()} changes.
-      *
-      * Note that we only allow displays that are able to show system decorations to use the content
-      * mode switch; however, not all displays that are able to show system decorations are allowed
-      * to use the content mode switch.
-      */
-     boolean allowContentModeSwitch() {
+    /**
+     * Whether the display is allowed to switch the content mode between extended and mirroring.
+     * If the content mode is extended, the display will start home activity and show system
+     * decorations, such as wallpapaer, status bar and navigation bar.
+     * If the content mode is mirroring, the display will not show home activity or system
+     * decorations.
+     * The content mode is switched when {@link Display#canHostTasks()} changes.
+     *
+     * Note that we only allow displays that are able to show system decorations to use the content
+     * mode switch; however, not all displays that are able to show system decorations are allowed
+     * to use the content mode switch.
+     */
+    boolean allowContentModeSwitch() {
+        if ((mDisplay.getFlags() & FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0) {
+            return false;
+        }
+
         // The default display should always show system decorations.
         if (isDefaultDisplay) {
             return false;
         }
 
-        // Private display should never show system decorations.
-        if (isPrivate()) {
+        // Private or untrusted display should never show system decorations.
+        if (isPrivate() || !isTrusted()) {
             return false;
         }
 
@@ -3310,14 +3298,9 @@
             return false;
         }
 
-        // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH.
-        if ((mDisplay.getFlags() & Display.FLAG_REAR) != 0) {
-            return false;
-        }
-
-        // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_CONTENT_MODE_SWITCH.
-        // Virtual displays cannot add or remove system decorations during their lifecycle.
-        if (mDisplay.getType() == Display.TYPE_VIRTUAL) {
+        // Display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled should always show system
+        // decorations, and should not switch the content mode.
+        if ((mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
             return false;
         }
 
@@ -5072,7 +5055,7 @@
         }
 
         mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
-        if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
+        if (!inTransition()) {
             mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
                     mLastHasContent,
                     mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
@@ -5698,7 +5681,8 @@
             // This display is configured to show system decorations.
             return true;
         }
-        if (isPublicSecondaryDisplayWithDesktopModeForceEnabled()) {
+        if (!DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()
+                && isPublicSecondaryDisplayWithDesktopModeForceEnabled()) {
             if (com.android.window.flags.Flags.rearDisplayDisableForceDesktopSystemDecorations()) {
                 // System decorations should not be forced on a rear display due to security
                 // policies.
@@ -7101,9 +7085,14 @@
         }
 
         @Override
-        public void setAnimatingTypes(@InsetsType int animatingTypes) {
+        public void setAnimatingTypes(@InsetsType int animatingTypes,
+                @Nullable ImeTracker.Token statsToken) {
             if (mAnimatingTypes != animatingTypes) {
                 mAnimatingTypes = animatingTypes;
+
+                if (android.view.inputmethod.Flags.reportAnimatingInsetsTypes()) {
+                    getInsetsStateController().onAnimatingTypesChanged(this, statsToken);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 9cf792d..9edbb70 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -168,19 +168,6 @@
     private int mDeferredRotationPauseCount;
 
     /**
-     * A count of the windows which are 'seamlessly rotated', e.g. a surface at an old orientation
-     * is being transformed. We freeze orientation updates while any windows are seamlessly rotated,
-     * so we need to track when this hits zero so we can apply deferred orientation updates.
-     */
-    private int mSeamlessRotationCount;
-
-    /**
-     * True in the interval from starting seamless rotation until the last rotated window draws in
-     * the new orientation.
-     */
-    private boolean mRotatingSeamlessly;
-
-    /**
      * Behavior of rotation suggestions.
      *
      * @see Settings.Secure#SHOW_ROTATION_SUGGESTIONS
@@ -630,15 +617,6 @@
             return true;
         }
 
-        if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
-            // The screen rotation animation uses a screenshot to freeze the screen while windows
-            // resize underneath. When we are rotating seamlessly, we allow the elements to
-            // transition to their rotated state independently and without a freeze required.
-            prepareSeamlessRotation();
-        } else {
-            cancelSeamlessRotation();
-        }
-
         // Give a remote handler (system ui) some time to reposition things.
         startRemoteRotation(oldRotation, mRotation);
 
@@ -677,42 +655,6 @@
         }
     }
 
-    /**
-     * This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
-     * set by previous {@link #updateRotationUnchecked}, but another orientation change happens
-     * before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
-     * and it doesn't choose seamless rotation.
-     */
-    void cancelSeamlessRotation() {
-        if (!mRotatingSeamlessly) {
-            return;
-        }
-        mDisplayContent.forAllWindows(w -> {
-            if (w.mSeamlesslyRotated) {
-                w.cancelSeamlessRotation();
-                w.mSeamlesslyRotated = false;
-            }
-        }, true /* traverseTopToBottom */);
-        mSeamlessRotationCount = 0;
-        mRotatingSeamlessly = false;
-        mDisplayContent.finishAsyncRotationIfPossible();
-    }
-
-    private void prepareSeamlessRotation() {
-        // We are careful to reset this in case a window was removed before it finished
-        // seamless rotation.
-        mSeamlessRotationCount = 0;
-        mRotatingSeamlessly = true;
-    }
-
-    boolean isRotatingSeamlessly() {
-        return mRotatingSeamlessly;
-    }
-
-    boolean hasSeamlessRotatingWindow() {
-        return mSeamlessRotationCount > 0;
-    }
-
     @VisibleForTesting
     boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
         // Display doesn't need to be frozen because application has been started in correct
@@ -750,13 +692,6 @@
             return false;
         }
 
-        // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
-        // complete (that is, waiting for windows to redraw). It's tempting to check
-        // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
-        if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
-            return false;
-        }
-
         return true;
     }
 
@@ -774,28 +709,6 @@
         return oldRotation != Surface.ROTATION_180 && newRotation != Surface.ROTATION_180;
     }
 
-    void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
-        if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
-            return;
-        }
-
-        w.mSeamlesslyRotated = seamlesslyRotated;
-        if (seamlesslyRotated) {
-            mSeamlessRotationCount++;
-        } else {
-            mSeamlessRotationCount--;
-        }
-        if (mSeamlessRotationCount == 0) {
-            ProtoLog.i(WM_DEBUG_ORIENTATION,
-                    "Performing post-rotate rotation after seamless rotation");
-            // Finish seamless rotation.
-            mRotatingSeamlessly = false;
-            mDisplayContent.finishAsyncRotationIfPossible();
-
-            updateRotationAndSendNewConfigIfChanged();
-        }
-    }
-
     void restoreSettings(int userRotationMode, int userRotation, int fixedToUserRotation) {
         mFixedToUserRotation = fixedToUserRotation;
 
diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
index fa7a99d..d90fff2 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
@@ -40,14 +40,14 @@
     }
 
     int[] registerListener(IDisplayWindowListener listener) {
+        mDisplayListeners.register(listener);
+        final IntArray displayIds = new IntArray();
         synchronized (mService.mGlobalLock) {
-            mDisplayListeners.register(listener);
-            final IntArray displayIds = new IntArray();
             mService.mAtmService.mRootWindowContainer.forAllDisplays((displayContent) -> {
                 displayIds.add(displayContent.mDisplayId);
             });
-            return displayIds.toArray();
         }
+        return displayIds.toArray();
     }
 
     void unregisterListener(IDisplayWindowListener listener) {
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 7b6fc9e..64ae21d 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -239,9 +239,10 @@
 
     static class EmbeddedWindow implements InputTarget {
         final IBinder mClient;
-        @Nullable final WindowState mHostWindowState;
+        @Nullable WindowState mHostWindowState;
         @Nullable final ActivityRecord mHostActivityRecord;
-        final String mName;
+        String mName;
+        final String mInputHandleName;
         final int mOwnerUid;
         final int mOwnerPid;
         final WindowManagerService mWmService;
@@ -279,13 +280,12 @@
          * @param displayId used for focus requests
          */
         EmbeddedWindow(Session session, WindowManagerService service, IBinder clientToken,
-                       WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
-                       int displayId, InputTransferToken inputTransferToken, String inputHandleName,
-                       boolean isFocusable) {
+                       @Nullable WindowState hostWindowState, int ownerUid, int ownerPid,
+                       int windowType, int displayId, InputTransferToken inputTransferToken,
+                       String inputHandleName, boolean isFocusable) {
             mSession = session;
             mWmService = service;
             mClient = clientToken;
-            mHostWindowState = hostWindowState;
             mHostActivityRecord = (mHostWindowState != null) ? mHostWindowState.mActivityRecord
                     : null;
             mOwnerUid = ownerUid;
@@ -293,11 +293,9 @@
             mWindowType = windowType;
             mDisplayId = displayId;
             mInputTransferToken = inputTransferToken;
-            final String hostWindowName =
-                    (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
-                            : "";
             mIsFocusable = isFocusable;
-            mName = "Embedded{" + inputHandleName + hostWindowName + "}";
+            mInputHandleName = inputHandleName;
+            updateHost(hostWindowState);
         }
 
         @Override
@@ -486,5 +484,19 @@
             proto.end(token2);
             proto.end(token);
         }
+
+        public void updateHost(WindowState hostWindowState) {
+            if (mHostWindowState == hostWindowState && mName != null) {
+                return;
+            }
+
+            ProtoLog.d(WM_DEBUG_EMBEDDED_WINDOWS, "[%s] Updated host window from %s to %s",
+                    this, mHostWindowState, hostWindowState);
+            mHostWindowState = hostWindowState;
+            final String hostWindowName =
+                    (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
+                            : "";
+            mName = "Embedded{" + mInputHandleName + hostWindowName + "}";
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index a017a11..e508a6d 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -23,8 +23,6 @@
 import android.annotation.Nullable;
 import android.util.Slog;
 
-import com.android.window.flags.Flags;
-
 import java.util.ArrayList;
 
 /** Helper class to ensure activities are in the right visible state for a container. */
@@ -112,18 +110,11 @@
 
                 if (adjacentTaskFragments != null && adjacentTaskFragments.contains(
                         childTaskFragment)) {
-                    final boolean isTranslucent;
-                    if (Flags.allowMultipleAdjacentTaskFragments()) {
-                        isTranslucent = childTaskFragment.isTranslucent(starting)
-                                || childTaskFragment.forOtherAdjacentTaskFragments(
-                                        adjacentTaskFragment -> {
-                                            return adjacentTaskFragment.isTranslucent(starting);
-                                        });
-                    } else {
-                        isTranslucent = childTaskFragment.isTranslucent(starting)
-                                || childTaskFragment.getAdjacentTaskFragment()
-                                .isTranslucent(starting);
-                    }
+                    final boolean isTranslucent = childTaskFragment.isTranslucent(starting)
+                            || childTaskFragment.forOtherAdjacentTaskFragments(
+                                    adjacentTaskFragment -> {
+                                        return adjacentTaskFragment.isTranslucent(starting);
+                                    });
                     if (!isTranslucent) {
                         // Everything behind two adjacent TaskFragments are occluded.
                         mBehindFullyOccludedContainer = true;
@@ -135,14 +126,10 @@
                     if (adjacentTaskFragments == null) {
                         adjacentTaskFragments = new ArrayList<>();
                     }
-                    if (Flags.allowMultipleAdjacentTaskFragments()) {
-                        final ArrayList<TaskFragment> adjacentTfs = adjacentTaskFragments;
-                        childTaskFragment.forOtherAdjacentTaskFragments(adjacentTf -> {
-                            adjacentTfs.add(adjacentTf);
-                        });
-                    } else {
-                        adjacentTaskFragments.add(childTaskFragment.getAdjacentTaskFragment());
-                    }
+                    final ArrayList<TaskFragment> adjacentTfs = adjacentTaskFragments;
+                    childTaskFragment.forOtherAdjacentTaskFragments(adjacentTf -> {
+                        adjacentTfs.add(adjacentTf);
+                    });
                 }
             } else if (child.asActivityRecord() != null) {
                 setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity);
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 2cac63c..53681f9 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -82,6 +82,13 @@
      */
     private boolean mGivenInsetsReady = false;
 
+    /**
+     * The last state of the windowContainer. This is used to reset server visibility, in case of
+     * the IME (temporarily) redrawing  (e.g. during a rotation), to dispatch the control with
+     * leash again after it has finished drawing.
+     */
+    private boolean mLastDrawn = false;
+
     ImeInsetsSourceProvider(@NonNull InsetsSource source,
             @NonNull InsetsStateController stateController,
             @NonNull DisplayContent displayContent) {
@@ -97,6 +104,7 @@
             final WindowState ws =
                     mWindowContainer != null ? mWindowContainer.asWindowState() : null;
             final boolean givenInsetsPending = ws != null && ws.mGivenInsetsPending;
+            mLastDrawn = ws != null && ws.isDrawn();
 
             // isLeashReadyForDispatching (used to dispatch the leash of the control) is
             // depending on mGivenInsetsReady. Therefore, triggering notifyControlChanged here
@@ -158,6 +166,35 @@
         }
     }
 
+    /**
+     * This is used to determine the desired serverVisibility state. For the IME, just having a
+     * window state that would be visible by policy is not enough.
+     */
+    @Override
+    protected boolean isSurfaceVisible() {
+        final boolean isSurfaceVisible = super.isSurfaceVisible();
+        if (android.view.inputmethod.Flags.refactorInsetsController()) {
+            final WindowState windowState = mWindowContainer.asWindowState();
+            if (mControl != null && windowState != null) {
+                final boolean isDrawn = windowState.isDrawn();
+                if (!isServerVisible() && isSurfaceVisible) {
+                    // In case the IME becomes visible, we need to check if it is already drawn and
+                    // does not have given insets pending. If it's not yet drawn, we do not set
+                    // server visibility
+                    return isDrawn && !windowState.mGivenInsetsPending;
+                } else if (mLastDrawn && !isDrawn) {
+                    // If the IME was drawn before, but is not drawn anymore, we need to reset
+                    // server visibility, which will also reset {@link
+                    // ImeInsetsSourceProvider#mGivenInsetsReady}. Otherwise, the new control
+                    // with leash won't be dispatched after the surface has redrawn.
+                    return false;
+                }
+            }
+        }
+        return isSurfaceVisible;
+    }
+
+
     @Nullable
     @Override
     InsetsSourceControl getControl(InsetsControlTarget target) {
@@ -263,8 +300,8 @@
         boolean oldVisibility = mSource.isVisible();
         super.updateVisibility();
         if (Flags.refactorInsetsController()) {
-            if (mSource.isVisible() && !oldVisibility && mImeRequester != null) {
-                reportImeDrawnForOrganizerIfNeeded(mImeRequester);
+            if (mSource.isVisible() && !oldVisibility && mControlTarget != null) {
+                reportImeDrawnForOrganizerIfNeeded(mControlTarget);
             }
         }
         onSourceChanged();
@@ -284,15 +321,24 @@
         if (Flags.refactorInsetsController()) {
             // TODO(b/353463205) investigate if we should fail the statsToken, or if it's only
             //  temporary null.
-            if (target != null) {
+            if (target != null && target == mControlTarget) {
                 // If insets target is not available (e.g. RemoteInsetsControlTarget), use current
                 // IME input target to update IME request state. For example, switch from a task
                 // with showing IME to a split-screen task without showing IME.
-                InsetsTarget insetsTarget = target.getWindow();
-                if (insetsTarget == null && mServerVisible) {
-                    insetsTarget = mDisplayContent.getImeInputTarget();
+                InputTarget imeInputTarget = mDisplayContent.getImeInputTarget();
+                if (imeInputTarget != target && imeInputTarget != null) {
+                    // The controlTarget should be updated with the visibility of the
+                    // current IME input target.
+                    reportImeInputTargetStateToControlTarget(imeInputTarget, target,
+                            statsToken);
+                } else {
+                    invokeOnImeRequestedChangedListener(target, statsToken);
                 }
-                invokeOnImeRequestedChangedListener(insetsTarget, statsToken);
+            } else {
+                ProtoLog.w(WM_DEBUG_IME,
+                        "Not invoking onImeRequestedChangedListener, target=%s, current "
+                                + "controlTarget=%s",
+                        target, mControlTarget);
             }
         }
     }
@@ -328,8 +374,7 @@
             if (changed) {
                 ImeTracker.forLogging().onProgress(statsToken,
                         ImeTracker.PHASE_SERVER_UPDATE_CLIENT_VISIBILITY);
-                invokeOnImeRequestedChangedListener(mDisplayContent.getImeInputTarget(),
-                        statsToken);
+                invokeOnImeRequestedChangedListener(controlTarget, statsToken);
             } else {
                 // TODO(b/353463205) check cancelled / failed
                 ImeTracker.forLogging().onCancelled(statsToken,
@@ -383,7 +428,8 @@
                 // not all virtual displays have an ImeInsetsSourceProvider, so it is not
                 // guaranteed that the IME will be started when the control target reports its
                 // requested visibility back. Thus, invoking the listener here.
-                invokeOnImeRequestedChangedListener(imeInsetsTarget, statsToken);
+                invokeOnImeRequestedChangedListener((InsetsControlTarget) imeInsetsTarget,
+                        statsToken);
             } else {
                 ImeTracker.forLogging().onFailed(statsToken,
                         ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
@@ -392,18 +438,21 @@
     }
 
     // TODO(b/353463205) check callers to see if we can make statsToken @NonNull
-    private void invokeOnImeRequestedChangedListener(InsetsTarget insetsTarget,
+    private void invokeOnImeRequestedChangedListener(InsetsControlTarget controlTarget,
             @Nullable ImeTracker.Token statsToken) {
         final var imeListener = mDisplayContent.mWmService.mOnImeRequestedChangedListener;
         if (imeListener != null) {
-            if (insetsTarget != null) {
+            if (controlTarget != null) {
+                final boolean imeAnimating = Flags.reportAnimatingInsetsTypes()
+                        && (controlTarget.getAnimatingTypes() & WindowInsets.Type.ime()) != 0;
                 ImeTracker.forLogging().onProgress(statsToken,
                         ImeTracker.PHASE_WM_POSTING_CHANGED_IME_VISIBILITY);
                 mDisplayContent.mWmService.mH.post(() -> {
                     ImeTracker.forLogging().onProgress(statsToken,
                             ImeTracker.PHASE_WM_INVOKING_IME_REQUESTED_LISTENER);
-                    imeListener.onImeRequestedChanged(insetsTarget.getWindowToken(),
-                            insetsTarget.isRequestedVisible(WindowInsets.Type.ime()), statsToken);
+                    imeListener.onImeRequestedChanged(controlTarget.getWindowToken(),
+                            controlTarget.isRequestedVisible(WindowInsets.Type.ime())
+                                    || imeAnimating, statsToken);
                 });
             } else {
                 ImeTracker.forLogging().onFailed(statsToken,
@@ -416,6 +465,26 @@
         }
     }
 
+    @Override
+    void onAnimatingTypesChanged(InsetsControlTarget caller,
+            @Nullable ImeTracker.Token statsToken) {
+        if (Flags.reportAnimatingInsetsTypes()) {
+            final InsetsControlTarget controlTarget = getControlTarget();
+            // If the IME is not being requested anymore and the animation is finished, we need to
+            // invoke the listener, to let IMS eventually know
+            if (caller != null && caller == controlTarget && !caller.isRequestedVisible(
+                    WindowInsets.Type.ime())
+                    && (caller.getAnimatingTypes() & WindowInsets.Type.ime()) == 0) {
+                ImeTracker.forLogging().onFailed(statsToken,
+                        ImeTracker.PHASE_WM_NOTIFY_HIDE_ANIMATION_FINISHED);
+                invokeOnImeRequestedChangedListener(caller, statsToken);
+            } else {
+                ImeTracker.forLogging().onCancelled(statsToken,
+                        ImeTracker.PHASE_WM_NOTIFY_HIDE_ANIMATION_FINISHED);
+            }
+        }
+    }
+
     private void reportImeDrawnForOrganizerIfNeeded(@NonNull InsetsControlTarget caller) {
         final WindowState callerWindow = caller.getWindow();
         if (callerWindow == null) {
@@ -757,6 +826,8 @@
         pw.print(prefix);
         pw.print("mImeShowing=");
         pw.print(mImeShowing);
+        pw.print(" mLastDrawn=");
+        pw.print(mLastDrawn);
         if (mImeRequester != null) {
             pw.print(prefix);
             pw.print("showImePostLayout pending for mImeRequester=");
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 7751ac3..a4bc5cb 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -343,6 +343,13 @@
         }
     }
 
+    @Override
+    public boolean isKeyguardLocked(int displayId) {
+        synchronized (mService.mGlobalLock) {
+            return mService.mAtmService.mKeyguardController.isKeyguardLocked(displayId);
+        }
+    }
+
     /** Waits until the built-in input devices have been configured. */
     public boolean waitForInputDevicesReady(long timeoutMillis) {
         synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index 6462a37..3c248fb 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -107,8 +107,10 @@
 
     /**
      * @param animatingTypes the {@link InsetsType}s, that are currently animating
+     * @param statsToken the token tracking the current IME request or {@code null} otherwise.
      */
-    default void setAnimatingTypes(@InsetsType int animatingTypes) {
+    default void setAnimatingTypes(@InsetsType int animatingTypes,
+            @Nullable ImeTracker.Token statsToken) {
     }
 
     /** Returns {@code target.getWindow()}, or null if {@code target} is {@code null}. */
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index b748902..3b715d6 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -176,6 +176,16 @@
     }
 
     /**
+     * @return Whether the current window container has a visible surface.
+     */
+    protected boolean isSurfaceVisible() {
+        final WindowState windowState = mWindowContainer.asWindowState();
+        return windowState != null
+                ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
+                : mWindowContainer.isVisibleRequested();
+    }
+
+    /**
      * Updates the window container that currently backs this source.
      *
      * @param windowContainer The window container that links to this source.
@@ -368,20 +378,9 @@
         if (mWindowContainer == null) {
             return;
         }
-        WindowState windowState = mWindowContainer.asWindowState();
-        boolean isServerVisible = windowState != null
-                ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
-                : mWindowContainer.isVisibleRequested();
+        final WindowState windowState = mWindowContainer.asWindowState();
+        final boolean isServerVisible = isSurfaceVisible();
 
-        if (android.view.inputmethod.Flags.refactorInsetsController()) {
-            if (mControl != null && mControl.getType() == WindowInsets.Type.ime() && !mServerVisible
-                    && isServerVisible && windowState != null) {
-                // in case the IME becomes visible, we need to check if it is already drawn and
-                // does not have given insets pending. If it's not yet drawn, we do not set
-                // server visibility
-                isServerVisible = windowState.isDrawn() && !windowState.mGivenInsetsPending;
-            }
-        }
         final boolean serverVisibleChanged = mServerVisible != isServerVisible;
         setServerVisible(isServerVisible);
         if (mControl != null && mControlTarget != null) {
@@ -673,6 +672,10 @@
                 mServerVisible, mClientVisible);
     }
 
+    void onAnimatingTypesChanged(InsetsControlTarget caller,
+            @Nullable ImeTracker.Token statsToken) {
+    }
+
     protected boolean isLeashReadyForDispatching() {
         return isLeashInitialized();
     }
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 5e0395f..64fe669 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -393,6 +393,15 @@
         }
     }
 
+    void onAnimatingTypesChanged(InsetsControlTarget target,
+            @Nullable ImeTracker.Token statsToken) {
+        for (int i = mProviders.size() - 1; i >= 0; i--) {
+            final InsetsSourceProvider provider = mProviders.valueAt(i);
+            final boolean isImeProvider = provider.getSource().getType() == WindowInsets.Type.ime();
+            provider.onAnimatingTypesChanged(target, isImeProvider ? statsToken : null);
+        }
+    }
+
     private void notifyPendingInsetsControlChanged() {
         if (mPendingTargetProvidersMap.isEmpty()) {
             return;
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index fa65bda..2406178 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -26,6 +26,7 @@
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.WindowConfiguration.WindowingMode;
@@ -36,6 +37,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between
@@ -96,18 +98,18 @@
             mTmpResult.reset();
             final LaunchParamsModifier modifier = mModifiers.get(i);
 
-            switch(modifier.onCalculate(task, layout, activity, source, options, request, phase,
+            switch (modifier.onCalculate(task, layout, activity, source, options, request, phase,
                     mTmpCurrent, mTmpResult)) {
                 case RESULT_SKIP:
                     // Do not apply any results when we are told to skip
                     continue;
                 case RESULT_DONE:
                     // Set result and return immediately.
-                    result.set(mTmpResult);
+                    result.merge(mTmpResult);
                     return;
                 case RESULT_CONTINUE:
                     // Set result and continue
-                    result.set(mTmpResult);
+                    result.merge(mTmpResult);
                     break;
             }
         }
@@ -138,6 +140,10 @@
         mService.deferWindowLayout();
         try {
             if (task.getRootTask().inMultiWindowMode()) {
+                if (!mTmpParams.mAppBounds.isEmpty()) {
+                    task.getRequestedOverrideConfiguration().windowConfiguration.setAppBounds(
+                            mTmpParams.mAppBounds);
+                }
                 task.setBounds(mTmpParams.mBounds);
                 return true;
             }
@@ -168,7 +174,11 @@
      */
     static class LaunchParams {
         /** The bounds within the parent container. */
+        @NonNull
         final Rect mBounds = new Rect();
+        /** The bounds within the parent container respecting insets. Usually empty. */
+        @NonNull
+        final Rect mAppBounds = new Rect();
 
         /** The display area the {@link Task} would prefer to be on. */
         @Nullable
@@ -178,24 +188,45 @@
         @WindowingMode
         int mWindowingMode;
 
+        /** Whether the Activity needs the safe region bounds. A {@code null} value means unset. */
+        @Nullable
+        Boolean mNeedsSafeRegionBounds = null;
+
         /** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */
         void reset() {
             mBounds.setEmpty();
+            mAppBounds.setEmpty();
             mPreferredTaskDisplayArea = null;
             mWindowingMode = WINDOWING_MODE_UNDEFINED;
+            mNeedsSafeRegionBounds = null;
         }
 
         /** Copies the values set on the passed in {@link LaunchParams}. */
         void set(LaunchParams params) {
             mBounds.set(params.mBounds);
+            mAppBounds.set(params.mAppBounds);
             mPreferredTaskDisplayArea = params.mPreferredTaskDisplayArea;
             mWindowingMode = params.mWindowingMode;
+            mNeedsSafeRegionBounds = params.mNeedsSafeRegionBounds;
+        }
+
+        /** Merges the values set on the passed in {@link LaunchParams}. */
+        void merge(LaunchParams params) {
+            mBounds.set(params.mBounds);
+            mAppBounds.set(params.mAppBounds);
+            mPreferredTaskDisplayArea = params.mPreferredTaskDisplayArea;
+            mWindowingMode = params.mWindowingMode;
+            // Only update mNeedsSafeRegionBounds if a modifier updates it by setting a non null
+            // value. Otherwise, carry over from previous modifiers
+            if (params.mNeedsSafeRegionBounds != null) {
+                mNeedsSafeRegionBounds = params.mNeedsSafeRegionBounds;
+            }
         }
 
         /** Returns {@code true} if no values have been explicitly set. */
         boolean isEmpty() {
-            return mBounds.isEmpty() && mPreferredTaskDisplayArea == null
-                    && mWindowingMode == WINDOWING_MODE_UNDEFINED;
+            return mBounds.isEmpty() && mAppBounds.isEmpty() && mPreferredTaskDisplayArea == null
+                    && mWindowingMode == WINDOWING_MODE_UNDEFINED && mNeedsSafeRegionBounds == null;
         }
 
         boolean hasWindowingMode() {
@@ -215,15 +246,20 @@
 
             if (mPreferredTaskDisplayArea != that.mPreferredTaskDisplayArea) return false;
             if (mWindowingMode != that.mWindowingMode) return false;
-            return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null;
+            if (!mAppBounds.equals(that.mAppBounds)) return false;
+            if (!Objects.equals(mNeedsSafeRegionBounds, that.mNeedsSafeRegionBounds)) return false;
+            return !mBounds.isEmpty() ? mBounds.equals(that.mBounds) : that.mBounds.isEmpty();
         }
 
         @Override
         public int hashCode() {
-            int result = mBounds != null ? mBounds.hashCode() : 0;
+            int result = !mBounds.isEmpty() ? mBounds.hashCode() : 0;
+            result = 31 * result + mAppBounds.hashCode();
             result = 31 * result + (mPreferredTaskDisplayArea != null
                     ? mPreferredTaskDisplayArea.hashCode() : 0);
             result = 31 * result + mWindowingMode;
+            result = 31 * result + (mNeedsSafeRegionBounds != null
+                    ? Boolean.hashCode(mNeedsSafeRegionBounds) : 0);
             return result;
         }
     }
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 6dd7d35..6e59828 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -21,20 +21,13 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import android.app.PictureInPictureParams;
 import android.content.res.Resources;
-import android.graphics.Insets;
-import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.RotationUtils;
 import android.util.Slog;
 import android.view.IPinnedTaskListener;
-import android.view.Surface;
-import android.view.SurfaceControl;
-import android.window.PictureInPictureSurfaceTransaction;
 
 import java.io.PrintWriter;
 
@@ -71,11 +64,7 @@
      * based on the new rotation.
      */
     private Rect mDestRotatedBounds;
-    /**
-     * Non-null if the entering PiP task from recents animation will cause display rotation to
-     * change. The transaction is based on the old rotation.
-     */
-    private PictureInPictureSurfaceTransaction mPipTransaction;
+
     /** Whether to skip task configuration change once. */
     private boolean mFreezingTaskConfig;
     /** Defer display orientation change if the PiP task is animating across orientations. */
@@ -212,14 +201,12 @@
     }
 
     /**
-     * Sets the transaction for {@link #startSeamlessRotationIfNeeded} if the orientation of display
-     * will be changed. This is only called when finishing recents animation with pending
-     * orientation change that will be handled by
-     * {@link DisplayContent.FixedRotationTransitionListener#onFinishRecentsAnimation}.
+     * Sets a hint if the orientation of display will be changed. This is only called when
+     * finishing recents animation with pending orientation change that will be handled by
+     * {@link DisplayContent.FixedRotationTransitionListener}.
      */
-    void setEnterPipTransaction(PictureInPictureSurfaceTransaction tx) {
+    void setEnterPipWithRotatedTransientLaunch() {
         mFreezingTaskConfig = true;
-        mPipTransaction = tx;
     }
 
     /** Called when the activity in PiP task has PiP windowing mode (at the end of animation). */
@@ -233,81 +220,6 @@
     }
 
     /**
-     * Resets rotation and applies scale and position to PiP task surface to match the current
-     * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it
-     * receives the callback of fixed rotation completion.
-     */
-    void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t,
-            int oldRotation, int newRotation) {
-        final Rect bounds = mDestRotatedBounds;
-        final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
-        final boolean emptyPipPositionTx = pipTx == null || pipTx.mPosition == null;
-        if (bounds == null && emptyPipPositionTx) {
-            return;
-        }
-        final TaskDisplayArea taskArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final Task pinnedTask = taskArea.getRootPinnedTask();
-        if (pinnedTask == null) {
-            return;
-        }
-
-        mDestRotatedBounds = null;
-        mPipTransaction = null;
-        final Rect areaBounds = taskArea.getBounds();
-        if (!emptyPipPositionTx) {
-            // The transaction from recents animation is in old rotation. So the position needs to
-            // be rotated.
-            float dx = pipTx.mPosition.x;
-            float dy = pipTx.mPosition.y;
-            final Matrix matrix = pipTx.getMatrix();
-            if (pipTx.mRotation == 90) {
-                dx = pipTx.mPosition.y;
-                dy = areaBounds.right - pipTx.mPosition.x;
-                matrix.postRotate(-90);
-            } else if (pipTx.mRotation == -90) {
-                dx = areaBounds.bottom - pipTx.mPosition.y;
-                dy = pipTx.mPosition.x;
-                matrix.postRotate(90);
-            }
-            matrix.postTranslate(dx, dy);
-            final SurfaceControl leash = pinnedTask.getSurfaceControl();
-            t.setMatrix(leash, matrix, new float[9]);
-            if (pipTx.hasCornerRadiusSet()) {
-                t.setCornerRadius(leash, pipTx.mCornerRadius);
-            }
-            Slog.i(TAG, "Seamless rotation PiP tx=" + pipTx + " pos=" + dx + "," + dy);
-            return;
-        }
-
-        final PictureInPictureParams params = pinnedTask.getPictureInPictureParams();
-        final Rect sourceHintRect = params != null && params.hasSourceBoundsHint()
-                ? params.getSourceRectHint()
-                : null;
-        Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect);
-        final int rotationDelta = RotationUtils.deltaRotation(oldRotation, newRotation);
-        // Adjust for display cutout if applicable.
-        if (sourceHintRect != null && rotationDelta == Surface.ROTATION_270) {
-            if (pinnedTask.getDisplayCutoutInsets() != null) {
-                final int rotationBackDelta = RotationUtils.deltaRotation(newRotation, oldRotation);
-                final Rect displayCutoutInsets = RotationUtils.rotateInsets(
-                        Insets.of(pinnedTask.getDisplayCutoutInsets()), rotationBackDelta).toRect();
-                sourceHintRect.offset(displayCutoutInsets.left, displayCutoutInsets.top);
-            }
-        }
-        final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect)
-                ? sourceHintRect : areaBounds;
-        final int w = contentBounds.width();
-        final int h = contentBounds.height();
-        final float scale = w <= h ? (float) bounds.width() / w : (float) bounds.height() / h;
-        final int insetLeft = (int) ((contentBounds.left - areaBounds.left) * scale + .5f);
-        final int insetTop = (int) ((contentBounds.top - areaBounds.top) * scale + .5f);
-        final Matrix matrix = new Matrix();
-        matrix.setScale(scale, scale);
-        matrix.postTranslate(bounds.left - insetLeft, bounds.top - insetTop);
-        t.setMatrix(pinnedTask.getSurfaceControl(), matrix, new float[9]);
-    }
-
-    /**
      * Returns {@code true} to skip {@link Task#onConfigurationChanged} because it is expected that
      * there will be a orientation change and a PiP configuration change.
      */
@@ -321,7 +233,6 @@
         mFreezingTaskConfig = false;
         mDeferOrientationChanging = false;
         mDestRotatedBounds = null;
-        mPipTransaction = null;
     }
 
     /**
@@ -381,9 +292,6 @@
         if (mDestRotatedBounds != null) {
             pw.println(prefix + "  mPendingBounds=" + mDestRotatedBounds);
         }
-        if (mPipTransaction != null) {
-            pw.println(prefix + "  mPipTransaction=" + mPipTransaction);
-        }
         pw.println(prefix + "  mIsImeShowing=" + mIsImeShowing);
         pw.println(prefix + "  mImeHeight=" + mImeHeight);
         pw.println(prefix + "  mMinAspectRatio=" + mMinAspectRatio);
diff --git a/services/core/java/com/android/server/wm/PointerEventDispatcher.java b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
index 4f8ec63..be259ec 100644
--- a/services/core/java/com/android/server/wm/PointerEventDispatcher.java
+++ b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
@@ -80,7 +80,7 @@
     public void unregisterInputEventListener(PointerEventListener listener) {
         synchronized (mListeners) {
             if (!mListeners.contains(listener)) {
-                throw new IllegalStateException("registerInputEventListener: " + listener +
+                throw new IllegalStateException("unregisterInputEventListener: " + listener +
                         " not registered.");
             }
             mListeners.remove(listener);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index cf201c9..39d1062 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -79,8 +79,6 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -655,9 +653,6 @@
         final int count = mChildren.size();
         for (int i = 0; i < count; ++i) {
             final int pendingChanges = mChildren.get(i).pendingLayoutChanges;
-            if ((pendingChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
-                animator.mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
-            }
             if (pendingChanges != 0) {
                 hasChanges = true;
             }
@@ -858,7 +853,8 @@
 
         // Post these on a handler such that we don't call into power manager service while
         // holding the window manager lock to avoid lock contention with power manager lock.
-        mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, mDisplayBrightnessOverrides)
+        // Send a copy of the brightness overrides as they may be cleared before being sent out.
+        mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, mDisplayBrightnessOverrides.clone())
                 .sendToTarget();
         mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
 
@@ -1024,18 +1020,6 @@
         return changed;
     }
 
-    boolean copyAnimToLayoutParams() {
-        boolean doRequest = false;
-
-        final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams;
-        if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
-            mUpdateRotation = true;
-            doRequest = true;
-        }
-
-        return doRequest;
-    }
-
     private final class MyHandler extends Handler {
 
         public MyHandler(Looper looper) {
@@ -1732,26 +1716,14 @@
                     activityAssistInfos.clear();
                     activityAssistInfos.add(new ActivityAssistInfo(top));
                     // Check if the activity on the split screen.
-                    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));
-                            }
+                    top.getTask().forOtherAdjacentTasks(task -> {
+                        final ActivityRecord adjacentActivityRecord =
+                                task.getTopNonFinishingActivity();
+                        if (adjacentActivityRecord != null) {
+                            activityAssistInfos.add(
+                                    new ActivityAssistInfo(adjacentActivityRecord));
                         }
-                    }
+                    });
                     if (rootTask == topFocusedRootTask) {
                         topVisibleActivities.addAll(0, activityAssistInfos);
                     } else {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 3ed16db..c5b47f9 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -738,12 +738,18 @@
     }
 
     @Override
-    public void updateAnimatingTypes(IWindow window, @InsetsType int animatingTypes) {
+    public void updateAnimatingTypes(IWindow window, @InsetsType int animatingTypes,
+            @Nullable ImeTracker.Token statsToken) {
         synchronized (mService.mGlobalLock) {
             final WindowState win = mService.windowForClientLocked(this, window,
                     false /* throwOnError */);
             if (win != null) {
-                win.setAnimatingTypes(animatingTypes);
+                ImeTracker.forLogging().onProgress(statsToken,
+                        ImeTracker.PHASE_WM_UPDATE_ANIMATING_TYPES);
+                win.setAnimatingTypes(animatingTypes, statsToken);
+            } else {
+                ImeTracker.forLogging().onFailed(statsToken,
+                        ImeTracker.PHASE_WM_UPDATE_ANIMATING_TYPES);
             }
         }
     }
@@ -910,10 +916,16 @@
             int privateFlags, int inputFeatures, int type, IBinder windowToken,
             InputTransferToken inputTransferToken, String inputHandleName,
             InputChannel outInputChannel) {
-        if (hostInputTransferToken == null && !mCanAddInternalSystemWindow) {
-            // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
-            // embedded windows without providing a host window input token
-            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+        if (!Flags.updateHostInputTransferToken()) {
+            // This is not a valid security check, callers can pass in a bogus token. If the
+            // token is not known to wm, then input APIs is request focus or transferTouchGesture
+            // will fail. Removing this check allows SCVH to be created before associating with a
+            // host window.
+            if (hostInputTransferToken == null && !mCanAddInternalSystemWindow) {
+                // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
+                // embedded windows without providing a host window input token
+                throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+            }
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -928,12 +940,14 @@
     }
 
     @Override
-    public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
+    public void updateInputChannel(IBinder channelToken,
+            @Nullable InputTransferToken hostInputTransferToken,
+            int displayId, SurfaceControl surface,
             int flags, int privateFlags, int inputFeatures, Region region) {
         final long identity = Binder.clearCallingIdentity();
         try {
-            mService.updateInputChannel(channelToken, displayId, surface, flags,
-                    mCanAddInternalSystemWindow ? privateFlags : 0, inputFeatures, region);
+            mService.updateInputChannel(channelToken, hostInputTransferToken, displayId, surface,
+                    flags, mCanAddInternalSystemWindow ? privateFlags : 0, inputFeatures, region);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 22f0278..8587b5a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2461,21 +2461,6 @@
         return parentTask == null ? null : parentTask.getCreatedByOrganizerTask();
     }
 
-    /** @deprecated b/373709676 replace with {@link #forOtherAdjacentTasks(Consumer)} ()}. */
-    @Deprecated
-    @Nullable
-    Task getAdjacentTask() {
-        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() {
@@ -2499,11 +2484,6 @@
      * 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;
@@ -2521,10 +2501,6 @@
      * 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;
@@ -3651,20 +3627,13 @@
                 final TaskFragment taskFragment = wc.asTaskFragment();
                 if (taskFragment != null && taskFragment.isEmbedded()
                         && taskFragment.hasAdjacentTaskFragment()) {
-                    if (Flags.allowMultipleAdjacentTaskFragments()) {
-                        final int[] nextLayer = { layer };
-                        taskFragment.forOtherAdjacentTaskFragments(adjacentTf -> {
-                            if (adjacentTf.shouldBoostDimmer()) {
-                                adjacentTf.assignLayer(t, nextLayer[0]++);
-                            }
-                        });
-                        layer = nextLayer[0];
-                    } else {
-                        final TaskFragment adjacentTf = taskFragment.getAdjacentTaskFragment();
+                    final int[] nextLayer = { layer };
+                    taskFragment.forOtherAdjacentTaskFragments(adjacentTf -> {
                         if (adjacentTf.shouldBoostDimmer()) {
-                            adjacentTf.assignLayer(t, layer++);
+                            adjacentTf.assignLayer(t, nextLayer[0]++);
                         }
-                    }
+                    });
+                    layer = nextLayer[0];
                 }
 
                 // Place the decor surface just above the owner TaskFragment.
@@ -3862,10 +3831,11 @@
         pw.print(ActivityInfo.resizeModeToString(mResizeMode));
         pw.print(" mSupportsPictureInPicture="); pw.print(mSupportsPictureInPicture);
         pw.print(" isResizeable="); pw.println(isResizeable());
-        pw.print(" isPerceptible="); pw.println(mIsPerceptible);
+        pw.print(prefix); pw.print("isPerceptible="); pw.println(mIsPerceptible);
         pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
         pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
-        pw.print(prefix); pw.println(" isTrimmable=" + mIsTrimmableFromRecents);
+        pw.print(prefix); pw.print("isTrimmable=" + mIsTrimmableFromRecents);
+        pw.print(" isForceHidden="); pw.println(isForceHidden());
         if (mLaunchAdjacentDisabled) {
             pw.println(prefix + "mLaunchAdjacentDisabled=true");
         }
@@ -4497,7 +4467,7 @@
     }
 
     void onPictureInPictureParamsChanged() {
-        if (inPinnedWindowingMode() || Flags.enableDesktopWindowingPip()) {
+        if (inPinnedWindowingMode() || DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
             dispatchTaskInfoChangedIfNeeded(true /* force */);
         }
     }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 1966ecf..fb7bab4 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -60,7 +60,6 @@
 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;
@@ -1089,19 +1088,14 @@
         // Use launch-adjacent-flag-root if launching with launch-adjacent flag.
         if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
                 && mLaunchAdjacentFlagRootTask != null) {
-            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();
-            }
+            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;
+            });
+            final Task launchAdjacentRootAdjacentTask = tmpTask[0];
             if (sourceTask != null && (sourceTask == candidateTask
                     || sourceTask.topRunningActivity() == null)) {
                 // Do nothing when task that is getting opened is same as the source or when
@@ -1129,14 +1123,6 @@
                 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)) {
@@ -1163,24 +1149,16 @@
                     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];
+                final Task[] adjacentRootTask = new Task[1];
+                sourceTask.forOtherAdjacentTasks(task -> {
+                    if (candidateTask == task || candidateTask.isDescendantOf(task)) {
+                        adjacentRootTask[0] = task;
+                        return true;
                     }
-                } else {
-                    final Task adjacentTarget = taskWithAdjacent.getAdjacentTask();
-                    if (candidateTask == adjacentTarget
-                            || candidateTask.isDescendantOf(adjacentTarget)) {
-                        return adjacentTarget;
-                    }
+                    return false;
+                });
+                if (adjacentRootTask[0] != null) {
+                    return adjacentRootTask[0];
                 }
                 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 5183c6b..70dabf8 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -24,7 +24,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -235,11 +234,6 @@
     /** This task fragment will be removed when the cleanup of its children are done. */
     private boolean mIsRemovalRequested;
 
-    /** @deprecated b/373709676 replace with {@link #mAdjacentTaskFragments} */
-    @Deprecated
-    @Nullable
-    private TaskFragment mAdjacentTaskFragment;
-
     /**
      * The TaskFragments that are adjacent to each other, including this TaskFragment.
      * All TaskFragments in this set share the same set instance.
@@ -455,22 +449,6 @@
         return service.mWindowOrganizerController.getTaskFragment(token);
     }
 
-    /** @deprecated b/373709676 replace with {@link #setAdjacentTaskFragments}. */
-    @Deprecated
-    void setAdjacentTaskFragment(@NonNull TaskFragment taskFragment) {
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            if (mAdjacentTaskFragment == taskFragment) {
-                return;
-            }
-            resetAdjacentTaskFragment();
-            mAdjacentTaskFragment = taskFragment;
-            taskFragment.setAdjacentTaskFragment(this);
-            return;
-        }
-
-        setAdjacentTaskFragments(new AdjacentSet(this, taskFragment));
-    }
-
     void setAdjacentTaskFragments(@NonNull AdjacentSet adjacentTaskFragments) {
         adjacentTaskFragments.setAsAdjacent();
     }
@@ -483,56 +461,18 @@
         return mCompanionTaskFragment;
     }
 
-    /** @deprecated b/373709676 replace with {@link #clearAdjacentTaskFragments()}. */
-    @Deprecated
-    private void resetAdjacentTaskFragment() {
-        if (Flags.allowMultipleAdjacentTaskFragments()) {
-            throw new IllegalStateException("resetAdjacentTaskFragment shouldn't be called when"
-                    + " allowMultipleAdjacentTaskFragments is enabled. Use either"
-                    + " #clearAdjacentTaskFragments or #removeFromAdjacentTaskFragments");
-        }
-        // Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment.
-        if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
-            mAdjacentTaskFragment.mAdjacentTaskFragment = null;
-            mAdjacentTaskFragment.mDelayLastActivityRemoval = false;
-        }
-        mAdjacentTaskFragment = null;
-        mDelayLastActivityRemoval = false;
-    }
-
     void clearAdjacentTaskFragments() {
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            resetAdjacentTaskFragment();
-            return;
-        }
-
         if (mAdjacentTaskFragments != null) {
             mAdjacentTaskFragments.clear();
         }
     }
 
     void removeFromAdjacentTaskFragments() {
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            resetAdjacentTaskFragment();
-            return;
-        }
-
         if (mAdjacentTaskFragments != null) {
             mAdjacentTaskFragments.remove(this);
         }
     }
 
-    /** @deprecated b/373709676 replace with {@link #getAdjacentTaskFragments()}. */
-    @Deprecated
-    @Nullable
-    TaskFragment getAdjacentTaskFragment() {
-        if (Flags.allowMultipleAdjacentTaskFragments()) {
-            throw new IllegalStateException("allowMultipleAdjacentTaskFragments is enabled. "
-                    + "Use #getAdjacentTaskFragments instead");
-        }
-        return mAdjacentTaskFragment;
-    }
-
     @Nullable
     AdjacentSet getAdjacentTaskFragments() {
         return mAdjacentTaskFragments;
@@ -561,16 +501,10 @@
     }
 
     boolean hasAdjacentTaskFragment() {
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            return mAdjacentTaskFragment != null;
-        }
         return mAdjacentTaskFragments != null;
     }
 
     boolean isAdjacentTo(@NonNull TaskFragment other) {
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            return mAdjacentTaskFragment == other;
-        }
         return other != this
                 && mAdjacentTaskFragments != null
                 && mAdjacentTaskFragments.contains(other);
@@ -1377,21 +1311,12 @@
                         if (taskFragment.isAdjacentTo(this)) {
                             continue;
                         }
-                        if (Flags.allowMultipleAdjacentTaskFragments()) {
-                            final boolean isOccluding = mTmpRect.intersect(taskFragment.getBounds())
-                                    || taskFragment.forOtherAdjacentTaskFragments(adjacentTf -> {
-                                        return mTmpRect.intersect(adjacentTf.getBounds());
-                                    });
-                            if (isOccluding) {
-                                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
-                            }
-                        } else {
-                            final TaskFragment adjacentTaskFragment =
-                                    taskFragment.mAdjacentTaskFragment;
-                            if (mTmpRect.intersect(taskFragment.getBounds())
-                                    || mTmpRect.intersect(adjacentTaskFragment.getBounds())) {
-                                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
-                            }
+                        final boolean isOccluding = mTmpRect.intersect(taskFragment.getBounds())
+                                || taskFragment.forOtherAdjacentTaskFragments(adjacentTf -> {
+                                    return mTmpRect.intersect(adjacentTf.getBounds());
+                                });
+                        if (isOccluding) {
+                            return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
                         }
                     }
                 }
@@ -1427,37 +1352,22 @@
                 // 2. Adjacent TaskFragments do not overlap, so that if this TaskFragment is behind
                 // any translucent TaskFragment in the adjacent set, then this TaskFragment is
                 // visible behind translucent.
-                if (Flags.allowMultipleAdjacentTaskFragments()) {
-                    final boolean hasTraversedAdj = otherTaskFrag.forOtherAdjacentTaskFragments(
-                            adjacentTaskFragments::contains);
-                    if (hasTraversedAdj) {
-                        final boolean isTranslucent =
-                                isBehindTransparentTaskFragment(otherTaskFrag, starting)
-                                || otherTaskFrag.forOtherAdjacentTaskFragments(
-                                        (Predicate<TaskFragment>) tf ->
-                                                isBehindTransparentTaskFragment(tf, starting));
-                        if (isTranslucent) {
-                            // Can be visible behind a translucent adjacent TaskFragments.
-                            gotTranslucentFullscreen = true;
-                            gotTranslucentAdjacent = true;
-                            continue;
-                        }
-                        // Can not be visible behind adjacent TaskFragments.
-                        return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+                final boolean hasTraversedAdj = otherTaskFrag.forOtherAdjacentTaskFragments(
+                        adjacentTaskFragments::contains);
+                if (hasTraversedAdj) {
+                    final boolean isTranslucent =
+                            isBehindTransparentTaskFragment(otherTaskFrag, starting)
+                                    || otherTaskFrag.forOtherAdjacentTaskFragments(
+                                    (Predicate<TaskFragment>) tf ->
+                                            isBehindTransparentTaskFragment(tf, starting));
+                    if (isTranslucent) {
+                        // Can be visible behind a translucent adjacent TaskFragments.
+                        gotTranslucentFullscreen = true;
+                        gotTranslucentAdjacent = true;
+                        continue;
                     }
-                } else {
-                    if (adjacentTaskFragments.contains(otherTaskFrag.mAdjacentTaskFragment)) {
-                        if (isBehindTransparentTaskFragment(otherTaskFrag, starting)
-                                || isBehindTransparentTaskFragment(
-                                        otherTaskFrag.mAdjacentTaskFragment, starting)) {
-                            // Can be visible behind a translucent adjacent TaskFragments.
-                            gotTranslucentFullscreen = true;
-                            gotTranslucentAdjacent = true;
-                            continue;
-                        }
-                        // Can not be visible behind adjacent TaskFragments.
-                        return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
-                    }
+                    // Can not be visible behind adjacent TaskFragments.
+                    return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
                 }
                 adjacentTaskFragments.add(otherTaskFrag);
             }
@@ -2423,15 +2333,24 @@
         @Nullable DisplayInfo mTmpOverrideDisplayInfo;
         @Nullable AppCompatDisplayInsets mTmpCompatInsets;
         @Nullable Rect mParentAppBoundsOverride;
+        @Nullable Rect mParentBoundsOverride;
         int mTmpOverrideConfigOrientation;
         boolean mUseOverrideInsetsForConfig;
 
         void resolveTmpOverrides(DisplayContent dc, Configuration parentConfig,
-                boolean isFixedRotationTransforming) {
-            mParentAppBoundsOverride = new Rect(parentConfig.windowConfiguration.getAppBounds());
+                boolean isFixedRotationTransforming, @Nullable Rect safeRegionBounds) {
+            mParentAppBoundsOverride = safeRegionBounds != null ? safeRegionBounds : new Rect(
+                    parentConfig.windowConfiguration.getAppBounds());
+            mParentBoundsOverride = safeRegionBounds != null ? safeRegionBounds : new Rect(
+                    parentConfig.windowConfiguration.getBounds());
             mTmpOverrideConfigOrientation = parentConfig.orientation;
-            final Insets insets;
-            if (mUseOverrideInsetsForConfig && dc != null
+            Insets insets = Insets.NONE;
+            if (safeRegionBounds != null) {
+                // Modify orientation based on the parent app bounds if safe region bounds are set.
+                mTmpOverrideConfigOrientation =
+                        mParentAppBoundsOverride.height() >= mParentAppBoundsOverride.width()
+                                ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+            } else if (mUseOverrideInsetsForConfig && dc != null
                     && !isFloating(parentConfig.windowConfiguration.getWindowingMode())) {
                 // Insets are decoupled from configuration by default from V+, use legacy
                 // compatibility behaviour for apps targeting SDK earlier than 35
@@ -2447,10 +2366,8 @@
                         .getDecorInsetsInfo(rotation, dw, dh);
                 final Rect stableBounds = decorInsets.mOverrideConfigFrame;
                 mTmpOverrideConfigOrientation = stableBounds.width() > stableBounds.height()
-                                ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+                        ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
                 insets = Insets.of(decorInsets.mOverrideNonDecorInsets);
-            } else {
-                insets = Insets.NONE;
             }
             mParentAppBoundsOverride.inset(insets);
         }
@@ -2542,7 +2459,8 @@
             inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
             outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
 
-            if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
+            // Floating tasks shouldn't be restricted by containing app bounds.
+            if (!customContainerPolicy && !isFloating(windowingMode)) {
                 final Rect containingAppBounds;
                 if (insideParentBounds) {
                     containingAppBounds = useOverrideInsetsForConfig
@@ -3299,40 +3217,23 @@
 
         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;
-            }
+        final boolean hasAdjacentOnTop = forOtherAdjacentTaskFragments(
+                tf -> siblings.indexOf(tf) > zOrder);
+        if (!hasAdjacentOnTop) {
+            // early return if this TF already has higher z-ordering.
+            return false;
         }
 
         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 (!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;
-            }
+        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.
@@ -3456,16 +3357,9 @@
             sb.append(" organizerProc=");
             sb.append(mTaskFragmentOrganizerProcessName);
         }
-        if (Flags.allowMultipleAdjacentTaskFragments()) {
-            if (mAdjacentTaskFragments != null) {
-                sb.append(" adjacent=");
-                sb.append(mAdjacentTaskFragments);
-            }
-        } else {
-            if (mAdjacentTaskFragment != null) {
-                sb.append(" adjacent=");
-                sb.append(mAdjacentTaskFragment);
-            }
+        if (mAdjacentTaskFragments != null) {
+            sb.append(" adjacent=");
+            sb.append(mAdjacentTaskFragments);
         }
         sb.append('}');
         return sb.toString();
@@ -3591,10 +3485,6 @@
         }
 
         AdjacentSet(@NonNull ArraySet<TaskFragment> taskFragments) {
-            if (!Flags.allowMultipleAdjacentTaskFragments()) {
-                throw new IllegalStateException("allowMultipleAdjacentTaskFragments must be"
-                        + " enabled to set more than two TaskFragments adjacent to each other.");
-            }
             final int size = taskFragments.size();
             if (size < 2) {
                 throw new IllegalArgumentException("Adjacent TaskFragments must contain at least"
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 8917f4e..2c10af4 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1249,7 +1249,7 @@
                 // Skip dispatching the change for PiP task to avoid its activity drawing for the
                 // intermediate state which will cause flickering. The final PiP bounds in new
                 // rotation will be applied by PipTransition.
-                ar.mDisplayContent.mPinnedTaskController.setEnterPipTransaction(null);
+                ar.mDisplayContent.mPinnedTaskController.setEnterPipWithRotatedTransientLaunch();
             }
             return inPip;
         }
@@ -2589,9 +2589,6 @@
         }
         // When the TaskFragment has an adjacent TaskFragment, sibling behind them should be
         // hidden unless any of them are translucent.
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            return taskFragment.getAdjacentTaskFragment().isTranslucentForTransition();
-        }
         return taskFragment.forOtherAdjacentTaskFragments(TaskFragment::isTranslucentForTransition);
     }
 
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 3a4d9d2..b042e11 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -57,7 +57,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
-import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
+import com.android.server.wallpaper.WallpaperCropper;
+import com.android.server.wallpaper.WallpaperDefaultDisplayInfo;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -71,7 +72,6 @@
 class WallpaperController {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM;
     private WindowManagerService mService;
-    private WallpaperCropUtils mWallpaperCropUtils = null;
     private DisplayContent mDisplayContent;
 
     // Larger index has higher z-order.
@@ -116,6 +116,13 @@
 
     private boolean mShouldOffsetWallpaperCenter;
 
+    // This is for WallpaperCropper, which has cropping logic for the default display only.
+    // This is lazily initialization by getOrCreateDefaultDisplayInfo. DO NOT use this member
+    // variable directly.
+    // TODO(b/400685784) make the WallpaperCropper operate on every display independently
+    @Nullable
+    private WallpaperDefaultDisplayInfo mDefaultDisplayInfo = null;
+
     private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
         final ActivityRecord ar = w.mActivityRecord;
         // The animating window can still be visible on screen if it is in transition, so we
@@ -246,10 +253,6 @@
         return largestDisplaySize;
     }
 
-    void setWallpaperCropUtils(WallpaperCropUtils wallpaperCropUtils) {
-        mWallpaperCropUtils = wallpaperCropUtils;
-    }
-
     WindowState getWallpaperTarget() {
         return mWallpaperTarget;
     }
@@ -352,16 +355,12 @@
         int offsetY;
 
         if (multiCrop()) {
-            if (mWallpaperCropUtils == null) {
-                Slog.e(TAG, "Update wallpaper offsets before the system is ready. Aborting");
-                return false;
-            }
             Point bitmapSize = new Point(
                     wallpaperWin.mRequestedWidth, wallpaperWin.mRequestedHeight);
             SparseArray<Rect> cropHints = token.getCropHints();
             wallpaperFrame = bitmapSize.x <= 0 || bitmapSize.y <= 0 ? wallpaperWin.getFrame()
-                    : mWallpaperCropUtils.getCrop(screenSize, bitmapSize, cropHints,
-                            wallpaperWin.isRtl());
+                    : WallpaperCropper.getCrop(screenSize, getOrCreateDefaultDisplayInfo(),
+                            bitmapSize, cropHints, wallpaperWin.isRtl());
             int frameWidth = wallpaperFrame.width();
             int frameHeight = wallpaperFrame.height();
             float frameRatio = (float) frameWidth / frameHeight;
@@ -508,6 +507,16 @@
         return changed;
     }
 
+    private WallpaperDefaultDisplayInfo getOrCreateDefaultDisplayInfo() {
+        if (mDefaultDisplayInfo != null) {
+            return mDefaultDisplayInfo;
+        }
+        WindowManager windowManager = mService.mContext.getSystemService(WindowManager.class);
+        Resources resources = mService.mContext.getResources();
+        mDefaultDisplayInfo = new WallpaperDefaultDisplayInfo(windowManager, resources);
+        return mDefaultDisplayInfo;
+    }
+
     /**
      * Get an extra offset if needed ({@link #mShouldOffsetWallpaperCenter} = true, typically on
      * multiple display devices) so that the wallpaper in a smaller display ends up centered at the
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 80137a2..3f2b40c 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -65,9 +65,6 @@
     /** Time of current animation step. Reset on each iteration */
     long mCurrentTime;
 
-    int mBulkUpdateParams = 0;
-    Object mLastWindowFreezeSource;
-
     private boolean mInitialized = false;
 
     private Choreographer mChoreographer;
@@ -145,7 +142,6 @@
         final int animationFlags = useShellTransition ? CHILDREN : (TRANSITION | CHILDREN);
         boolean rootAnimating = false;
         mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
-        mBulkUpdateParams = 0;
         if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
         }
@@ -202,8 +198,7 @@
         }
 
         final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
-        final boolean doRequest = mBulkUpdateParams != 0 && root.copyAnimToLayoutParams();
-        if (hasPendingLayoutChanges || doRequest) {
+        if (hasPendingLayoutChanges) {
             mService.mWindowPlacerLocked.requestTraversal();
         }
 
@@ -245,7 +240,6 @@
 
         if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: exit"
-                    + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
                     + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
         }
     }
@@ -265,17 +259,6 @@
         mRunningExpensiveAnimations = runningExpensiveAnimations;
     }
 
-    private static String bulkUpdateParamsToString(int bulkUpdateParams) {
-        StringBuilder builder = new StringBuilder(128);
-        if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
-            builder.append(" UPDATE_ROTATION");
-        }
-        if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING) != 0) {
-            builder.append(" SET_WALLPAPER_ACTION_PENDING");
-        }
-        return builder.toString();
-    }
-
     public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
         final String subPrefix = "  " + prefix;
 
@@ -292,11 +275,6 @@
             pw.print(prefix); pw.print("mCurrentTime=");
                     pw.println(TimeUtils.formatUptime(mCurrentTime));
         }
-        if (mBulkUpdateParams != 0) {
-            pw.print(prefix); pw.print("mBulkUpdateParams=0x");
-                    pw.print(Integer.toHexString(mBulkUpdateParams));
-                    pw.println(bulkUpdateParamsToString(mBulkUpdateParams));
-        }
     }
 
     void scheduleAnimation() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 466ed78..5cbba355 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -97,6 +97,7 @@
 import com.android.server.wm.SurfaceAnimator.AnimationType;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 import com.android.server.wm.utils.AlwaysTruePredicate;
+import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -161,6 +162,15 @@
     protected @InsetsType int mMergedExcludeInsetsTypes = 0;
     private @InsetsType int mExcludeInsetsTypes = 0;
 
+    /**
+     * Bounds for the safe region for this window container which control the
+     * {@link AppCompatSafeRegionPolicy}. These bounds can be passed on to the subtree if the
+     * subtree has no other bounds for the safe region. The value will be null if there are no safe
+     * region bounds for the window container.
+     */
+    @Nullable
+    private Rect mSafeRegionBounds;
+
     @Nullable
     private ArrayMap<IBinder, DeathRecipient> mInsetsOwnerDeathRecipientMap;
 
@@ -556,6 +566,38 @@
                 mParent != null ? mParent.mMergedExcludeInsetsTypes : 0);
     }
 
+    /**
+     * Returns the safe region bounds on the window container. If the window container has no safe
+     * region bounds set, the safe region bounds as set on the nearest ancestor is returned.
+     */
+    @Nullable
+    Rect getSafeRegionBounds() {
+        if (mSafeRegionBounds != null) {
+            return mSafeRegionBounds;
+        }
+        if (mParent == null) {
+            return null;
+        }
+        return mParent.getSafeRegionBounds();
+    }
+
+    /**
+     * Sets the safe region bounds on the window container. Set bounds to {@code null} to reset.
+     *
+     * @param safeRegionBounds the safe region {@link Rect} that should be set on this
+     *                         WindowContainer
+     */
+    void setSafeRegionBounds(@Nullable Rect safeRegionBounds) {
+        if (!Flags.safeRegionLetterboxing()) {
+            Slog.i(TAG, "Feature safe region letterboxing is not available");
+            return;
+        }
+        mSafeRegionBounds = safeRegionBounds;
+        // Trigger a config change whenever this method is called since the safe region bounds
+        // can be modified (including a reset).
+        onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+    }
+
     private void mergeExcludeInsetsTypesAndNotifyInsetsChanged(
             @InsetsType int excludeInsetsTypesFromParent) {
         final ArraySet<WindowState> changedWindows = new ArraySet<>();
@@ -2006,11 +2048,16 @@
         return getActivity(r -> !r.finishing, true /* traverseTopToBottom */);
     }
 
-    ActivityRecord getTopMostVisibleFreeformActivity() {
+    ActivityRecord getTopMostFreeformActivity() {
         return getActivity(r -> r.isVisibleRequested() && r.inFreeformWindowingMode(),
                 true /* traverseTopToBottom */);
     }
 
+    ActivityRecord getTopMostVisibleFreeformActivity() {
+        return getActivity(r -> r.isVisible() && r.inFreeformWindowingMode(),
+                true /* traverseTopToBottom */);
+    }
+
     ActivityRecord getTopActivity(boolean includeFinishing, boolean includeOverlays) {
         // Break down into 4 calls to avoid object creation due to capturing input params.
         if (includeFinishing) {
@@ -3225,6 +3272,7 @@
                 mLocalInsetsSources.valueAt(i).dump(childPrefix, pw);
             }
         }
+        pw.println(prefix + mSafeRegionBounds + " SafeRegionBounds");
     }
 
     final void updateSurfacePositionNonOrganized() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 4b5a3a0..5f2a2ad 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -54,7 +54,6 @@
 import com.android.internal.policy.KeyInterceptionInfo;
 import com.android.server.input.InputManagerService;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
 import com.android.server.wm.SensitiveContentPackages.PackageInfo;
 
 import java.lang.annotation.Retention;
@@ -772,12 +771,6 @@
     public abstract void setWallpaperCropHints(IBinder windowToken, SparseArray<Rect> cropHints);
 
     /**
-     * Transmits the {@link WallpaperCropUtils} instance to {@link WallpaperController}.
-     * {@link WallpaperCropUtils} contains the helpers to properly position the wallpaper.
-     */
-    public abstract void setWallpaperCropUtils(WallpaperCropUtils wallpaperCropUtils);
-
-    /**
      * Returns {@code true} if a Window owned by {@code uid} has focus.
      */
     public abstract boolean isUidFocused(int uid);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9fc0339..fb38c58 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -198,6 +198,8 @@
 import android.graphics.Region;
 import android.hardware.configstore.V1_0.OptionalBool;
 import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
+import android.hardware.devicestate.DeviceState;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputSettings;
@@ -356,7 +358,6 @@
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.power.ShutdownThread;
 import com.android.server.utils.PriorityDump;
-import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
 import com.android.window.flags.Flags;
 
 import dalvik.annotation.optimization.NeverCompile;
@@ -1033,6 +1034,21 @@
     PowerManager mPowerManager;
     PowerManagerInternal mPowerManagerInternal;
 
+    private DeviceStateManager mDeviceStateManager;
+    private DeviceStateCallback mDeviceStateCallback;
+    private class DeviceStateCallback implements DeviceStateManager.DeviceStateCallback {
+        private DeviceState mCurrentDeviceState;
+        @Override
+        public void onDeviceStateChanged(@NonNull DeviceState state) {
+            mCurrentDeviceState = state;
+        }
+
+        boolean isInRearDisplayOuterDefaultState() {
+            return mCurrentDeviceState != null && mCurrentDeviceState
+                    .hasProperties(DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT);
+        }
+    }
+
     private float mWindowAnimationScaleSetting = 1.0f;
     private float mTransitionAnimationScaleSetting = 1.0f;
     private float mAnimatorDurationScaleSetting = 1.0f;
@@ -1318,6 +1334,10 @@
         mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
 
+        mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+        mDeviceStateCallback = new DeviceStateCallback();
+        mDeviceStateManager.registerCallback(new HandlerExecutor(mH), mDeviceStateCallback);
+
         if (mPowerManagerInternal != null) {
             mPowerManagerInternal.registerLowPowerModeObserver(
                     new PowerManagerInternal.LowPowerModeListener() {
@@ -2133,7 +2153,6 @@
         }
 
         final DisplayContent dc = win.getDisplayContent();
-        dc.getDisplayRotation().markForSeamlessRotation(win, false /* seamlesslyRotated */);
 
         win.resetAppOpsState();
 
@@ -4735,7 +4754,8 @@
 
     @EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS)
     @Override
-    public void updateDisplayWindowAnimatingTypes(int displayId, @InsetsType int animatingTypes) {
+    public void updateDisplayWindowAnimatingTypes(int displayId, @InsetsType int animatingTypes,
+            @Nullable ImeTracker.Token statsToken) {
         updateDisplayWindowAnimatingTypes_enforcePermission();
         if (android.view.inputmethod.Flags.reportAnimatingInsetsTypes()) {
             final long origId = Binder.clearCallingIdentity();
@@ -4743,9 +4763,13 @@
                 synchronized (mGlobalLock) {
                     final DisplayContent dc = mRoot.getDisplayContent(displayId);
                     if (dc == null || dc.mRemoteInsetsControlTarget == null) {
+                        ImeTracker.forLogging().onFailed(statsToken,
+                                ImeTracker.PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES);
                         return;
                     }
-                    dc.mRemoteInsetsControlTarget.setAnimatingTypes(animatingTypes);
+                    ImeTracker.forLogging().onProgress(statsToken,
+                            ImeTracker.PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES);
+                    dc.mRemoteInsetsControlTarget.setAnimatingTypes(animatingTypes, statsToken);
                 }
             } finally {
                 Binder.restoreCallingIdentity(origId);
@@ -7595,6 +7619,26 @@
     }
 
     @Override
+    public boolean isEligibleForDesktopMode(int displayId) {
+        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "isEligibleForDesktopMode()")) {
+            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+        }
+
+        synchronized (mGlobalLock) {
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent == null) {
+                ProtoLog.e(WM_ERROR, "Attempted to check isEligibleForDesktopMode() "
+                        + "for a display that does not exist: %d", displayId);
+                return false;
+            }
+            if (!displayContent.isSystemDecorationsSupported()) {
+                return false;
+            }
+            return displayContent.isDefaultDisplay || displayContent.allowContentModeSwitch();
+        }
+    }
+
+    @Override
     public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
         if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowSystemDecors()")) {
             throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
@@ -8100,12 +8144,6 @@
         }
 
         @Override
-        public void setWallpaperCropUtils(WallpaperCropUtils wallpaperCropUtils) {
-            mRoot.getDisplayContent(DEFAULT_DISPLAY).mWallpaperController
-                    .setWallpaperCropUtils(wallpaperCropUtils);
-        }
-
-        @Override
         public boolean isUidFocused(int uid) {
             synchronized (mGlobalLock) {
                 for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
@@ -8957,6 +8995,17 @@
             }
         }
 
+        if (mDeviceStateCallback.isInRearDisplayOuterDefaultState()) {
+            final Display[] rearDisplays = mDisplayManager
+                    .getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR);
+            if (rearDisplays.length > 0 && rearDisplays[0].getDisplayId() == t.getDisplayId()) {
+                // Do not change display focus to the inner display if we're in this mode. Note that
+                // in this mode, the inner display is configured as a rear display.
+                Slog.w(TAG, "Ignoring focus change because device is in RDM.");
+                return;
+            }
+        }
+
         clearPointerDownOutsideFocusRunnable();
 
         final InputTarget focusedInputTarget = mFocusedInputTarget;
@@ -9329,10 +9378,12 @@
 
     /**
      * Updates the flags on an existing surface's input channel. This assumes the surface provided
-     * is the one associated with the provided input-channel. If this isn't the case, behavior
-     * is undefined.
+     * is the one associated with the provided input-channel. If this isn't the case, behavior is
+     * undefined.
      */
-    void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
+    void updateInputChannel(IBinder channelToken,
+            @Nullable InputTransferToken hostInputTransferToken, int displayId,
+            SurfaceControl surface,
             int flags, int privateFlags, int inputFeatures, Region region) {
         final InputApplicationHandle applicationHandle;
         final String name;
@@ -9346,6 +9397,11 @@
             name = win.toString();
             applicationHandle = win.getApplicationHandle();
             win.setIsFocusable((flags & FLAG_NOT_FOCUSABLE) == 0);
+            if (Flags.updateHostInputTransferToken()) {
+                WindowState hostWindowState = hostInputTransferToken != null
+                        ? mInputToWindowMap.get(hostInputTransferToken.getToken()) : null;
+                win.updateHost(hostWindowState);
+            }
         }
 
         updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
@@ -9374,23 +9430,6 @@
             return focusedActivity;
         }
 
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
-            final ActivityRecord adjacentTopActivity = adjacentTaskFragment.topRunningActivity();
-            if (adjacentTopActivity == null) {
-                // Return if no adjacent activity.
-                return focusedActivity;
-            }
-
-            if (adjacentTopActivity.getLastWindowCreateTime()
-                    < focusedActivity.getLastWindowCreateTime()) {
-                // Return if the current focus activity has more recently active window.
-                return focusedActivity;
-            }
-
-            return adjacentTopActivity;
-        }
-
         // Find the adjacent activity with more recently active window.
         final ActivityRecord[] mostRecentActiveActivity = { focusedActivity };
         final long[] mostRecentActiveTime = { focusedActivity.getLastWindowCreateTime() };
@@ -9461,20 +9500,15 @@
             // No adjacent window.
             return false;
         }
-        final TaskFragment adjacentFragment;
-        if (Flags.allowMultipleAdjacentTaskFragments()) {
-            if (fromFragment.getAdjacentTaskFragments().size() > 2) {
-                throw new IllegalStateException("Not yet support 3+ adjacent for non-Task TFs");
-            }
-            final TaskFragment[] tmpAdjacent = new TaskFragment[1];
-            fromFragment.forOtherAdjacentTaskFragments(adjacentTF -> {
-                tmpAdjacent[0] = adjacentTF;
-                return true;
-            });
-            adjacentFragment = tmpAdjacent[0];
-        } else {
-            adjacentFragment = fromFragment.getAdjacentTaskFragment();
+        if (fromFragment.getAdjacentTaskFragments().size() > 2) {
+            throw new IllegalStateException("Not yet support 3+ adjacent for non-Task TFs");
         }
+        final TaskFragment[] tmpAdjacent = new TaskFragment[1];
+        fromFragment.forOtherAdjacentTaskFragments(adjacentTF -> {
+            tmpAdjacent[0] = adjacentTF;
+            return true;
+        });
+        final TaskFragment adjacentFragment = tmpAdjacent[0];
         if (adjacentFragment.isIsolatedNav()) {
             // Don't move the focus if the adjacent TF is isolated navigation.
             return false;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index ea1f35a..c19fa8c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -78,6 +78,7 @@
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT;
 import static android.window.WindowContainerTransaction.HierarchyOp.REACHABILITY_EVENT_X;
 import static android.window.WindowContainerTransaction.HierarchyOp.REACHABILITY_EVENT_Y;
@@ -472,35 +473,6 @@
         transition.setAllReady();
     }
 
-    // TODO(b/365884835): remove this method and callers.
-    @Override
-    public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
-            @NonNull IWindowContainerTransactionCallback callback,
-            @NonNull WindowContainerTransaction t) {
-        enforceTaskPermission("startLegacyTransition()");
-        final CallerInfo caller = new CallerInfo();
-        final long ident = Binder.clearCallingIdentity();
-        int syncId;
-        try {
-            synchronized (mGlobalLock) {
-                if (type < 0) {
-                    throw new IllegalArgumentException("Can't create transition with no type");
-                }
-                if (mTransitionController.getTransitionPlayer() != null) {
-                    throw new IllegalArgumentException("Can't use legacy transitions in"
-                            + " when shell transitions are enabled.");
-                }
-                syncId = startSyncWithOrganizer(callback);
-                applyTransaction(t, syncId, mService.mChainTracker.startLegacy("legacyTransit"),
-                        caller);
-                setSyncReady(syncId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        return syncId;
-    }
-
     @Override
     public void finishTransition(@NonNull IBinder transitionToken,
             @Nullable WindowContainerTransaction t) {
@@ -1544,6 +1516,19 @@
                 container.setExcludeInsetsTypes(hop.getExcludeInsetsTypes());
                 break;
             }
+            case HIERARCHY_OP_TYPE_SET_SAFE_REGION_BOUNDS: {
+                final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
+                if (container == null || !container.isAttached()) {
+                    Slog.e(TAG,
+                            "Attempt to operate on unknown or detached container: " + container);
+                    break;
+                }
+                if (chain.mTransition != null) {
+                    chain.mTransition.collect(container);
+                }
+                container.setSafeRegionBounds(hop.getSafeRegionBounds());
+                effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
+            }
         }
         return effects;
     }
@@ -1671,13 +1656,9 @@
                 }
                 if (!taskFragment.isAdjacentTo(secondaryTaskFragment)) {
                     // Only have lifecycle effect if the adjacent changed.
-                    if (Flags.allowMultipleAdjacentTaskFragments()) {
-                        // Activity Embedding only set two TFs adjacent.
-                        taskFragment.setAdjacentTaskFragments(
-                                new TaskFragment.AdjacentSet(taskFragment, secondaryTaskFragment));
-                    } else {
-                        taskFragment.setAdjacentTaskFragment(secondaryTaskFragment);
-                    }
+                    // Activity Embedding only set two TFs adjacent.
+                    taskFragment.setAdjacentTaskFragments(
+                            new TaskFragment.AdjacentSet(taskFragment, secondaryTaskFragment));
                     effects |= TRANSACT_EFFECTS_LIFECYCLE;
                 }
 
@@ -2220,30 +2201,6 @@
     }
 
     private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
-        if (!Flags.allowMultipleAdjacentTaskFragments()) {
-            final WindowContainer wc1 = WindowContainer.fromBinder(hop.getContainer());
-            if (wc1 == null || !wc1.isAttached()) {
-                Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc1);
-                return TRANSACT_EFFECTS_NONE;
-            }
-            final TaskFragment root1 = wc1.asTaskFragment();
-            final WindowContainer wc2 = WindowContainer.fromBinder(hop.getAdjacentRoot());
-            if (wc2 == null || !wc2.isAttached()) {
-                Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc2);
-                return TRANSACT_EFFECTS_NONE;
-            }
-            final TaskFragment root2 = wc2.asTaskFragment();
-            if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) {
-                throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
-                        + " organizer root1=" + root1 + " root2=" + root2);
-            }
-            if (root1.isAdjacentTo(root2)) {
-                return TRANSACT_EFFECTS_NONE;
-            }
-            root1.setAdjacentTaskFragment(root2);
-            return TRANSACT_EFFECTS_LIFECYCLE;
-        }
-
         final IBinder[] containers = hop.getContainers();
         final ArraySet<TaskFragment> adjacentRoots = new ArraySet<>();
         for (IBinder container : containers) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index bdd1372..d356128 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -337,6 +337,11 @@
     public static final int ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE = 1 << 25;
     public static final int ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER = 0x0000ffff;
 
+    private static final int ACTIVITY_STATE_VISIBLE =
+            com.android.window.flags.Flags.useVisibleRequestedForProcessTracker()
+                    ? ACTIVITY_STATE_FLAG_IS_VISIBLE
+                    : ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE;
+
     /**
      * The state for oom-adjustment calculation. The higher 16 bits are the activity states, and the
      * lower 16 bits are the task layer rank (see {@link Task#mLayerRank}). This field is written by
@@ -1260,8 +1265,7 @@
         int nonOccludedRatio = 0;
         long perceptibleTaskStoppedTimeMillis = Long.MIN_VALUE;
         final boolean wasResumed = hasResumedActivity();
-        final boolean wasAnyVisible = (mActivityStateFlags
-                & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
+        final boolean wasAnyVisible = (mActivityStateFlags & ACTIVITY_STATE_VISIBLE) != 0;
         for (int i = mActivities.size() - 1; i >= 0; i--) {
             final ActivityRecord r = mActivities.get(i);
             if (r.isVisible()) {
@@ -1275,8 +1279,9 @@
             if (task.mLayerRank != Task.LAYER_RANK_INVISIBLE) {
                 stateFlags |= ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK;
             }
+            final ActivityRecord.State state = r.getState();
             if (r.isVisibleRequested()) {
-                if (r.isState(RESUMED)) {
+                if (state == RESUMED) {
                     stateFlags |= ACTIVITY_STATE_FLAG_HAS_RESUMED;
                     final int windowingMode = r.getWindowingMode();
                     if (windowingMode == WINDOWING_MODE_MULTI_WINDOW
@@ -1301,13 +1306,21 @@
                 // this process, we'd find out the one with the minimal layer, thus it'll
                 // get a higher adj score.
             } else if (!visible && bestInvisibleState != PAUSING) {
-                if (r.isState(PAUSING, PAUSED)) {
+                if (state == PAUSING) {
                     bestInvisibleState = PAUSING;
-                } else if (r.isState(STOPPING)) {
+                    // Treat PAUSING as visible in case the next activity in the same process has
+                    // not yet been set as visible-requested.
+                    if (com.android.window.flags.Flags.useVisibleRequestedForProcessTracker()
+                            && r.isVisible()) {
+                        stateFlags |= ACTIVITY_STATE_FLAG_IS_VISIBLE;
+                    }
+                } else if (state == PAUSED) {
+                    bestInvisibleState = PAUSED;
+                } else if (state == STOPPING) {
                     bestInvisibleState = STOPPING;
                     // Not "finishing" if any of activity isn't finishing.
                     allStoppingFinishing &= r.finishing;
-                } else if (bestInvisibleState == DESTROYED && r.isState(STOPPED)) {
+                } else if (bestInvisibleState == DESTROYED && state == STOPPED) {
                     if (task.mIsPerceptible) {
                         perceptibleTaskStoppedTimeMillis =
                                 Long.max(r.mStoppedTime, perceptibleTaskStoppedTimeMillis);
@@ -1340,7 +1353,7 @@
         stateFlags |= minTaskLayer & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
         if (visible) {
             stateFlags |= ACTIVITY_STATE_FLAG_IS_VISIBLE;
-        } else if (bestInvisibleState == PAUSING) {
+        } else if (bestInvisibleState == PAUSING || bestInvisibleState == PAUSED) {
             stateFlags |= ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED;
         } else if (bestInvisibleState == STOPPING) {
             stateFlags |= ACTIVITY_STATE_FLAG_IS_STOPPING;
@@ -1351,8 +1364,7 @@
         mActivityStateFlags = stateFlags;
         mPerceptibleTaskStoppedTimeMillis = perceptibleTaskStoppedTimeMillis;
 
-        final boolean anyVisible = (stateFlags
-                & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
+        final boolean anyVisible = (stateFlags & ACTIVITY_STATE_VISIBLE) != 0;
         if (!wasAnyVisible && anyVisible) {
             mAtm.mVisibleActivityProcessTracker.onAnyActivityVisible(this);
             mAtm.mWindowManager.onProcessActivityVisibilityChanged(mUid, true /*visible*/);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1022d18..af52001 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -100,7 +100,6 @@
 import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
 import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
 
-import static com.android.input.flags.Flags.removeInputChannelFromWindowstate;
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS;
@@ -170,7 +169,6 @@
 import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
 import static com.android.server.wm.WindowStateProto.KEEP_CLEAR_AREAS;
 import static com.android.server.wm.WindowStateProto.MERGED_LOCAL_INSETS_SOURCES;
-import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
 import static com.android.server.wm.WindowStateProto.REMOVED;
 import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
 import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
@@ -232,7 +230,6 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.Surface;
-import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewDebug;
@@ -400,7 +397,6 @@
      * rotation.
      */
     final boolean mForceSeamlesslyRotate;
-    SeamlessRotator mPendingSeamlessRotate;
 
     private RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
 
@@ -594,13 +590,6 @@
     /** The time when the window was last requested to redraw for orientation change. */
     private long mOrientationChangeRedrawRequestTime;
 
-    /**
-     * The orientation during the last visible call to relayout. If our
-     * current orientation is different, the window can't be ready
-     * to be shown.
-     */
-    int mLastVisibleLayoutRotation = -1;
-
     /** Is this window now (or just being) removed? */
     boolean mRemoved;
 
@@ -613,10 +602,6 @@
 
     // Input channel and input window handle used by the input dispatcher.
     final InputWindowHandleWrapper mInputWindowHandle;
-    /**
-     * Only populated if flag REMOVE_INPUT_CHANNEL_FROM_WINDOWSTATE is disabled.
-     */
-    private InputChannel mInputChannel;
 
     /**
      * The token will be assigned to {@link InputWindowHandle#token} if this window can receive
@@ -660,15 +645,6 @@
     boolean mIsSurfacePositionPaused;
 
     /**
-     * During seamless rotation we have two phases, first the old window contents
-     * are rotated to look as if they didn't move in the new coordinate system. Then we
-     * have to freeze updates to this layer (to preserve the transformation) until
-     * the resize actually occurs. This is true from when the transformation is set
-     * and false until the transaction to resize is sent.
-     */
-    boolean mSeamlesslyRotated = false;
-
-    /**
      * Whether the IME insets have been consumed. If {@code true}, this window won't be able to
      * receive visible IME insets; {@code false}, otherwise.
      */
@@ -783,11 +759,6 @@
      */
     private boolean mInsetsAnimationRunning;
 
-    private final Consumer<SurfaceControl.Transaction> mSeamlessRotationFinishedConsumer = t -> {
-        finishSeamlessRotation(t);
-        updateSurfacePosition(t);
-    };
-
     private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
         // Only apply the position to the surface when there's no leash created.
         if (mSurfaceControl != null && mSurfaceControl.isValid() && !mSurfaceAnimator.hasLeash()) {
@@ -850,7 +821,8 @@
     }
 
     @Override
-    public void setAnimatingTypes(@InsetsType int animatingTypes) {
+    public void setAnimatingTypes(@InsetsType int animatingTypes,
+            @Nullable ImeTracker.Token statsToken) {
         if (mAnimatingTypes != animatingTypes) {
             if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                 Trace.instant(TRACE_TAG_WINDOW_MANAGER,
@@ -862,6 +834,17 @@
             mWmService.scheduleAnimationLocked();
 
             mAnimatingTypes = animatingTypes;
+
+            if (android.view.inputmethod.Flags.reportAnimatingInsetsTypes()) {
+                ImeTracker.forLogging().onProgress(statsToken,
+                        ImeTracker.PHASE_WM_WINDOW_ANIMATING_TYPES_CHANGED);
+                final InsetsStateController insetsStateController =
+                        getDisplayContent().getInsetsStateController();
+                insetsStateController.onAnimatingTypesChanged(this, statsToken);
+            }
+        } else {
+            ImeTracker.forLogging().onFailed(statsToken,
+                    ImeTracker.PHASE_WM_WINDOW_ANIMATING_TYPES_CHANGED);
         }
     }
 
@@ -898,69 +881,6 @@
         return visible && mFrozenInsetsState == null;
     }
 
-    void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
-            @Rotation int rotation, boolean requested) {
-        // Invisible windows and the wallpaper do not participate in the seamless rotation animation
-        if (!isVisibleNow() || mIsWallpaper) {
-            return;
-        }
-
-        if (mToken.hasFixedRotationTransform()) {
-            // The transform of its surface is handled by fixed rotation.
-            return;
-        }
-        final Task task = getTask();
-        if (task != null && task.inPinnedWindowingMode()) {
-            // It is handled by PinnedTaskController. Note that the windowing mode of activity
-            // and windows may still be fullscreen.
-            return;
-        }
-
-        if (mPendingSeamlessRotate != null) {
-            oldRotation = mPendingSeamlessRotate.getOldRotation();
-        }
-
-        // Skip performing seamless rotation when the controlled insets is IME with visible state.
-        if (mControllableInsetProvider != null
-                && mControllableInsetProvider.getSource().getType() == WindowInsets.Type.ime()) {
-            return;
-        }
-
-        if (mForceSeamlesslyRotate || requested) {
-            if (mControllableInsetProvider != null) {
-                mControllableInsetProvider.startSeamlessRotation();
-            }
-            mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo(),
-                    false /* applyFixedTransformationHint */);
-            // The surface position is going to be unrotated according to the last position.
-            // Make sure the source position is up-to-date.
-            mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
-            mPendingSeamlessRotate.unrotate(transaction, this);
-            getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
-                    true /* seamlesslyRotated */);
-            applyWithNextDraw(mSeamlessRotationFinishedConsumer);
-        }
-    }
-
-    void cancelSeamlessRotation() {
-        finishSeamlessRotation(getPendingTransaction());
-    }
-
-    void finishSeamlessRotation(SurfaceControl.Transaction t) {
-        if (mPendingSeamlessRotate == null) {
-            return;
-        }
-
-        mPendingSeamlessRotate.finish(t, this);
-        mPendingSeamlessRotate = null;
-
-        getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
-            false /* seamlesslyRotated */);
-        if (mControllableInsetProvider != null) {
-            mControllableInsetProvider.finishSeamlessRotation();
-        }
-    }
-
     List<Rect> getSystemGestureExclusion() {
         return mExclusionRects;
     }
@@ -1824,12 +1744,8 @@
      * Input Manager uses when discarding windows from input consideration.
      */
     boolean isPotentialDragTarget(boolean targetInterceptsGlobalDrag) {
-        if (removeInputChannelFromWindowstate()) {
-            return (targetInterceptsGlobalDrag || isVisibleNow()) && !mRemoved
-                    && mInputChannelToken != null && mInputWindowHandle != null;
-        }
         return (targetInterceptsGlobalDrag || isVisibleNow()) && !mRemoved
-                && mInputChannel != null && mInputWindowHandle != null;
+                && mInputChannelToken != null && mInputWindowHandle != null;
     }
 
     /**
@@ -2179,8 +2095,7 @@
                 && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
                 && !isDragResizing()
                 && hasMovementAnimation
-                && !mWinAnimator.mLastHidden
-                && !mSeamlesslyRotated;
+                && !mWinAnimator.mLastHidden;
     }
 
     /**
@@ -2577,25 +2492,13 @@
         if (mInputChannelToken != null) {
             throw new IllegalStateException("Window already has an input channel token.");
         }
-        if (removeInputChannelFromWindowstate()) {
-            String name = getName();
-            InputChannel channel = mWmService.mInputManager.createInputChannel(name);
-            mInputChannelToken = channel.getToken();
-            mInputWindowHandle.setToken(mInputChannelToken);
-            mWmService.mInputToWindowMap.put(mInputChannelToken, this);
-            channel.copyTo(outInputChannel);
-            channel.dispose();
-            return;
-        }
-        if (mInputChannel != null) {
-            throw new IllegalStateException("Window already has an input channel.");
-        }
         String name = getName();
-        mInputChannel = mWmService.mInputManager.createInputChannel(name);
-        mInputChannelToken = mInputChannel.getToken();
+        InputChannel channel = mWmService.mInputManager.createInputChannel(name);
+        mInputChannelToken = channel.getToken();
         mInputWindowHandle.setToken(mInputChannelToken);
         mWmService.mInputToWindowMap.put(mInputChannelToken, this);
-        mInputChannel.copyTo(outInputChannel);
+        channel.copyTo(outInputChannel);
+        channel.dispose();
     }
 
     /**
@@ -2618,12 +2521,6 @@
             mInputChannelToken = null;
         }
 
-        if (!removeInputChannelFromWindowstate()) {
-            if (mInputChannel != null) {
-                mInputChannel.dispose();
-                mInputChannel = null;
-            }
-        }
         mInputWindowHandle.setToken(null);
     }
 
@@ -3880,14 +3777,17 @@
     }
 
     /**
-     * Returns {@code true} if activity bounds are letterboxed or letterboxed for display cutout.
+     * Returns {@code true} if activity bounds are letterboxed or letterboxed for display cutout or
+     * letterboxed for a safe region.
      *
      * <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
-     * AppCompatLetterboxOverrides#shouldShowLetterboxUi} for more context.
+     * AppCompatLetterboxPolicy#shouldShowLetterboxUi} for more context.
      */
     boolean areAppWindowBoundsLetterboxed() {
         return mActivityRecord != null && !isStartingWindowAssociatedToTask()
-                && (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout());
+                && (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout()
+                || mActivityRecord.mAppCompatController
+                .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
     }
 
     /** Returns {@code true} if the window is letterboxed for the display cutout. */
@@ -4016,7 +3916,6 @@
         proto.write(REMOVED, mRemoved);
         proto.write(IS_ON_SCREEN, isOnScreen());
         proto.write(IS_VISIBLE, isVisible);
-        proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
         proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
         proto.write(HAS_COMPAT_SCALE, hasCompatScale());
         proto.write(GLOBAL_SCALE, mGlobalScale);
@@ -4162,14 +4061,6 @@
                     + " mDestroying=" + mDestroying
                     + " mRemoved=" + mRemoved);
         }
-        pw.print(prefix + "mForceSeamlesslyRotate=" + mForceSeamlesslyRotate
-                + " seamlesslyRotate: pending=");
-        if (mPendingSeamlessRotate != null) {
-            mPendingSeamlessRotate.dump(pw);
-        } else {
-            pw.print("null");
-        }
-        pw.println();
 
         if (mXOffset != 0 || mYOffset != 0) {
             pw.println(prefix + "mXOffset=" + mXOffset + " mYOffset=" + mYOffset);
@@ -4901,8 +4792,6 @@
             mWinAnimator.mEnterAnimationPending = true;
         }
 
-        mLastVisibleLayoutRotation = getDisplayContent().getRotation();
-
         mWinAnimator.mEnteringAnimation = true;
 
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareToDisplay");
@@ -5300,9 +5189,8 @@
 
         final AsyncRotationController asyncRotationController =
                 mDisplayContent.getAsyncRotationController();
-        if ((asyncRotationController != null
-                && asyncRotationController.hasSeamlessOperation(mToken))
-                || mPendingSeamlessRotate != null) {
+        if (asyncRotationController != null
+                && asyncRotationController.hasSeamlessOperation(mToken)) {
             // Freeze position while un-rotating the window, so its surface remains at the position
             // corresponding to the original rotation.
             return;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index a34b511..4fb74ef 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -42,9 +42,6 @@
     /** Only do a maximum of 6 repeated layouts. After that quit */
     private int mLayoutRepeatCount;
 
-    static final int SET_UPDATE_ROTATION                = 1 << 0;
-    static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 1;
-
     private boolean mTraversalScheduled;
     private int mDeferDepth = 0;
     /** The number of layout requests when deferring. */
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 9577608..adfabe1 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -169,7 +169,7 @@
         "android.hardware.broadcastradio@1.1",
         "android.hardware.contexthub@1.0",
         "android.hardware.common.fmq-V1-ndk",
-        "android.hardware.gnss-V3-cpp",
+        "android.hardware.gnss-V5-cpp",
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
@@ -193,10 +193,6 @@
         "android.hardware.tv.input@1.0",
         "android.hardware.tv.input-V2-ndk",
         "android.hardware.vibrator-V3-ndk",
-        "android.hardware.vibrator@1.0",
-        "android.hardware.vibrator@1.1",
-        "android.hardware.vibrator@1.2",
-        "android.hardware.vibrator@1.3",
         "android.hardware.vr@1.0",
         "android.hidl.token@1.0-utils",
         "android.frameworks.schedulerservice@1.0",
@@ -208,6 +204,7 @@
         "android.system.suspend.control-V1-cpp",
         "android.system.suspend.control.internal-cpp",
         "android.system.suspend-V1-ndk",
+        "android_location_flags_c_lib",
         "server_configurable_flags",
         "service.incremental",
     ],
diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp
index aeae13d..c674f90 100644
--- a/services/core/jni/com_android_server_display_DisplayControl.cpp
+++ b/services/core/jni/com_android_server_display_DisplayControl.cpp
@@ -24,12 +24,13 @@
 namespace android {
 
 static jobject nativeCreateVirtualDisplay(JNIEnv* env, jclass clazz, jstring nameObj,
-                                          jboolean secure, jstring uniqueIdStr,
-                                          jfloat requestedRefreshRate) {
+                                          jboolean secure, jboolean optimizeForPower,
+                                          jstring uniqueIdStr, jfloat requestedRefreshRate) {
     const ScopedUtfChars name(env, nameObj);
     const ScopedUtfChars uniqueId(env, uniqueIdStr);
     sp<IBinder> token(SurfaceComposerClient::createVirtualDisplay(std::string(name.c_str()),
                                                                   bool(secure),
+                                                                  bool(optimizeForPower),
                                                                   std::string(uniqueId.c_str()),
                                                                   requestedRefreshRate));
     return javaObjectForIBinder(env, token);
@@ -182,7 +183,7 @@
 
 static const JNINativeMethod sDisplayMethods[] = {
         // clang-format off
-    {"nativeCreateVirtualDisplay", "(Ljava/lang/String;ZLjava/lang/String;F)Landroid/os/IBinder;",
+    {"nativeCreateVirtualDisplay", "(Ljava/lang/String;ZZLjava/lang/String;F)Landroid/os/IBinder;",
             (void*)nativeCreateVirtualDisplay },
     {"nativeDestroyVirtualDisplay", "(Landroid/os/IBinder;)V",
             (void*)nativeDestroyVirtualDisplay },
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a8c49e1..ec8794f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -671,9 +671,13 @@
         return;
     }
 
-    // TODO(b/383092013): Add topology validation
     const DisplayTopologyGraph displayTopology =
             android_hardware_display_DisplayTopologyGraph_toNative(env, topologyGraph);
+    if (input_flags::enable_display_topology_validation() && !displayTopology.isValid()) {
+        LOG(ERROR) << "Ignoring Invalid DisplayTopology";
+        return;
+    }
+
     mInputManager->getDispatcher().setDisplayTopology(displayTopology);
     mInputManager->getChoreographer().setDisplayTopology(displayTopology);
 }
@@ -2309,13 +2313,6 @@
                                                                              locationKeyCode);
 }
 
-static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */,
-                                       const std::shared_ptr<InputChannel>& inputChannel,
-                                       void* data) {
-    NativeInputManager* im = static_cast<NativeInputManager*>(data);
-    im->removeInputChannel(inputChannel->getConnectionToken());
-}
-
 static jobject nativeCreateInputChannel(JNIEnv* env, jobject nativeImplObj, jstring nameObj) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
 
@@ -2337,8 +2334,6 @@
         return nullptr;
     }
 
-    android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
-            handleInputChannelDisposed, im);
     return inputChannelObj;
 }
 
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 9c033e2..93f6e95 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -36,6 +36,7 @@
 #include <android/hardware/gnss/BnGnssMeasurementCallback.h>
 #include <android/hardware/gnss/BnGnssPowerIndicationCallback.h>
 #include <android/hardware/gnss/BnGnssPsdsCallback.h>
+#include <android_location_flags.h>
 #include <binder/IServiceManager.h>
 #include <nativehelper/JNIHelp.h>
 #include <pthread.h>
@@ -53,6 +54,8 @@
 #include "gnss/Gnss.h"
 #include "gnss/GnssAntennaInfo.h"
 #include "gnss/GnssAntennaInfoCallback.h"
+#include "gnss/GnssAssistance.h"
+#include "gnss/GnssAssistanceCallback.h"
 #include "gnss/GnssBatching.h"
 #include "gnss/GnssConfiguration.h"
 #include "gnss/GnssDebug.h"
@@ -114,6 +117,7 @@
 using android::hardware::gnss::GnssPowerStats;
 using android::hardware::gnss::IGnssPowerIndication;
 using android::hardware::gnss::IGnssPowerIndicationCallback;
+using android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback;
 
 using IGnssAidl = android::hardware::gnss::IGnss;
 using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
@@ -140,6 +144,9 @@
 std::unique_ptr<android::gnss::GnssVisibilityControlInterface> gnssVisibilityControlIface = nullptr;
 std::unique_ptr<android::gnss::MeasurementCorrectionsInterface> gnssMeasurementCorrectionsIface =
         nullptr;
+std::unique_ptr<android::gnss::GnssAssistanceInterface> gnssAssistanceIface = nullptr;
+
+namespace location_flags = android::location::flags;
 
 namespace android {
 
@@ -229,6 +236,9 @@
     gnss::GnssVisibilityControl_class_init_once(env, clazz);
     gnss::MeasurementCorrections_class_init_once(env, clazz);
     gnss::MeasurementCorrectionsCallback_class_init_once(env, clazz);
+    if (location_flags::gnss_assistance_interface_jni()) {
+        gnss::GnssAssistance_class_init_once(env, clazz);
+    }
     gnss::Utils_class_init_once(env);
 }
 
@@ -266,7 +276,9 @@
     gnssBatchingIface = gnssHal->getGnssBatchingInterface();
     gnssVisibilityControlIface = gnssHal->getGnssVisibilityControlInterface();
     gnssPowerIndicationIface = gnssHal->getGnssPowerIndicationInterface();
-
+    if (location_flags::gnss_assistance_interface_jni()) {
+        gnssAssistanceIface = gnssHal->getGnssAssistanceInterface();
+    }
     if (mCallbacksObj) {
         ALOGE("Callbacks already initialized");
     } else {
@@ -355,13 +367,22 @@
     // Set IGnssPowerIndication.hal callback.
     if (gnssPowerIndicationIface != nullptr) {
         sp<IGnssPowerIndicationCallback> gnssPowerIndicationCallback =
-                new GnssPowerIndicationCallback();
+                sp<GnssPowerIndicationCallback>::make();
         auto status = gnssPowerIndicationIface->setCallback(gnssPowerIndicationCallback);
         if (!checkAidlStatus(status, "IGnssPowerIndication setCallback() failed.")) {
             gnssPowerIndicationIface = nullptr;
         }
     }
 
+    // Set IGnssAssistance callback.
+    if (gnssAssistanceIface != nullptr) {
+        sp<IGnssAssistanceCallback> gnssAssistanceCallback =
+                sp<gnss::GnssAssistanceCallback>::make();
+        if (!gnssAssistanceIface->setCallback(gnssAssistanceCallback)) {
+            ALOGI("IGnssAssistanceInterface setCallback() failed");
+        }
+    }
+
     return JNI_TRUE;
 }
 
@@ -493,6 +514,15 @@
     gnssPsdsIface->injectPsdsData(data, length, psdsType);
 }
 
+static void android_location_gnss_hal_GnssNative_inject_gnss_assistance(JNIEnv* env, jclass,
+                                                                        jobject gnssAssistanceObj) {
+    if (gnssAssistanceIface == nullptr) {
+        ALOGE("%s: IGnssAssistance interface not available.", __func__);
+        return;
+    }
+    gnssAssistanceIface->injectGnssAssistance(env, gnssAssistanceObj);
+}
+
 static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
         JNIEnv* env, jobject /* obj */, jlong networkHandle, jstring apn, jint apnIpType) {
     if (apn == nullptr) {
@@ -937,6 +967,8 @@
         {"native_stop_nmea_message_collection", "()Z",
          reinterpret_cast<void*>(
                  android_location_gnss_hal_GnssNative_stop_nmea_message_collection)},
+        {"native_inject_gnss_assistance", "(Landroid/location/GnssAssistance;)V",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_gnss_assistance)},
 };
 
 static const JNINativeMethod sBatchingMethods[] = {
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
index eb729de..0bee718 100644
--- a/services/core/jni/com_android_server_sensor_SensorService.cpp
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -60,6 +60,8 @@
     void unregisterRuntimeSensor(jint handle);
     jboolean sendRuntimeSensorEvent(JNIEnv* env, jint handle, jint type, jlong timestamp,
                                     jfloatArray values);
+    jboolean sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type, jint serial,
+                                             jlong timestamp, jfloatArray values);
 
 private:
     sp<SensorService> mService;
@@ -172,9 +174,9 @@
 
     sensors_event_t event{
             .version = sizeof(sensors_event_t),
-            .timestamp = timestamp,
             .sensor = handle,
             .type = type,
+            .timestamp = timestamp,
     };
 
     int valuesLength = env->GetArrayLength(values);
@@ -234,6 +236,42 @@
     return err == OK;
 }
 
+jboolean NativeSensorService::sendRuntimeSensorAdditionalInfo(JNIEnv* env, jint handle, jint type,
+                                                              jint serial, jlong timestamp,
+                                                              jfloatArray values) {
+    if (mService == nullptr) {
+        ALOGD("Dropping sendRuntimeSensorAdditionalInfo, sensor service not available.");
+        return false;
+    }
+
+    sensors_event_t event{
+            .version = sizeof(sensors_event_t),
+            .sensor = handle,
+            .type = SENSOR_TYPE_ADDITIONAL_INFO,
+            .timestamp = timestamp,
+            .additional_info =
+                    (additional_info_event_t){
+                            .type = type,
+                            .serial = serial,
+                    },
+    };
+
+    if (values != nullptr) {
+        int valuesLength = env->GetArrayLength(values);
+        if (valuesLength > 14) {
+            ALOGD("Dropping sendRuntimeSensorAdditionalInfo, number of values exceeds maximum.");
+            return false;
+        }
+        if (valuesLength > 0) {
+            jfloat* sensorValues = env->GetFloatArrayElements(values, nullptr);
+            memcpy(event.additional_info.data_float, sensorValues, valuesLength * sizeof(float));
+        }
+    }
+
+    status_t err = mService->sendRuntimeSensorEvent(event);
+    return err == OK;
+}
+
 NativeSensorService::ProximityActiveListenerDelegate::ProximityActiveListenerDelegate(
         JNIEnv* env, jobject listener)
       : mListener(env->NewGlobalRef(listener)) {}
@@ -326,6 +364,13 @@
     return service->sendRuntimeSensorEvent(env, handle, type, timestamp, values);
 }
 
+static jboolean sendRuntimeSensorAdditionalInfoNative(JNIEnv* env, jclass, jlong ptr, jint handle,
+                                                      jint type, jint serial, jlong timestamp,
+                                                      jfloatArray values) {
+    auto* service = reinterpret_cast<NativeSensorService*>(ptr);
+    return service->sendRuntimeSensorAdditionalInfo(env, handle, type, serial, timestamp, values);
+}
+
 static const JNINativeMethod methods[] = {
         {"startSensorServiceNative", "(L" PROXIMITY_ACTIVE_CLASS ";)J",
          reinterpret_cast<void*>(startSensorServiceNative)},
@@ -340,6 +385,8 @@
          reinterpret_cast<void*>(unregisterRuntimeSensorNative)},
         {"sendRuntimeSensorEventNative", "(JIIJ[F)Z",
          reinterpret_cast<void*>(sendRuntimeSensorEventNative)},
+        {"sendRuntimeSensorAdditionalInfoNative", "(JIIIJ[F)Z",
+         reinterpret_cast<void*>(sendRuntimeSensorAdditionalInfoNative)},
 };
 
 int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) {
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 534dbb1..11dbbdf 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -19,7 +19,6 @@
 #include <aidl/android/hardware/vibrator/IVibrator.h>
 #include <android/binder_parcel.h>
 #include <android/binder_parcel_jni.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
 #include <android/persistable_bundle_aidl.h>
 #include <android_os_vibrator.h>
 #include <nativehelper/JNIHelp.h>
@@ -32,8 +31,6 @@
 #include "core_jni_helpers.h"
 #include "jni.h"
 
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_3 = android::hardware::vibrator::V1_3;
 namespace Aidl = aidl::android::hardware::vibrator;
 
 using aidl::android::os::PersistableBundle;
@@ -80,31 +77,6 @@
     jfieldID timeMillis;
 } sPwlePointClassInfo;
 
-static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
-              static_cast<uint8_t>(Aidl::EffectStrength::LIGHT));
-static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) ==
-              static_cast<uint8_t>(Aidl::EffectStrength::MEDIUM));
-static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) ==
-              static_cast<uint8_t>(Aidl::EffectStrength::STRONG));
-
-static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) ==
-              static_cast<uint8_t>(Aidl::Effect::CLICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) ==
-              static_cast<uint8_t>(Aidl::Effect::DOUBLE_CLICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(Aidl::Effect::TICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(Aidl::Effect::THUD));
-static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(Aidl::Effect::POP));
-static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) ==
-              static_cast<uint8_t>(Aidl::Effect::HEAVY_CLICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) ==
-              static_cast<uint8_t>(Aidl::Effect::RINGTONE_1));
-static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) ==
-              static_cast<uint8_t>(Aidl::Effect::RINGTONE_2));
-static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) ==
-              static_cast<uint8_t>(Aidl::Effect::RINGTONE_15));
-static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
-              static_cast<uint8_t>(Aidl::Effect::TEXTURE_TICK));
-
 static std::shared_ptr<vibrator::HalController> findVibrator(int32_t vibratorId) {
     vibrator::ManagerHalController* manager =
             android_server_vibrator_VibratorManagerService_getManager();
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index e72259f..562e82f 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -17,7 +17,6 @@
         "-Werror",
         "-Wno-unused-parameter",
         "-Wthread-safety",
-
         "-DEGL_EGLEXT_PROTOTYPES",
         "-DGL_GLEXT_PROTOTYPES",
     ],
@@ -41,6 +40,8 @@
         "GnssMeasurementCallback.cpp",
         "GnssNavigationMessage.cpp",
         "GnssNavigationMessageCallback.cpp",
+        "GnssAssistance.cpp",
+        "GnssAssistanceCallback.cpp",
         "GnssPsds.cpp",
         "GnssPsdsCallback.cpp",
         "GnssVisibilityControl.cpp",
@@ -61,7 +62,7 @@
         "libnativehelper",
         "libhardware_legacy",
         "libutils",
-        "android.hardware.gnss-V3-cpp",
+        "android.hardware.gnss-V5-cpp",
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
         "android.hardware.gnss@2.0",
diff --git a/services/core/jni/gnss/Gnss.cpp b/services/core/jni/gnss/Gnss.cpp
index da8928b5..a3fd9aa 100644
--- a/services/core/jni/gnss/Gnss.cpp
+++ b/services/core/jni/gnss/Gnss.cpp
@@ -765,4 +765,15 @@
     return nullptr;
 }
 
+std::unique_ptr<GnssAssistanceInterface> GnssHal::getGnssAssistanceInterface() {
+    if (gnssHalAidl != nullptr) {
+        sp<hardware::gnss::gnss_assistance::IGnssAssistanceInterface> gnssAssistance;
+        auto status = gnssHalAidl->getExtensionGnssAssistanceInterface(&gnssAssistance);
+        if (checkAidlStatus(status, "Unable to get a handle to GnssAssistance")) {
+            return std::make_unique<GnssAssistanceInterface>(gnssAssistance);
+        }
+    }
+    return nullptr;
+}
+
 } // namespace android::gnss
diff --git a/services/core/jni/gnss/Gnss.h b/services/core/jni/gnss/Gnss.h
index 458da8a..2b6b751 100644
--- a/services/core/jni/gnss/Gnss.h
+++ b/services/core/jni/gnss/Gnss.h
@@ -34,6 +34,7 @@
 #include "AGnss.h"
 #include "AGnssRil.h"
 #include "GnssAntennaInfo.h"
+#include "GnssAssistance.h"
 #include "GnssBatching.h"
 #include "GnssCallback.h"
 #include "GnssConfiguration.h"
@@ -115,6 +116,7 @@
     std::unique_ptr<GnssVisibilityControlInterface> getGnssVisibilityControlInterface();
     std::unique_ptr<GnssAntennaInfoInterface> getGnssAntennaInfoInterface();
     std::unique_ptr<GnssPsdsInterface> getGnssPsdsInterface();
+    std::unique_ptr<GnssAssistanceInterface> getGnssAssistanceInterface();
 
     sp<hardware::gnss::IGnssPowerIndication> getGnssPowerIndicationInterface();
     sp<hardware::gnss::V1_0::IGnssNi> getGnssNiInterface();
diff --git a/services/core/jni/gnss/GnssAssistance.cpp b/services/core/jni/gnss/GnssAssistance.cpp
new file mode 100644
index 0000000..fff396e
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistance.cpp
@@ -0,0 +1,2047 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 before <log/log.h> to overwrite the default value.
+
+#define LOG_TAG "GnssAssistanceJni"
+
+#include "GnssAssistance.h"
+
+#include <utils/String16.h>
+
+#include "GnssAssistanceCallback.h"
+#include "Utils.h"
+
+namespace android::gnss {
+
+using GnssConstellationType = android::hardware::gnss::GnssConstellationType;
+using GnssCorrectionComponent = android::hardware::gnss::gnss_assistance::GnssCorrectionComponent;
+using GnssInterval =
+        android::hardware::gnss::gnss_assistance::GnssCorrectionComponent::GnssInterval;
+using GnssSatelliteAlmanac =
+        android::hardware::gnss::gnss_assistance::GnssAlmanac::GnssSatelliteAlmanac;
+using IonosphericCorrection = android::hardware::gnss::gnss_assistance::IonosphericCorrection;
+using PseudorangeCorrection =
+        android::hardware::gnss::gnss_assistance::GnssCorrectionComponent::PseudorangeCorrection;
+using GalileoSatelliteClockModel = android::hardware::gnss::gnss_assistance::
+        GalileoSatelliteEphemeris::GalileoSatelliteClockModel;
+using GalileoSvHealth =
+        android::hardware::gnss::gnss_assistance::GalileoSatelliteEphemeris::GalileoSvHealth;
+using GlonassSatelliteAlmanac =
+        android::hardware::gnss::gnss_assistance::GlonassAlmanac::GlonassSatelliteAlmanac;
+using GlonassSatelliteClockModel = android::hardware::gnss::gnss_assistance::
+        GlonassSatelliteEphemeris::GlonassSatelliteClockModel;
+using GlonassSatelliteOrbitModel = android::hardware::gnss::gnss_assistance::
+        GlonassSatelliteEphemeris::GlonassSatelliteOrbitModel;
+using GnssSignalType = hardware::gnss::GnssSignalType;
+using GnssConstellationType = hardware::gnss::GnssConstellationType;
+using BeidouB1CSatelliteOrbitType =
+        android::hardware::gnss::gnss_assistance::AuxiliaryInformation::BeidouB1CSatelliteOrbitType;
+using QzssSatelliteEphemeris = android::hardware::gnss::gnss_assistance::QzssSatelliteEphemeris;
+
+// Implementation of GnssAssistance (AIDL HAL)
+
+namespace {
+jmethodID method_gnssAssistanceGetGpsAssistance;
+jmethodID method_gnssAssistanceGetGlonassAssistance;
+jmethodID method_gnssAssistanceGetGalileoAssistance;
+jmethodID method_gnssAssistanceGetBeidouAssistance;
+jmethodID method_gnssAssistanceGetQzssAssistance;
+
+jmethodID method_listSize;
+jmethodID method_listGet;
+
+jmethodID method_gnssAlmanacGetIssueDateMillis;
+jmethodID method_gnssAlmanacGetIoda;
+jmethodID method_gnssAlmanacGetWeekNumber;
+jmethodID method_gnssAlmanacGetToaSeconds;
+jmethodID method_gnssAlmanacGetSatelliteAlmanacs;
+jmethodID method_gnssAlmanacIsCompleteAlmanacProvided;
+jmethodID method_satelliteAlmanacGetSvid;
+jmethodID method_satelliteAlmanacGetSvHealth;
+jmethodID method_satelliteAlmanacGetAf0;
+jmethodID method_satelliteAlmanacGetAf1;
+jmethodID method_satelliteAlmanacGetEccentricity;
+jmethodID method_satelliteAlmanacGetInclination;
+jmethodID method_satelliteAlmanacGetM0;
+jmethodID method_satelliteAlmanacGetOmega;
+jmethodID method_satelliteAlmanacGetOmega0;
+jmethodID method_satelliteAlmanacGetOmegaDot;
+jmethodID method_satelliteAlmanacGetRootA;
+
+jmethodID method_satelliteEphemerisTimeGetIode;
+jmethodID method_satelliteEphemerisTimeGetToeSeconds;
+jmethodID method_satelliteEphemerisTimeGetWeekNumber;
+
+jmethodID method_keplerianOrbitModelGetDeltaN;
+jmethodID method_keplerianOrbitModelGetEccentricity;
+jmethodID method_keplerianOrbitModelGetI0;
+jmethodID method_keplerianOrbitModelGetIDot;
+jmethodID method_keplerianOrbitModelGetM0;
+jmethodID method_keplerianOrbitModelGetOmega;
+jmethodID method_keplerianOrbitModelGetOmega0;
+jmethodID method_keplerianOrbitModelGetOmegaDot;
+jmethodID method_keplerianOrbitModelGetRootA;
+jmethodID method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation;
+jmethodID method_secondOrderHarmonicPerturbationGetCic;
+jmethodID method_secondOrderHarmonicPerturbationGetCis;
+jmethodID method_secondOrderHarmonicPerturbationGetCrc;
+jmethodID method_secondOrderHarmonicPerturbationGetCrs;
+jmethodID method_secondOrderHarmonicPerturbationGetCuc;
+jmethodID method_secondOrderHarmonicPerturbationGetCus;
+
+jmethodID method_klobucharIonosphericModelGetAlpha0;
+jmethodID method_klobucharIonosphericModelGetAlpha1;
+jmethodID method_klobucharIonosphericModelGetAlpha2;
+jmethodID method_klobucharIonosphericModelGetAlpha3;
+jmethodID method_klobucharIonosphericModelGetBeta0;
+jmethodID method_klobucharIonosphericModelGetBeta1;
+jmethodID method_klobucharIonosphericModelGetBeta2;
+jmethodID method_klobucharIonosphericModelGetBeta3;
+
+jmethodID method_utcModelGetA0;
+jmethodID method_utcModelGetA1;
+jmethodID method_utcModelGetTimeOfWeek;
+jmethodID method_utcModelGetWeekNumber;
+
+jmethodID method_leapSecondsModelGetDayNumberLeapSecondsFuture;
+jmethodID method_leapSecondsModelGetLeapSeconds;
+jmethodID method_leapSecondsModelGetLeapSecondsFuture;
+jmethodID method_leapSecondsModelGetWeekNumberLeapSecondsFuture;
+
+jmethodID method_timeModelsGetTimeOfWeek;
+jmethodID method_timeModelsGetToGnss;
+jmethodID method_timeModelsGetWeekNumber;
+jmethodID method_timeModelsGetA0;
+jmethodID method_timeModelsGetA1;
+
+jmethodID method_realTimeIntegrityModelGetBadSvid;
+jmethodID method_realTimeIntegrityModelGetBadSignalTypes;
+jmethodID method_realTimeIntegrityModelGetStartDateSeconds;
+jmethodID method_realTimeIntegrityModelGetEndDateSeconds;
+jmethodID method_realTimeIntegrityModelGetPublishDateSeconds;
+jmethodID method_realTimeIntegrityModelGetAdvisoryNumber;
+jmethodID method_realTimeIntegrityModelGetAdvisoryType;
+
+jmethodID method_gnssSignalTypeGetConstellationType;
+jmethodID method_gnssSignalTypeGetCarrierFrequencyHz;
+jmethodID method_gnssSignalTypeGetCodeType;
+
+jmethodID method_auxiliaryInformationGetSvid;
+jmethodID method_auxiliaryInformationGetAvailableSignalTypes;
+jmethodID method_auxiliaryInformationGetFrequencyChannelNumber;
+jmethodID method_auxiliaryInformationGetSatType;
+
+jmethodID method_satelliteCorrectionGetSvid;
+jmethodID method_satelliteCorrectionGetIonosphericCorrections;
+jmethodID method_ionosphericCorrectionGetCarrierFrequencyHz;
+jmethodID method_ionosphericCorrectionGetIonosphericCorrection;
+jmethodID method_gnssCorrectionComponentGetPseudorangeCorrection;
+jmethodID method_gnssCorrectionComponentGetSourceKey;
+jmethodID method_gnssCorrectionComponentGetValidityInterval;
+jmethodID method_pseudorangeCorrectionGetCorrectionMeters;
+jmethodID method_pseudorangeCorrectionGetCorrectionUncertaintyMeters;
+jmethodID method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond;
+jmethodID method_gnssIntervalGetStartMillisSinceGpsEpoch;
+jmethodID method_gnssIntervalGetEndMillisSinceGpsEpoch;
+
+jmethodID method_gpsAssistanceGetAlmanac;
+jmethodID method_gpsAssistanceGetIonosphericModel;
+jmethodID method_gpsAssistanceGetUtcModel;
+jmethodID method_gpsAssistanceGetLeapSecondsModel;
+jmethodID method_gpsAssistanceGetTimeModels;
+jmethodID method_gpsAssistanceGetSatelliteEphemeris;
+jmethodID method_gpsAssistanceGetRealTimeIntegrityModels;
+jmethodID method_gpsAssistanceGetSatelliteCorrections;
+jmethodID method_gpsSatelliteEphemerisGetSvid;
+jmethodID method_gpsSatelliteEphemerisGetGpsL2Params;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_gpsSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_gpsL2ParamsGetL2Code;
+jmethodID method_gpsL2ParamsGetL2Flag;
+jmethodID method_gpsSatelliteClockModelGetAf0;
+jmethodID method_gpsSatelliteClockModelGetAf1;
+jmethodID method_gpsSatelliteClockModelGetAf2;
+jmethodID method_gpsSatelliteClockModelGetTgd;
+jmethodID method_gpsSatelliteClockModelGetIodc;
+jmethodID method_gpsSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_gpsSatelliteHealthGetFitInt;
+jmethodID method_gpsSatelliteHealthGetSvAccur;
+jmethodID method_gpsSatelliteHealthGetSvHealth;
+
+jmethodID method_beidouAssistanceGetAlmanac;
+jmethodID method_beidouAssistanceGetIonosphericModel;
+jmethodID method_beidouAssistanceGetUtcModel;
+jmethodID method_beidouAssistanceGetLeapSecondsModel;
+jmethodID method_beidouAssistanceGetTimeModels;
+jmethodID method_beidouAssistanceGetSatelliteEphemeris;
+jmethodID method_beidouAssistanceGetSatelliteCorrections;
+jmethodID method_beidouAssistanceGetRealTimeIntegrityModels;
+jmethodID method_beidouSatelliteEphemerisGetSvid;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_beidouSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_beidouSatelliteClockModelGetAf0;
+jmethodID method_beidouSatelliteClockModelGetAf1;
+jmethodID method_beidouSatelliteClockModelGetAf2;
+jmethodID method_beidouSatelliteClockModelGetAodc;
+jmethodID method_beidouSatelliteClockModelGetTgd1;
+jmethodID method_beidouSatelliteClockModelGetTgd2;
+jmethodID method_beidouSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_beidouSatelliteHealthGetSatH1;
+jmethodID method_beidouSatelliteHealthGetSvAccur;
+jmethodID method_beidouSatelliteEphemerisTimeGetIode;
+jmethodID method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber;
+jmethodID method_beidouSatelliteEphemerisTimeGetToeSeconds;
+
+jmethodID method_galileoAssistanceGetAlmanac;
+jmethodID method_galileoAssistanceGetIonosphericModel;
+jmethodID method_galileoAssistanceGetUtcModel;
+jmethodID method_galileoAssistanceGetLeapSecondsModel;
+jmethodID method_galileoAssistanceGetTimeModels;
+jmethodID method_galileoAssistanceGetSatelliteEphemeris;
+jmethodID method_galileoAssistanceGetSatelliteCorrections;
+jmethodID method_galileoAssistanceGetRealTimeIntegrityModels;
+jmethodID method_galileoSatelliteEphemerisGetSvid;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteClockModels;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_galileoSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_galileoSatelliteClockModelGetAf0;
+jmethodID method_galileoSatelliteClockModelGetAf1;
+jmethodID method_galileoSatelliteClockModelGetAf2;
+jmethodID method_galileoSatelliteClockModelGetBgdSeconds;
+jmethodID method_galileoSatelliteClockModelGetSatelliteClockType;
+jmethodID method_galileoSatelliteClockModelGetSisaMeters;
+jmethodID method_galileoSatelliteClockModelGetTimeOfClockSeconds;
+jmethodID method_galileoSvHealthGetDataValidityStatusE1b;
+jmethodID method_galileoSvHealthGetDataValidityStatusE5a;
+jmethodID method_galileoSvHealthGetDataValidityStatusE5b;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE1b;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE5a;
+jmethodID method_galileoSvHealthGetSignalHealthStatusE5b;
+jmethodID method_galileoIonosphericModelGetAi0;
+jmethodID method_galileoIonosphericModelGetAi1;
+jmethodID method_galileoIonosphericModelGetAi2;
+
+jmethodID method_glonassAssistanceGetAlmanac;
+jmethodID method_glonassAssistanceGetUtcModel;
+jmethodID method_glonassAssistanceGetTimeModels;
+jmethodID method_glonassAssistanceGetSatelliteEphemeris;
+jmethodID method_glonassAssistanceGetSatelliteCorrections;
+jmethodID method_glonassAlmanacGetIssueDateMillis;
+jmethodID method_glonassAlmanacGetSatelliteAlmanacs;
+jmethodID method_glonassSatelliteAlmanacGetDeltaI;
+jmethodID method_glonassSatelliteAlmanacGetDeltaT;
+jmethodID method_glonassSatelliteAlmanacGetDeltaTDot;
+jmethodID method_glonassSatelliteAlmanacGetEccentricity;
+jmethodID method_glonassSatelliteAlmanacGetFrequencyChannelNumber;
+jmethodID method_glonassSatelliteAlmanacGetLambda;
+jmethodID method_glonassSatelliteAlmanacGetOmega;
+jmethodID method_glonassSatelliteAlmanacGetSlotNumber;
+jmethodID method_glonassSatelliteAlmanacGetHealthState;
+jmethodID method_glonassSatelliteAlmanacGetTLambda;
+jmethodID method_glonassSatelliteAlmanacGetTau;
+jmethodID method_glonassSatelliteAlmanacGetIsGlonassM;
+jmethodID method_glonassSatelliteAlmanacGetCalendarDayNumber;
+jmethodID method_glonassSatelliteEphemerisGetAgeInDays;
+jmethodID method_glonassSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_glonassSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_glonassSatelliteEphemerisGetHealthState;
+jmethodID method_glonassSatelliteEphemerisGetSlotNumber;
+jmethodID method_glonassSatelliteEphemerisGetFrameTimeSeconds;
+jmethodID method_glonassSatelliteEphemerisGetUpdateIntervalMinutes;
+jmethodID method_glonassSatelliteEphemerisGetIsGlonassM;
+jmethodID method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd;
+
+jmethodID method_glonassSatelliteOrbitModelGetX;
+jmethodID method_glonassSatelliteOrbitModelGetY;
+jmethodID method_glonassSatelliteOrbitModelGetZ;
+jmethodID method_glonassSatelliteOrbitModelGetXAccel;
+jmethodID method_glonassSatelliteOrbitModelGetYAccel;
+jmethodID method_glonassSatelliteOrbitModelGetZAccel;
+jmethodID method_glonassSatelliteOrbitModelGetXDot;
+jmethodID method_glonassSatelliteOrbitModelGetYDot;
+jmethodID method_glonassSatelliteOrbitModelGetZDot;
+jmethodID method_glonassSatelliteClockModelGetClockBias;
+jmethodID method_glonassSatelliteClockModelGetFrequencyBias;
+jmethodID method_glonassSatelliteClockModelGetFrequencyChannelNumber;
+jmethodID method_glonassSatelliteClockModelGetTimeOfClockSeconds;
+
+jmethodID method_qzssAssistanceGetAlmanac;
+jmethodID method_qzssAssistanceGetIonosphericModel;
+jmethodID method_qzssAssistanceGetUtcModel;
+jmethodID method_qzssAssistanceGetLeapSecondsModel;
+jmethodID method_qzssAssistanceGetTimeModels;
+jmethodID method_qzssAssistanceGetSatelliteEphemeris;
+jmethodID method_qzssAssistanceGetSatelliteCorrections;
+jmethodID method_qzssAssistanceGetRealTimeIntegrityModels;
+jmethodID method_qzssSatelliteEphemerisGetSvid;
+jmethodID method_qzssSatelliteEphemerisGetGpsL2Params;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteClockModel;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteOrbitModel;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteHealth;
+jmethodID method_qzssSatelliteEphemerisGetSatelliteEphemerisTime;
+jmethodID method_qzssSatelliteClockModelGetAf0;
+jmethodID method_qzssSatelliteClockModelGetAf1;
+jmethodID method_qzssSatelliteClockModelGetAf2;
+jmethodID method_qzssSatelliteClockModelGetAodc;
+jmethodID method_qzssSatelliteClockModelGetTgd1;
+jmethodID method_qzssSatelliteClockModelGetTgd2;
+jmethodID method_qzssSatelliteClockModelGetTimeOfClockSeconds;
+} // namespace
+
+void GnssAssistance_class_init_once(JNIEnv* env, jclass clazz) {
+    // Get the methods of GnssAssistance class.
+    jclass gnssAssistanceClass = env->FindClass("android/location/GnssAssistance");
+
+    method_gnssAssistanceGetGpsAssistance =
+            env->GetMethodID(gnssAssistanceClass, "getGpsAssistance",
+                             "()Landroid/location/GpsAssistance;");
+    method_gnssAssistanceGetGlonassAssistance =
+            env->GetMethodID(gnssAssistanceClass, "getGlonassAssistance",
+                             "()Landroid/location/GlonassAssistance;");
+    method_gnssAssistanceGetGalileoAssistance =
+            env->GetMethodID(gnssAssistanceClass, "getGalileoAssistance",
+                             "()Landroid/location/GalileoAssistance;");
+    method_gnssAssistanceGetBeidouAssistance =
+            env->GetMethodID(gnssAssistanceClass, "getBeidouAssistance",
+                             "()Landroid/location/BeidouAssistance;");
+    method_gnssAssistanceGetQzssAssistance =
+            env->GetMethodID(gnssAssistanceClass, "getQzssAssistance",
+                             "()Landroid/location/QzssAssistance;");
+
+    // Get the methods of List class.
+    jclass listClass = env->FindClass("java/util/List");
+
+    method_listSize = env->GetMethodID(listClass, "size", "()I");
+    method_listGet = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
+
+    // Get the methods of GnssAlmanac class.
+    jclass gnssAlmanacClass = env->FindClass("android/location/GnssAlmanac");
+
+    method_gnssAlmanacGetIssueDateMillis =
+            env->GetMethodID(gnssAlmanacClass, "getIssueDateMillis", "()J");
+    method_gnssAlmanacGetIoda = env->GetMethodID(gnssAlmanacClass, "getIoda", "()I");
+    method_gnssAlmanacGetWeekNumber = env->GetMethodID(gnssAlmanacClass, "getWeekNumber", "()I");
+    method_gnssAlmanacGetToaSeconds = env->GetMethodID(gnssAlmanacClass, "getToaSeconds", "()I");
+    method_gnssAlmanacGetSatelliteAlmanacs =
+            env->GetMethodID(gnssAlmanacClass, "getGnssSatelliteAlmanacs", "()Ljava/util/List;");
+    method_gnssAlmanacIsCompleteAlmanacProvided =
+            env->GetMethodID(gnssAlmanacClass, "isCompleteAlmanacProvided", "()Z");
+
+    // Get the methods of SatelliteAlmanac class.
+    jclass satelliteAlmanacClass =
+            env->FindClass("android/location/GnssAlmanac$GnssSatelliteAlmanac");
+
+    method_satelliteAlmanacGetSvid = env->GetMethodID(satelliteAlmanacClass, "getSvid", "()I");
+    method_satelliteAlmanacGetSvHealth =
+            env->GetMethodID(satelliteAlmanacClass, "getSvHealth", "()I");
+    method_satelliteAlmanacGetAf0 = env->GetMethodID(satelliteAlmanacClass, "getAf0", "()D");
+    method_satelliteAlmanacGetAf1 = env->GetMethodID(satelliteAlmanacClass, "getAf1", "()D");
+    method_satelliteAlmanacGetEccentricity =
+            env->GetMethodID(satelliteAlmanacClass, "getEccentricity", "()D");
+    method_satelliteAlmanacGetInclination =
+            env->GetMethodID(satelliteAlmanacClass, "getInclination", "()D");
+    method_satelliteAlmanacGetM0 = env->GetMethodID(satelliteAlmanacClass, "getM0", "()D");
+    method_satelliteAlmanacGetOmega = env->GetMethodID(satelliteAlmanacClass, "getOmega", "()D");
+    method_satelliteAlmanacGetOmega0 = env->GetMethodID(satelliteAlmanacClass, "getOmega0", "()D");
+    method_satelliteAlmanacGetOmegaDot =
+            env->GetMethodID(satelliteAlmanacClass, "getOmegaDot", "()D");
+    method_satelliteAlmanacGetRootA = env->GetMethodID(satelliteAlmanacClass, "getRootA", "()D");
+
+    // Get the mothods of SatelliteEphemerisTime class.
+    jclass satelliteEphemerisTimeClass = env->FindClass("android/location/SatelliteEphemerisTime");
+
+    method_satelliteEphemerisTimeGetIode =
+            env->GetMethodID(satelliteEphemerisTimeClass, "getIode", "()I");
+    method_satelliteEphemerisTimeGetToeSeconds =
+            env->GetMethodID(satelliteEphemerisTimeClass, "getToeSeconds", "()I");
+    method_satelliteEphemerisTimeGetWeekNumber =
+            env->GetMethodID(satelliteEphemerisTimeClass, "getWeekNumber", "()I");
+
+    // Get the mothods of KeplerianOrbitModel class.
+    jclass keplerianOrbitModelClass = env->FindClass("android/location/KeplerianOrbitModel");
+
+    method_keplerianOrbitModelGetDeltaN =
+            env->GetMethodID(keplerianOrbitModelClass, "getDeltaN", "()D");
+    method_keplerianOrbitModelGetEccentricity =
+            env->GetMethodID(keplerianOrbitModelClass, "getEccentricity", "()D");
+    method_keplerianOrbitModelGetI0 = env->GetMethodID(keplerianOrbitModelClass, "getI0", "()D");
+    method_keplerianOrbitModelGetIDot =
+            env->GetMethodID(keplerianOrbitModelClass, "getIDot", "()D");
+    method_keplerianOrbitModelGetM0 = env->GetMethodID(keplerianOrbitModelClass, "getM0", "()D");
+    method_keplerianOrbitModelGetOmega =
+            env->GetMethodID(keplerianOrbitModelClass, "getOmega", "()D");
+    method_keplerianOrbitModelGetOmega0 =
+            env->GetMethodID(keplerianOrbitModelClass, "getOmega0", "()D");
+    method_keplerianOrbitModelGetOmegaDot =
+            env->GetMethodID(keplerianOrbitModelClass, "getOmegaDot", "()D");
+    method_keplerianOrbitModelGetRootA =
+            env->GetMethodID(keplerianOrbitModelClass, "getRootA", "()D");
+    method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation =
+            env->GetMethodID(keplerianOrbitModelClass, "getSecondOrderHarmonicPerturbation",
+                             "()Landroid/location/"
+                             "KeplerianOrbitModel$SecondOrderHarmonicPerturbation;");
+
+    // Get the methods of SecondOrderHarmonicPerturbation class.
+    jclass secondOrderHarmonicPerturbationClass =
+            env->FindClass("android/location/KeplerianOrbitModel$SecondOrderHarmonicPerturbation");
+
+    method_secondOrderHarmonicPerturbationGetCic =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCic", "()D");
+    method_secondOrderHarmonicPerturbationGetCis =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCis", "()D");
+    method_secondOrderHarmonicPerturbationGetCrc =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCrc", "()D");
+    method_secondOrderHarmonicPerturbationGetCrs =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCrs", "()D");
+    method_secondOrderHarmonicPerturbationGetCuc =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCuc", "()D");
+    method_secondOrderHarmonicPerturbationGetCus =
+            env->GetMethodID(secondOrderHarmonicPerturbationClass, "getCus", "()D");
+
+    // Get the methods of KlobucharIonosphericModel class.
+    jclass klobucharIonosphericModelClass =
+            env->FindClass("android/location/KlobucharIonosphericModel");
+
+    method_klobucharIonosphericModelGetAlpha0 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getAlpha0", "()D");
+    method_klobucharIonosphericModelGetAlpha1 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getAlpha1", "()D");
+    method_klobucharIonosphericModelGetAlpha2 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getAlpha2", "()D");
+    method_klobucharIonosphericModelGetAlpha3 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getAlpha3", "()D");
+    method_klobucharIonosphericModelGetBeta0 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getBeta0", "()D");
+    method_klobucharIonosphericModelGetBeta1 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getBeta1", "()D");
+    method_klobucharIonosphericModelGetBeta2 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getBeta2", "()D");
+    method_klobucharIonosphericModelGetBeta3 =
+            env->GetMethodID(klobucharIonosphericModelClass, "getBeta3", "()D");
+
+    // Get the methods of UtcModel class.
+    jclass utcModelClass = env->FindClass("android/location/UtcModel");
+
+    method_utcModelGetA0 = env->GetMethodID(utcModelClass, "getA0", "()D");
+    method_utcModelGetA1 = env->GetMethodID(utcModelClass, "getA1", "()D");
+    method_utcModelGetTimeOfWeek = env->GetMethodID(utcModelClass, "getTimeOfWeek", "()I");
+    method_utcModelGetWeekNumber = env->GetMethodID(utcModelClass, "getWeekNumber", "()I");
+
+    // Get the methods of LeapSecondsModel class.
+    jclass leapSecondsModelClass = env->FindClass("android/location/LeapSecondsModel");
+
+    method_leapSecondsModelGetDayNumberLeapSecondsFuture =
+            env->GetMethodID(leapSecondsModelClass, "getDayNumberLeapSecondsFuture", "()I");
+    method_leapSecondsModelGetLeapSeconds =
+            env->GetMethodID(leapSecondsModelClass, "getLeapSeconds", "()I");
+    method_leapSecondsModelGetLeapSecondsFuture =
+            env->GetMethodID(leapSecondsModelClass, "getLeapSecondsFuture", "()I");
+    method_leapSecondsModelGetWeekNumberLeapSecondsFuture =
+            env->GetMethodID(leapSecondsModelClass, "getWeekNumberLeapSecondsFuture", "()I");
+
+    // Get the methods of TimeModel class.
+    jclass timeModelsClass = env->FindClass("android/location/TimeModel");
+
+    method_timeModelsGetTimeOfWeek = env->GetMethodID(timeModelsClass, "getTimeOfWeek", "()I");
+    method_timeModelsGetToGnss = env->GetMethodID(timeModelsClass, "getToGnss", "()I");
+    method_timeModelsGetWeekNumber = env->GetMethodID(timeModelsClass, "getWeekNumber", "()I");
+    method_timeModelsGetA0 = env->GetMethodID(timeModelsClass, "getA0", "()D");
+    method_timeModelsGetA1 = env->GetMethodID(timeModelsClass, "getA1", "()D");
+
+    // Get the methods of AuxiliaryInformation class.
+    jclass auxiliaryInformationClass = env->FindClass("android/location/AuxiliaryInformation");
+
+    method_auxiliaryInformationGetSvid =
+            env->GetMethodID(auxiliaryInformationClass, "getSvid", "()I");
+    method_auxiliaryInformationGetAvailableSignalTypes =
+            env->GetMethodID(auxiliaryInformationClass, "getAvailableSignalTypes",
+                             "()Ljava/util/List;");
+    method_auxiliaryInformationGetFrequencyChannelNumber =
+            env->GetMethodID(auxiliaryInformationClass, "getFrequencyChannelNumber", "()I");
+    method_auxiliaryInformationGetSatType =
+            env->GetMethodID(auxiliaryInformationClass, "getSatType", "()I");
+
+    // Get the methods of RealTimeIntegrityModel
+    jclass realTimeIntegrityModelClass = env->FindClass("android/location/RealTimeIntegrityModel");
+
+    method_realTimeIntegrityModelGetBadSvid =
+            env->GetMethodID(realTimeIntegrityModelClass, "getBadSvid", "()I");
+    method_realTimeIntegrityModelGetBadSignalTypes =
+            env->GetMethodID(realTimeIntegrityModelClass, "getBadSignalTypes",
+                             "()Ljava/util/List;");
+    method_realTimeIntegrityModelGetStartDateSeconds =
+            env->GetMethodID(realTimeIntegrityModelClass, "getStartDateSeconds", "()J");
+    method_realTimeIntegrityModelGetEndDateSeconds =
+            env->GetMethodID(realTimeIntegrityModelClass, "getEndDateSeconds", "()J");
+    method_realTimeIntegrityModelGetPublishDateSeconds =
+            env->GetMethodID(realTimeIntegrityModelClass, "getPublishDateSeconds", "()J");
+    method_realTimeIntegrityModelGetAdvisoryNumber =
+            env->GetMethodID(realTimeIntegrityModelClass, "getAdvisoryNumber",
+                             "()Ljava/lang/String;");
+    method_realTimeIntegrityModelGetAdvisoryType =
+            env->GetMethodID(realTimeIntegrityModelClass, "getAdvisoryType",
+                             "()Ljava/lang/String;");
+
+    // Get the methods of GnssSignalType class.
+    jclass gnssSignalTypeClass = env->FindClass("android/location/GnssSignalType");
+
+    method_gnssSignalTypeGetConstellationType =
+            env->GetMethodID(gnssSignalTypeClass, "getConstellationType", "()I");
+    method_gnssSignalTypeGetCarrierFrequencyHz =
+            env->GetMethodID(gnssSignalTypeClass, "getCarrierFrequencyHz", "()D");
+    method_gnssSignalTypeGetCodeType =
+            env->GetMethodID(gnssSignalTypeClass, "getCodeType", "()Ljava/lang/String;");
+
+    // Get the methods of SatelliteCorrection class.
+    jclass satelliteCorrectionClass =
+            env->FindClass("android/location/GnssAssistance$GnssSatelliteCorrections");
+
+    method_satelliteCorrectionGetSvid =
+            env->GetMethodID(satelliteCorrectionClass, "getSvid", "()I");
+    method_satelliteCorrectionGetIonosphericCorrections =
+            env->GetMethodID(satelliteCorrectionClass, "getIonosphericCorrections",
+                             "()Ljava/util/List;");
+
+    // Get the methods of IonosphericCorrection class.
+    jclass ionosphericCorrectionClass = env->FindClass("android/location/IonosphericCorrection");
+
+    method_ionosphericCorrectionGetCarrierFrequencyHz =
+            env->GetMethodID(ionosphericCorrectionClass, "getCarrierFrequencyHz", "()J");
+    method_ionosphericCorrectionGetIonosphericCorrection =
+            env->GetMethodID(ionosphericCorrectionClass, "getIonosphericCorrection",
+                             "()Landroid/location/GnssCorrectionComponent;");
+
+    // Get the methods of GnssCorrectionComponent class.
+    jclass gnssCorrectionComponentClass =
+            env->FindClass("android/location/GnssCorrectionComponent");
+
+    method_gnssCorrectionComponentGetPseudorangeCorrection =
+            env->GetMethodID(gnssCorrectionComponentClass, "getPseudorangeCorrection",
+                             "()Landroid/location/GnssCorrectionComponent$PseudorangeCorrection;");
+    method_gnssCorrectionComponentGetSourceKey =
+            env->GetMethodID(gnssCorrectionComponentClass, "getSourceKey", "()Ljava/lang/String;");
+    method_gnssCorrectionComponentGetValidityInterval =
+            env->GetMethodID(gnssCorrectionComponentClass, "getValidityInterval",
+                             "()Landroid/location/GnssCorrectionComponent$GnssInterval;");
+
+    // Get the methods of PseudorangeCorrection class.
+    jclass pseudorangeCorrectionClass =
+            env->FindClass("android/location/GnssCorrectionComponent$PseudorangeCorrection");
+
+    method_pseudorangeCorrectionGetCorrectionMeters =
+            env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionMeters", "()D");
+    method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond =
+            env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionRateMetersPerSecond", "()D");
+    method_pseudorangeCorrectionGetCorrectionUncertaintyMeters =
+            env->GetMethodID(pseudorangeCorrectionClass, "getCorrectionUncertaintyMeters", "()D");
+
+    // Get the methods of GnssInterval class.
+    jclass gnssIntervalClass =
+            env->FindClass("android/location/GnssCorrectionComponent$GnssInterval");
+
+    method_gnssIntervalGetStartMillisSinceGpsEpoch =
+            env->GetMethodID(gnssIntervalClass, "getStartMillisSinceGpsEpoch", "()J");
+    method_gnssIntervalGetEndMillisSinceGpsEpoch =
+            env->GetMethodID(gnssIntervalClass, "getEndMillisSinceGpsEpoch", "()J");
+
+    // Get the methods of GpsAssistance class.
+    jclass gpsAssistanceClass = env->FindClass("android/location/GpsAssistance");
+
+    method_gpsAssistanceGetAlmanac =
+            env->GetMethodID(gpsAssistanceClass, "getAlmanac", "()Landroid/location/GnssAlmanac;");
+    method_gpsAssistanceGetIonosphericModel =
+            env->GetMethodID(gpsAssistanceClass, "getIonosphericModel",
+                             "()Landroid/location/KlobucharIonosphericModel;");
+    method_gpsAssistanceGetUtcModel =
+            env->GetMethodID(gpsAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+    method_gpsAssistanceGetLeapSecondsModel =
+            env->GetMethodID(gpsAssistanceClass, "getLeapSecondsModel",
+                             "()Landroid/location/LeapSecondsModel;");
+    method_gpsAssistanceGetTimeModels =
+            env->GetMethodID(gpsAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+    method_gpsAssistanceGetSatelliteEphemeris =
+            env->GetMethodID(gpsAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+    method_gpsAssistanceGetRealTimeIntegrityModels =
+            env->GetMethodID(gpsAssistanceClass, "getRealTimeIntegrityModels",
+                             "()Ljava/util/List;");
+    method_gpsAssistanceGetSatelliteCorrections =
+            env->GetMethodID(gpsAssistanceClass, "getSatelliteCorrections", "()Ljava/util/List;");
+
+    // Get the methods of GpsSatelliteEphemeris class.
+    jclass gpsSatelliteEphemerisClass = env->FindClass("android/location/GpsSatelliteEphemeris");
+
+    method_gpsSatelliteEphemerisGetSvid =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getSvid", "()I");
+    method_gpsSatelliteEphemerisGetGpsL2Params =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getGpsL2Params",
+                             "()Landroid/location/GpsSatelliteEphemeris$GpsL2Params;");
+    method_gpsSatelliteEphemerisGetSatelliteClockModel =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteClockModel",
+                             "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteClockModel;");
+    method_gpsSatelliteEphemerisGetSatelliteOrbitModel =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteOrbitModel",
+                             "()Landroid/location/KeplerianOrbitModel;");
+    method_gpsSatelliteEphemerisGetSatelliteHealth =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteHealth",
+                             "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteHealth;");
+    method_gpsSatelliteEphemerisGetSatelliteEphemerisTime =
+            env->GetMethodID(gpsSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+                             "()Landroid/location/SatelliteEphemerisTime;");
+
+    // Get the methods of GpsL2Params class.
+    jclass gpsL2ParamsClass = env->FindClass("android/location/GpsSatelliteEphemeris$GpsL2Params");
+    method_gpsL2ParamsGetL2Code = env->GetMethodID(gpsL2ParamsClass, "getL2Code", "()I");
+    method_gpsL2ParamsGetL2Flag = env->GetMethodID(gpsL2ParamsClass, "getL2Flag", "()I");
+
+    // Get the methods of GpsSatelliteClockModel class.
+    jclass gpsSatelliteClockModelClass =
+            env->FindClass("android/location/GpsSatelliteEphemeris$GpsSatelliteClockModel");
+    method_gpsSatelliteClockModelGetAf0 =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getAf0", "()D");
+    method_gpsSatelliteClockModelGetAf1 =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getAf1", "()D");
+    method_gpsSatelliteClockModelGetAf2 =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getAf2", "()D");
+    method_gpsSatelliteClockModelGetTgd =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getTgd", "()D");
+    method_gpsSatelliteClockModelGetIodc =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getIodc", "()I");
+    method_gpsSatelliteClockModelGetTimeOfClockSeconds =
+            env->GetMethodID(gpsSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+    // Get the methods of GpsSatelliteHealth class.
+    jclass gpsSatelliteHealthClass =
+            env->FindClass("android/location/GpsSatelliteEphemeris$GpsSatelliteHealth");
+    method_gpsSatelliteHealthGetFitInt =
+            env->GetMethodID(gpsSatelliteHealthClass, "getFitInt", "()D");
+    method_gpsSatelliteHealthGetSvAccur =
+            env->GetMethodID(gpsSatelliteHealthClass, "getSvAccur", "()D");
+    method_gpsSatelliteHealthGetSvHealth =
+            env->GetMethodID(gpsSatelliteHealthClass, "getSvHealth", "()I");
+
+    // Get the methods of BeidouAssistance class.
+    jclass beidouAssistanceClass = env->FindClass("android/location/BeidouAssistance");
+    method_beidouAssistanceGetAlmanac = env->GetMethodID(beidouAssistanceClass, "getAlmanac",
+                                                         "()Landroid/location/GnssAlmanac;");
+    method_beidouAssistanceGetIonosphericModel =
+            env->GetMethodID(beidouAssistanceClass, "getIonosphericModel",
+                             "()Landroid/location/KlobucharIonosphericModel;");
+    method_beidouAssistanceGetUtcModel =
+            env->GetMethodID(beidouAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+    method_beidouAssistanceGetLeapSecondsModel =
+            env->GetMethodID(beidouAssistanceClass, "getLeapSecondsModel",
+                             "()Landroid/location/LeapSecondsModel;");
+    method_beidouAssistanceGetTimeModels =
+            env->GetMethodID(beidouAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+    method_beidouAssistanceGetSatelliteEphemeris =
+            env->GetMethodID(beidouAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+    method_beidouAssistanceGetSatelliteCorrections =
+            env->GetMethodID(beidouAssistanceClass, "getSatelliteCorrections",
+                             "()Ljava/util/List;");
+    method_beidouAssistanceGetRealTimeIntegrityModels =
+            env->GetMethodID(beidouAssistanceClass, "getRealTimeIntegrityModels",
+                             "()Ljava/util/List;");
+
+    // Get the methods of BeidouSatelliteEphemeris class.
+    jclass beidouSatelliteEphemerisClass =
+            env->FindClass("android/location/BeidouSatelliteEphemeris");
+    method_beidouSatelliteEphemerisGetSvid =
+            env->GetMethodID(beidouSatelliteEphemerisClass, "getSvid", "()I");
+    method_beidouSatelliteEphemerisGetSatelliteClockModel =
+            env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteClockModel",
+                             "()Landroid/location/"
+                             "BeidouSatelliteEphemeris$BeidouSatelliteClockModel;");
+    method_beidouSatelliteEphemerisGetSatelliteOrbitModel =
+            env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteOrbitModel",
+                             "()Landroid/location/KeplerianOrbitModel;");
+    method_beidouSatelliteEphemerisGetSatelliteHealth =
+            env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteHealth",
+                             "()Landroid/location/BeidouSatelliteEphemeris$BeidouSatelliteHealth;");
+    method_beidouSatelliteEphemerisGetSatelliteEphemerisTime =
+            env->GetMethodID(beidouSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+                             "()Landroid/location/"
+                             "BeidouSatelliteEphemeris$BeidouSatelliteEphemerisTime;");
+
+    // Get the methods of BeidouSatelliteClockModel
+    jclass beidouSatelliteClockModelClass =
+            env->FindClass("android/location/BeidouSatelliteEphemeris$BeidouSatelliteClockModel");
+    method_beidouSatelliteClockModelGetAf0 =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getAf0", "()D");
+    method_beidouSatelliteClockModelGetAf1 =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getAf1", "()D");
+    method_beidouSatelliteClockModelGetAf2 =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getAf2", "()D");
+    method_beidouSatelliteClockModelGetAodc =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getAodc", "()I");
+    method_beidouSatelliteClockModelGetTgd1 =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getTgd1", "()D");
+    method_beidouSatelliteClockModelGetTgd2 =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getTgd2", "()D");
+    method_beidouSatelliteClockModelGetTimeOfClockSeconds =
+            env->GetMethodID(beidouSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+    // Get the methods of BeidouSatelliteHealth
+    jclass beidouSatelliteHealthClass =
+            env->FindClass("android/location/BeidouSatelliteEphemeris$BeidouSatelliteHealth");
+    method_beidouSatelliteHealthGetSatH1 =
+            env->GetMethodID(beidouSatelliteHealthClass, "getSatH1", "()I");
+    method_beidouSatelliteHealthGetSvAccur =
+            env->GetMethodID(beidouSatelliteHealthClass, "getSvAccur", "()D");
+
+    // Get the methods of BeidouSatelliteEphemerisTime
+    jclass beidouSatelliteEphemerisTimeClass = env->FindClass(
+            "android/location/BeidouSatelliteEphemeris$BeidouSatelliteEphemerisTime");
+    method_beidouSatelliteEphemerisTimeGetIode =
+            env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getIode", "()I");
+    method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber =
+            env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getBeidouWeekNumber", "()I");
+    method_beidouSatelliteEphemerisTimeGetToeSeconds =
+            env->GetMethodID(beidouSatelliteEphemerisTimeClass, "getToeSeconds", "()I");
+
+    // Get the methods of GalileoAssistance class.
+    jclass galileoAssistanceClass = env->FindClass("android/location/GalileoAssistance");
+    method_galileoAssistanceGetAlmanac = env->GetMethodID(galileoAssistanceClass, "getAlmanac",
+                                                          "()Landroid/location/GnssAlmanac;");
+    method_galileoAssistanceGetIonosphericModel =
+            env->GetMethodID(galileoAssistanceClass, "getIonosphericModel",
+                             "()Landroid/location/KlobucharIonosphericModel;");
+    method_galileoAssistanceGetUtcModel = env->GetMethodID(galileoAssistanceClass, "getUtcModel",
+                                                           "()Landroid/location/UtcModel;");
+    method_galileoAssistanceGetLeapSecondsModel =
+            env->GetMethodID(galileoAssistanceClass, "getLeapSecondsModel",
+                             "()Landroid/location/LeapSecondsModel;");
+    method_galileoAssistanceGetTimeModels =
+            env->GetMethodID(galileoAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+    method_galileoAssistanceGetSatelliteEphemeris =
+            env->GetMethodID(galileoAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+    method_galileoAssistanceGetSatelliteCorrections =
+            env->GetMethodID(galileoAssistanceClass, "getSatelliteCorrections",
+                             "()Ljava/util/List;");
+    method_galileoAssistanceGetRealTimeIntegrityModels =
+            env->GetMethodID(galileoAssistanceClass, "getRealTimeIntegrityModels",
+                             "()Ljava/util/List;");
+
+    // Get the methods of GalileoSatelliteEphemeris class
+    jclass galileoSatelliteEphemerisClass =
+            env->FindClass("android/location/GalileoSatelliteEphemeris");
+    method_galileoSatelliteEphemerisGetSatelliteClockModels =
+            env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteClockModels",
+                             "()Ljava/util/List;");
+    method_galileoSatelliteEphemerisGetSvid =
+            env->GetMethodID(galileoSatelliteEphemerisClass, "getSvid", "()I");
+    method_galileoSatelliteEphemerisGetSatelliteEphemerisTime =
+            env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+                             "()Landroid/location/SatelliteEphemerisTime;");
+    method_galileoSatelliteEphemerisGetSatelliteHealth =
+            env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteHealth",
+                             "()Landroid/location/GalileoSatelliteEphemeris$GalileoSvHealth;");
+    method_galileoSatelliteEphemerisGetSatelliteOrbitModel =
+            env->GetMethodID(galileoSatelliteEphemerisClass, "getSatelliteOrbitModel",
+                             "()Landroid/location/KeplerianOrbitModel;");
+
+    // Get the methods of GalileoSatelliteClockModel class.
+    jclass galileoSatelliteClockModelClass =
+            env->FindClass("android/location/GalileoSatelliteEphemeris$GalileoSatelliteClockModel");
+    method_galileoSatelliteClockModelGetAf0 =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getAf0", "()D");
+    method_galileoSatelliteClockModelGetAf1 =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getAf1", "()D");
+    method_galileoSatelliteClockModelGetAf2 =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getAf2", "()D");
+    method_galileoSatelliteClockModelGetBgdSeconds =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getBgdSeconds", "()D");
+    method_galileoSatelliteClockModelGetSatelliteClockType =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getSatelliteClockType", "()I");
+    method_galileoSatelliteClockModelGetSisaMeters =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getSisaMeters", "()D");
+    method_galileoSatelliteClockModelGetTimeOfClockSeconds =
+            env->GetMethodID(galileoSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+    // Get the methods of GalileoSvHealth class.
+    jclass galileoSvHealthClass =
+            env->FindClass("android/location/GalileoSatelliteEphemeris$GalileoSvHealth");
+    method_galileoSvHealthGetDataValidityStatusE1b =
+            env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE1b", "()I");
+    method_galileoSvHealthGetDataValidityStatusE5a =
+            env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE5a", "()I");
+    method_galileoSvHealthGetDataValidityStatusE5b =
+            env->GetMethodID(galileoSvHealthClass, "getDataValidityStatusE5b", "()I");
+    method_galileoSvHealthGetSignalHealthStatusE1b =
+            env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE1b", "()I");
+    method_galileoSvHealthGetSignalHealthStatusE5a =
+            env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE5a", "()I");
+    method_galileoSvHealthGetSignalHealthStatusE5b =
+            env->GetMethodID(galileoSvHealthClass, "getSignalHealthStatusE5b", "()I");
+
+    // Get the methods of GalileoIonosphericModel class.
+    jclass galileoIonosphericModelClass =
+            env->FindClass("android/location/GalileoIonosphericModel");
+    method_galileoIonosphericModelGetAi0 =
+            env->GetMethodID(galileoIonosphericModelClass, "getAi0", "()D");
+    method_galileoIonosphericModelGetAi1 =
+            env->GetMethodID(galileoIonosphericModelClass, "getAi1", "()D");
+    method_galileoIonosphericModelGetAi2 =
+            env->GetMethodID(galileoIonosphericModelClass, "getAi2", "()D");
+
+    // Get the methods of GlonassAssistance class.
+    jclass glonassAssistanceClass = env->FindClass("android/location/GlonassAssistance");
+    method_glonassAssistanceGetAlmanac = env->GetMethodID(glonassAssistanceClass, "getAlmanac",
+                                                          "()Landroid/location/GlonassAlmanac;");
+    method_glonassAssistanceGetUtcModel = env->GetMethodID(glonassAssistanceClass, "getUtcModel",
+                                                           "()Landroid/location/UtcModel;");
+    method_glonassAssistanceGetTimeModels =
+            env->GetMethodID(glonassAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+    method_glonassAssistanceGetSatelliteEphemeris =
+            env->GetMethodID(glonassAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+    method_glonassAssistanceGetSatelliteCorrections =
+            env->GetMethodID(glonassAssistanceClass, "getSatelliteCorrections",
+                             "()Ljava/util/List;");
+
+    // Get the methods of GlonassAlmanac class.
+    jclass glonassAlmanacClass = env->FindClass("android/location/GlonassAlmanac");
+    method_glonassAlmanacGetIssueDateMillis =
+            env->GetMethodID(glonassAlmanacClass, "getIssueDateMillis", "()J");
+    method_glonassAlmanacGetSatelliteAlmanacs =
+            env->GetMethodID(glonassAlmanacClass, "getSatelliteAlmanacs", "()Ljava/util/List;");
+
+    // Get the methods of GlonassSatelliteAlmanac class
+    jclass glonassSatelliteAlmanacClass =
+            env->FindClass("android/location/GlonassAlmanac$GlonassSatelliteAlmanac");
+    method_glonassSatelliteAlmanacGetDeltaI =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaI", "()D");
+    method_glonassSatelliteAlmanacGetDeltaT =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaT", "()D");
+    method_glonassSatelliteAlmanacGetDeltaTDot =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getDeltaTDot", "()D");
+    method_glonassSatelliteAlmanacGetEccentricity =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getEccentricity", "()D");
+    method_glonassSatelliteAlmanacGetFrequencyChannelNumber =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getFrequencyChannelNumber", "()I");
+    method_glonassSatelliteAlmanacGetLambda =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getLambda", "()D");
+    method_glonassSatelliteAlmanacGetOmega =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getOmega", "()D");
+    method_glonassSatelliteAlmanacGetSlotNumber =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getSlotNumber", "()I");
+    method_glonassSatelliteAlmanacGetHealthState =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getHealthState", "()I");
+    method_glonassSatelliteAlmanacGetTLambda =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getTLambda", "()D");
+    method_glonassSatelliteAlmanacGetTau =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getTau", "()D");
+    method_glonassSatelliteAlmanacGetCalendarDayNumber =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "getCalendarDayNumber", "()I");
+    method_glonassSatelliteAlmanacGetIsGlonassM =
+            env->GetMethodID(glonassSatelliteAlmanacClass, "isGlonassM", "()Z");
+
+    // Get the methods of GlonassSatelliteEphemeris
+    jclass glonassSatelliteEphemerisClass =
+            env->FindClass("android/location/GlonassSatelliteEphemeris");
+    method_glonassSatelliteEphemerisGetAgeInDays =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getAgeInDays", "()I");
+    method_glonassSatelliteEphemerisGetFrameTimeSeconds =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getFrameTimeSeconds", "()D");
+    method_glonassSatelliteEphemerisGetHealthState =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getHealthState", "()I");
+    method_glonassSatelliteEphemerisGetSlotNumber =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getSlotNumber", "()I");
+    method_glonassSatelliteEphemerisGetSatelliteClockModel =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getSatelliteClockModel",
+                             "()Landroid/location/"
+                             "GlonassSatelliteEphemeris$GlonassSatelliteClockModel;");
+    method_glonassSatelliteEphemerisGetSatelliteOrbitModel =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getSatelliteOrbitModel",
+                             "()Landroid/location/"
+                             "GlonassSatelliteEphemeris$GlonassSatelliteOrbitModel;");
+    method_glonassSatelliteEphemerisGetUpdateIntervalMinutes =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "getUpdateIntervalMinutes", "()I");
+    method_glonassSatelliteEphemerisGetIsGlonassM =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "isGlonassM", "()Z");
+    method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd =
+            env->GetMethodID(glonassSatelliteEphemerisClass, "isUpdateIntervalOdd", "()Z");
+
+    // Get the methods of GlonassSatelliteOrbitModel
+    jclass glonassSatelliteOrbitModelClass =
+            env->FindClass("android/location/GlonassSatelliteEphemeris$GlonassSatelliteOrbitModel");
+    method_glonassSatelliteOrbitModelGetX =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getX", "()D");
+    method_glonassSatelliteOrbitModelGetXAccel =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getXAccel", "()D");
+    method_glonassSatelliteOrbitModelGetXDot =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getXDot", "()D");
+    method_glonassSatelliteOrbitModelGetY =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getY", "()D");
+    method_glonassSatelliteOrbitModelGetYAccel =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getYAccel", "()D");
+    method_glonassSatelliteOrbitModelGetYDot =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getYDot", "()D");
+    method_glonassSatelliteOrbitModelGetZ =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getZ", "()D");
+    method_glonassSatelliteOrbitModelGetZAccel =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getZAccel", "()D");
+    method_glonassSatelliteOrbitModelGetZDot =
+            env->GetMethodID(glonassSatelliteOrbitModelClass, "getZDot", "()D");
+
+    // Get the methods of GlonassSatelliteClockModel
+    jclass glonassSatelliteClockModelClass =
+            env->FindClass("android/location/GlonassSatelliteEphemeris$GlonassSatelliteClockModel");
+    method_glonassSatelliteClockModelGetClockBias =
+            env->GetMethodID(glonassSatelliteClockModelClass, "getClockBias", "()D");
+    method_glonassSatelliteClockModelGetFrequencyBias =
+            env->GetMethodID(glonassSatelliteClockModelClass, "getFrequencyBias", "()D");
+    method_glonassSatelliteClockModelGetFrequencyChannelNumber =
+            env->GetMethodID(glonassSatelliteClockModelClass, "getFrequencyChannelNumber", "()I");
+    method_glonassSatelliteClockModelGetTimeOfClockSeconds =
+            env->GetMethodID(glonassSatelliteClockModelClass, "getTimeOfClockSeconds", "()J");
+
+    // Get the methods of QzssAssistance class.
+    jclass qzssAssistanceClass = env->FindClass("android/location/QzssAssistance");
+    method_qzssAssistanceGetAlmanac =
+            env->GetMethodID(qzssAssistanceClass, "getAlmanac", "()Landroid/location/GnssAlmanac;");
+    method_qzssAssistanceGetIonosphericModel =
+            env->GetMethodID(qzssAssistanceClass, "getIonosphericModel",
+                             "()Landroid/location/KlobucharIonosphericModel;");
+    method_qzssAssistanceGetUtcModel =
+            env->GetMethodID(qzssAssistanceClass, "getUtcModel", "()Landroid/location/UtcModel;");
+    method_qzssAssistanceGetLeapSecondsModel =
+            env->GetMethodID(qzssAssistanceClass, "getLeapSecondsModel",
+                             "()Landroid/location/LeapSecondsModel;");
+    method_qzssAssistanceGetTimeModels =
+            env->GetMethodID(qzssAssistanceClass, "getTimeModels", "()Ljava/util/List;");
+    method_qzssAssistanceGetSatelliteEphemeris =
+            env->GetMethodID(qzssAssistanceClass, "getSatelliteEphemeris", "()Ljava/util/List;");
+    method_qzssAssistanceGetSatelliteCorrections =
+            env->GetMethodID(qzssAssistanceClass, "getSatelliteCorrections", "()Ljava/util/List;");
+
+    // Get the methods of QzssSatelliteEphemeris class.
+    jclass qzssSatelliteEphemerisClass = env->FindClass("android/location/QzssSatelliteEphemeris");
+    method_qzssSatelliteEphemerisGetSvid =
+            env->GetMethodID(qzssSatelliteEphemerisClass, "getSvid", "()I");
+    method_qzssSatelliteEphemerisGetGpsL2Params =
+            env->GetMethodID(qzssSatelliteEphemerisClass, "getGpsL2Params",
+                             "()Landroid/location/GpsSatelliteEphemeris$GpsL2Params;");
+    method_qzssSatelliteEphemerisGetSatelliteEphemerisTime =
+            env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteEphemerisTime",
+                             "()Landroid/location/SatelliteEphemerisTime;");
+    method_qzssSatelliteEphemerisGetSatelliteHealth =
+            env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteHealth",
+                             "()Landroid/location/GpsSatelliteEphemeris$GpsSatelliteHealth;");
+    method_qzssSatelliteEphemerisGetSatelliteOrbitModel =
+            env->GetMethodID(qzssSatelliteEphemerisClass, "getSatelliteOrbitModel",
+                             "()Landroid/location/KeplerianOrbitModel;");
+}
+
+GnssAssistanceInterface::GnssAssistanceInterface(
+        const sp<IGnssAssistanceInterface>& iGnssAssistance)
+      : mGnssAssistanceInterface(iGnssAssistance) {
+    assert(mGnssAssistanceInterface != nullptr);
+}
+
+jboolean GnssAssistanceInterface::injectGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj) {
+    GnssAssistance gnssAssistance;
+    GnssAssistanceUtil::setGnssAssistance(env, gnssAssistanceObj, gnssAssistance);
+    auto status = mGnssAssistanceInterface->injectGnssAssistance(gnssAssistance);
+    return checkAidlStatus(status, "IGnssAssistanceInterface injectGnssAssistance() failed.");
+}
+
+jboolean GnssAssistanceInterface::setCallback(const sp<IGnssAssistanceCallback>& callback) {
+    auto status = mGnssAssistanceInterface->setCallback(callback);
+    return checkAidlStatus(status, "IGnssAssistanceInterface setCallback() failed.");
+}
+
+void GnssAssistanceUtil::setGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj,
+                                           GnssAssistance& gnssAssistance) {
+    jobject gpsAssistanceObj =
+            env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGpsAssistance);
+    jobject glonassAssistanceObj =
+            env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGlonassAssistance);
+    jobject qzssAssistanceObj =
+            env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetQzssAssistance);
+    jobject galileoAssistanceObj =
+            env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetGalileoAssistance);
+    jobject beidouAssistanceObj =
+            env->CallObjectMethod(gnssAssistanceObj, method_gnssAssistanceGetBeidouAssistance);
+    GnssAssistanceUtil::setGpsAssistance(env, gpsAssistanceObj, gnssAssistance.gpsAssistance);
+    GnssAssistanceUtil::setGlonassAssistance(env, glonassAssistanceObj,
+                                             gnssAssistance.glonassAssistance);
+    GnssAssistanceUtil::setQzssAssistance(env, qzssAssistanceObj, gnssAssistance.qzssAssistance);
+    GnssAssistanceUtil::setGalileoAssistance(env, galileoAssistanceObj,
+                                             gnssAssistance.galileoAssistance);
+    GnssAssistanceUtil::setBeidouAssistance(env, beidouAssistanceObj,
+                                            gnssAssistance.beidouAssistance);
+    env->DeleteLocalRef(gpsAssistanceObj);
+    env->DeleteLocalRef(glonassAssistanceObj);
+    env->DeleteLocalRef(qzssAssistanceObj);
+    env->DeleteLocalRef(galileoAssistanceObj);
+    env->DeleteLocalRef(beidouAssistanceObj);
+}
+
+void GnssAssistanceUtil::setQzssAssistance(JNIEnv* env, jobject qzssAssistanceObj,
+                                           QzssAssistance& qzssAssistance) {
+    jobject qzssAlmanacObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetAlmanac);
+    jobject qzssIonosphericModelObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetIonosphericModel);
+    jobject qzssUtcModelObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetUtcModel);
+    jobject qzssLeapSecondsModelObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetLeapSecondsModel);
+    jobject qzssTimeModelsObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetTimeModels);
+    jobject qzssSatelliteEphemerisObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetSatelliteEphemeris);
+    jobject qzssSatelliteCorrectionsObj =
+            env->CallObjectMethod(qzssAssistanceObj, method_qzssAssistanceGetSatelliteCorrections);
+    setGnssAlmanac(env, qzssAlmanacObj, qzssAssistance.almanac);
+    setKlobucharIonosphericModel(env, qzssIonosphericModelObj, qzssAssistance.ionosphericModel);
+    setUtcModel(env, qzssUtcModelObj, qzssAssistance.utcModel);
+    setLeapSecondsModel(env, qzssLeapSecondsModelObj, qzssAssistance.leapSecondsModel);
+    setTimeModels(env, qzssTimeModelsObj, qzssAssistance.timeModels);
+    setGpsOrQzssSatelliteEphemeris<QzssSatelliteEphemeris>(env, qzssSatelliteEphemerisObj,
+                                                           qzssAssistance.satelliteEphemeris);
+    setSatelliteCorrections(env, qzssSatelliteCorrectionsObj, qzssAssistance.satelliteCorrections);
+    env->DeleteLocalRef(qzssAlmanacObj);
+    env->DeleteLocalRef(qzssIonosphericModelObj);
+    env->DeleteLocalRef(qzssUtcModelObj);
+    env->DeleteLocalRef(qzssLeapSecondsModelObj);
+    env->DeleteLocalRef(qzssTimeModelsObj);
+    env->DeleteLocalRef(qzssSatelliteEphemerisObj);
+    env->DeleteLocalRef(qzssSatelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGlonassAssistance(JNIEnv* env, jobject glonassAssistanceObj,
+                                              GlonassAssistance& galileoAssistance) {
+    jobject glonassAlmanacObj =
+            env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetAlmanac);
+    jobject utcModelObj =
+            env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetUtcModel);
+    jobject timeModelsObj =
+            env->CallObjectMethod(glonassAssistanceObj, method_glonassAssistanceGetTimeModels);
+    jobject satelliteEphemerisObj =
+            env->CallObjectMethod(glonassAssistanceObj,
+                                  method_glonassAssistanceGetSatelliteEphemeris);
+    jobject satelliteCorrectionsObj =
+            env->CallObjectMethod(glonassAssistanceObj,
+                                  method_glonassAssistanceGetSatelliteCorrections);
+    setGlonassAlmanac(env, glonassAlmanacObj, galileoAssistance.almanac);
+    setUtcModel(env, utcModelObj, galileoAssistance.utcModel);
+    setTimeModels(env, timeModelsObj, galileoAssistance.timeModels);
+    setGlonassSatelliteEphemeris(env, satelliteEphemerisObj, galileoAssistance.satelliteEphemeris);
+    setSatelliteCorrections(env, satelliteCorrectionsObj, galileoAssistance.satelliteCorrections);
+    env->DeleteLocalRef(glonassAlmanacObj);
+    env->DeleteLocalRef(utcModelObj);
+    env->DeleteLocalRef(timeModelsObj);
+    env->DeleteLocalRef(satelliteEphemerisObj);
+    env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGlonassAlmanac(JNIEnv* env, jobject glonassAlmanacObj,
+                                           GlonassAlmanac& glonassAlmanac) {
+    if (glonassAlmanacObj == nullptr) {
+        glonassAlmanac.issueDateMs = -1;
+        return;
+    }
+    jlong issueDateMillis =
+            env->CallLongMethod(glonassAlmanacObj, method_glonassAlmanacGetIssueDateMillis);
+    glonassAlmanac.issueDateMs = issueDateMillis;
+    jobject satelliteAlmanacsObj =
+            env->CallObjectMethod(glonassAlmanacObj, method_glonassAlmanacGetSatelliteAlmanacs);
+    if (satelliteAlmanacsObj == nullptr) return;
+    auto len = env->CallIntMethod(satelliteAlmanacsObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject glonassSatelliteAlmanacObj =
+                env->CallObjectMethod(satelliteAlmanacsObj, method_listGet, i);
+        if (glonassSatelliteAlmanacObj == nullptr) continue;
+        GlonassSatelliteAlmanac glonassSatelliteAlmanac;
+        jdouble deltaI = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                               method_glonassSatelliteAlmanacGetDeltaI);
+        glonassSatelliteAlmanac.deltaI = deltaI;
+        jdouble deltaT = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                               method_glonassSatelliteAlmanacGetDeltaT);
+        glonassSatelliteAlmanac.deltaT = deltaT;
+        jdouble deltaTDot = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                                  method_glonassSatelliteAlmanacGetDeltaTDot);
+        glonassSatelliteAlmanac.deltaTDot = deltaTDot;
+        jdouble eccentricity = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                                     method_glonassSatelliteAlmanacGetEccentricity);
+        glonassSatelliteAlmanac.eccentricity = eccentricity;
+        jint frequencyChannelNumber =
+                env->CallIntMethod(glonassSatelliteAlmanacObj,
+                                   method_glonassSatelliteAlmanacGetFrequencyChannelNumber);
+        glonassSatelliteAlmanac.frequencyChannelNumber =
+                static_cast<int32_t>(frequencyChannelNumber);
+        jdouble lambda = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                               method_glonassSatelliteAlmanacGetLambda);
+        glonassSatelliteAlmanac.lambda = lambda;
+        jdouble omega = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                              method_glonassSatelliteAlmanacGetOmega);
+        glonassSatelliteAlmanac.omega = omega;
+        jint slotNumber = env->CallIntMethod(glonassSatelliteAlmanacObj,
+                                             method_glonassSatelliteAlmanacGetSlotNumber);
+        glonassSatelliteAlmanac.slotNumber = static_cast<int32_t>(slotNumber);
+        jint healthState = env->CallIntMethod(glonassSatelliteAlmanacObj,
+                                              method_glonassSatelliteAlmanacGetHealthState);
+        glonassSatelliteAlmanac.svHealth = static_cast<int32_t>(healthState);
+        jdouble tLambda = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                                method_glonassSatelliteAlmanacGetTLambda);
+        glonassSatelliteAlmanac.tLambda = tLambda;
+        jdouble tau = env->CallDoubleMethod(glonassSatelliteAlmanacObj,
+                                            method_glonassSatelliteAlmanacGetTau);
+        glonassSatelliteAlmanac.tau = tau;
+        jboolean isGlonassM = env->CallBooleanMethod(glonassSatelliteAlmanacObj,
+                                                     method_glonassSatelliteAlmanacGetIsGlonassM);
+        glonassSatelliteAlmanac.isGlonassM = isGlonassM;
+        jint calendarDayNumber =
+                env->CallIntMethod(glonassSatelliteAlmanacObj,
+                                   method_glonassSatelliteAlmanacGetCalendarDayNumber);
+        glonassSatelliteAlmanac.calendarDayNumber = static_cast<int32_t>(calendarDayNumber);
+        glonassAlmanac.satelliteAlmanacs.push_back(glonassSatelliteAlmanac);
+        env->DeleteLocalRef(glonassSatelliteAlmanacObj);
+    }
+    env->DeleteLocalRef(satelliteAlmanacsObj);
+}
+
+void GnssAssistanceUtil::setGlonassSatelliteEphemeris(
+        JNIEnv* env, jobject glonassSatelliteEphemerisListObj,
+        std::vector<GlonassSatelliteEphemeris>& glonassSatelliteEphemerisList) {
+    if (glonassSatelliteEphemerisListObj == nullptr) return;
+    auto len = env->CallIntMethod(glonassSatelliteEphemerisListObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject glonassSatelliteEphemerisObj =
+                env->CallObjectMethod(glonassSatelliteEphemerisListObj, method_listGet, i);
+        if (glonassSatelliteEphemerisObj == nullptr) continue;
+        GlonassSatelliteEphemeris glonassSatelliteEphemeris;
+        jdouble ageInDays = env->CallDoubleMethod(glonassSatelliteEphemerisObj,
+                                                  method_glonassSatelliteEphemerisGetAgeInDays);
+        glonassSatelliteEphemeris.ageInDays = ageInDays;
+
+        // Set the GlonassSatelliteClockModel.
+        jobject glonassSatelliteClockModelObj =
+                env->CallObjectMethod(glonassSatelliteEphemerisObj,
+                                      method_glonassSatelliteEphemerisGetSatelliteClockModel);
+        GlonassSatelliteClockModel glonassSatelliteClockModel;
+        jdouble clockBias = env->CallDoubleMethod(glonassSatelliteClockModelObj,
+                                                  method_glonassSatelliteClockModelGetClockBias);
+        glonassSatelliteClockModel.clockBias = clockBias;
+        jdouble frequencyBias =
+                env->CallDoubleMethod(glonassSatelliteClockModelObj,
+                                      method_glonassSatelliteClockModelGetFrequencyBias);
+        glonassSatelliteClockModel.frequencyBias = frequencyBias;
+        jint frequencyChannelNumber =
+                env->CallIntMethod(glonassSatelliteClockModelObj,
+                                   method_glonassSatelliteClockModelGetFrequencyChannelNumber);
+        glonassSatelliteClockModel.frequencyChannelNumber =
+                static_cast<int32_t>(frequencyChannelNumber);
+        jdouble timeOfClockSeconds =
+                env->CallDoubleMethod(glonassSatelliteClockModelObj,
+                                      method_glonassSatelliteClockModelGetTimeOfClockSeconds);
+        glonassSatelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+        glonassSatelliteEphemeris.satelliteClockModel = glonassSatelliteClockModel;
+        env->DeleteLocalRef(glonassSatelliteClockModelObj);
+
+        // Set the GlonassSatelliteOrbitModel.
+        jobject glonassSatelliteOrbitModelObj =
+                env->CallObjectMethod(glonassSatelliteEphemerisObj,
+                                      method_glonassSatelliteEphemerisGetSatelliteOrbitModel);
+        GlonassSatelliteOrbitModel glonassSatelliteOrbitModel;
+        jdouble x = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                          method_glonassSatelliteOrbitModelGetX);
+        glonassSatelliteOrbitModel.x = x;
+        jdouble y = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                          method_glonassSatelliteOrbitModelGetY);
+        glonassSatelliteOrbitModel.y = y;
+        jdouble z = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                          method_glonassSatelliteOrbitModelGetZ);
+        glonassSatelliteOrbitModel.z = z;
+        jdouble xAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                               method_glonassSatelliteOrbitModelGetXAccel);
+        glonassSatelliteOrbitModel.xAccel = xAccel;
+        jdouble yAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                               method_glonassSatelliteOrbitModelGetYAccel);
+        glonassSatelliteOrbitModel.yAccel = yAccel;
+        jdouble zAccel = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                               method_glonassSatelliteOrbitModelGetZAccel);
+        glonassSatelliteOrbitModel.zAccel = zAccel;
+        jdouble xDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                             method_glonassSatelliteOrbitModelGetXDot);
+        glonassSatelliteOrbitModel.xDot = xDot;
+        jdouble yDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                             method_glonassSatelliteOrbitModelGetYDot);
+        glonassSatelliteOrbitModel.yDot = yDot;
+        jdouble zDot = env->CallDoubleMethod(glonassSatelliteOrbitModelObj,
+                                             method_glonassSatelliteOrbitModelGetZDot);
+        glonassSatelliteOrbitModel.zDot = zDot;
+        glonassSatelliteEphemeris.satelliteOrbitModel = glonassSatelliteOrbitModel;
+        env->DeleteLocalRef(glonassSatelliteOrbitModelObj);
+
+        jint healthState = env->CallIntMethod(glonassSatelliteEphemerisObj,
+                                              method_glonassSatelliteEphemerisGetHealthState);
+        glonassSatelliteEphemeris.svHealth = static_cast<int32_t>(healthState);
+        jint slotNumber = env->CallIntMethod(glonassSatelliteEphemerisObj,
+                                             method_glonassSatelliteEphemerisGetSlotNumber);
+        glonassSatelliteEphemeris.slotNumber = static_cast<int32_t>(slotNumber);
+        jdouble frameTimeSeconds =
+                env->CallDoubleMethod(glonassSatelliteEphemerisObj,
+                                      method_glonassSatelliteEphemerisGetFrameTimeSeconds);
+        glonassSatelliteEphemeris.frameTimeSeconds = frameTimeSeconds;
+        jint updateIntervalMinutes =
+                env->CallIntMethod(glonassSatelliteEphemerisObj,
+                                   method_glonassSatelliteEphemerisGetUpdateIntervalMinutes);
+        glonassSatelliteEphemeris.updateIntervalMinutes =
+                static_cast<int32_t>(updateIntervalMinutes);
+        jboolean isGlonassM = env->CallBooleanMethod(glonassSatelliteEphemerisObj,
+                                                     method_glonassSatelliteEphemerisGetIsGlonassM);
+        glonassSatelliteEphemeris.isGlonassM = isGlonassM;
+        jboolean isUpdateIntervalOdd =
+                env->CallBooleanMethod(glonassSatelliteEphemerisObj,
+                                       method_glonassSatelliteEphemerisGetIsUpdateIntervalOdd);
+        glonassSatelliteEphemeris.isOddUpdateInterval = isUpdateIntervalOdd;
+        glonassSatelliteEphemerisList.push_back(glonassSatelliteEphemeris);
+        env->DeleteLocalRef(glonassSatelliteEphemerisObj);
+    }
+}
+
+void GnssAssistanceUtil::setGalileoAssistance(JNIEnv* env, jobject galileoAssistanceObj,
+                                              GalileoAssistance& galileoAssistance) {
+    jobject galileoAlmanacObj =
+            env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetAlmanac);
+    jobject ionosphericModelObj =
+            env->CallObjectMethod(galileoAssistanceObj,
+                                  method_galileoAssistanceGetIonosphericModel);
+    jobject utcModelObj =
+            env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetUtcModel);
+    jobject leapSecondsModelObj =
+            env->CallObjectMethod(galileoAssistanceObj,
+                                  method_galileoAssistanceGetLeapSecondsModel);
+    jobject timeModelsObj =
+            env->CallObjectMethod(galileoAssistanceObj, method_galileoAssistanceGetTimeModels);
+    jobject satelliteEphemerisObj =
+            env->CallObjectMethod(galileoAssistanceObj,
+                                  method_galileoAssistanceGetSatelliteEphemeris);
+    jobject realTimeIntegrityModelsObj =
+            env->CallObjectMethod(galileoAssistanceObj,
+                                  method_galileoAssistanceGetRealTimeIntegrityModels);
+    jobject satelliteCorrectionsObj =
+            env->CallObjectMethod(galileoAssistanceObj,
+                                  method_galileoAssistanceGetSatelliteCorrections);
+    setGnssAlmanac(env, galileoAlmanacObj, galileoAssistance.almanac);
+    setGaliloKlobucharIonosphericModel(env, ionosphericModelObj,
+                                       galileoAssistance.ionosphericModel);
+    setUtcModel(env, utcModelObj, galileoAssistance.utcModel);
+    setLeapSecondsModel(env, leapSecondsModelObj, galileoAssistance.leapSecondsModel);
+    setTimeModels(env, timeModelsObj, galileoAssistance.timeModels);
+    setGalileoSatelliteEphemeris(env, satelliteEphemerisObj, galileoAssistance.satelliteEphemeris);
+    setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+                               galileoAssistance.realTimeIntegrityModels);
+    setSatelliteCorrections(env, satelliteCorrectionsObj, galileoAssistance.satelliteCorrections);
+    env->DeleteLocalRef(galileoAlmanacObj);
+    env->DeleteLocalRef(ionosphericModelObj);
+    env->DeleteLocalRef(utcModelObj);
+    env->DeleteLocalRef(leapSecondsModelObj);
+    env->DeleteLocalRef(timeModelsObj);
+    env->DeleteLocalRef(satelliteEphemerisObj);
+    env->DeleteLocalRef(realTimeIntegrityModelsObj);
+    env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setGaliloKlobucharIonosphericModel(
+        JNIEnv* env, jobject galileoIonosphericModelObj,
+        GalileoIonosphericModel& ionosphericModel) {
+    if (galileoIonosphericModelObj == nullptr) return;
+    jdouble ai0 =
+            env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi0);
+    ionosphericModel.ai0 = ai0;
+    jdouble ai1 =
+            env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi1);
+    ionosphericModel.ai1 = ai1;
+    jdouble ai2 =
+            env->CallDoubleMethod(galileoIonosphericModelObj, method_galileoIonosphericModelGetAi2);
+    ionosphericModel.ai2 = ai2;
+}
+
+void GnssAssistanceUtil::setGalileoSatelliteEphemeris(
+        JNIEnv* env, jobject galileoSatelliteEphemerisListObj,
+        std::vector<GalileoSatelliteEphemeris>& galileoSatelliteEphemerisList) {
+    if (galileoSatelliteEphemerisListObj == nullptr) return;
+    auto len = env->CallIntMethod(galileoSatelliteEphemerisListObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject galileoSatelliteEphemerisObj =
+                env->CallObjectMethod(galileoSatelliteEphemerisListObj, method_listGet, i);
+        GalileoSatelliteEphemeris galileoSatelliteEphemeris;
+        GalileoSvHealth galileoSvHealth;
+        // Set the svid of the satellite.
+        jint svid = env->CallLongMethod(galileoSatelliteEphemerisObj,
+                                        method_galileoSatelliteEphemerisGetSvid);
+        galileoSatelliteEphemeris.svid = svid;
+
+        // Set the satellite clock models.
+        jobject galileoSatelliteClockModelListObj =
+                env->CallObjectMethod(galileoSatelliteEphemerisObj,
+                                      method_galileoSatelliteEphemerisGetSatelliteClockModels);
+        auto size = env->CallIntMethod(galileoSatelliteClockModelListObj, method_listSize);
+        for (uint16_t j = 0; j < size; ++j) {
+            jobject galileoSatelliteClockModelObj =
+                    env->CallObjectMethod(galileoSatelliteClockModelListObj, method_listGet, j);
+            if (galileoSatelliteClockModelObj == nullptr) continue;
+            GalileoSatelliteClockModel galileoSatelliteClockModel;
+            jdouble af0 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                                method_galileoSatelliteClockModelGetAf0);
+            galileoSatelliteClockModel.af0 = af0;
+            jdouble af1 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                                method_galileoSatelliteClockModelGetAf1);
+            galileoSatelliteClockModel.af1 = af1;
+            jdouble af2 = env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                                method_galileoSatelliteClockModelGetAf2);
+            galileoSatelliteClockModel.af2 = af2;
+            jdouble bgdSeconds =
+                    env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                          method_galileoSatelliteClockModelGetBgdSeconds);
+            galileoSatelliteClockModel.bgdSeconds = bgdSeconds;
+            jint satelliteClockType =
+                    env->CallIntMethod(galileoSatelliteClockModelObj,
+                                       method_galileoSatelliteClockModelGetSatelliteClockType);
+            galileoSatelliteClockModel.satelliteClockType =
+                    static_cast<GalileoSatelliteClockModel::SatelliteClockType>(satelliteClockType);
+            jdouble sisaMeters =
+                    env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                          method_galileoSatelliteClockModelGetSisaMeters);
+            galileoSatelliteClockModel.sisaMeters = sisaMeters;
+            jdouble timeOfClockSeconds =
+                    env->CallDoubleMethod(galileoSatelliteClockModelObj,
+                                          method_galileoSatelliteClockModelGetTimeOfClockSeconds);
+            galileoSatelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+            galileoSatelliteEphemeris.satelliteClockModel.push_back(galileoSatelliteClockModel);
+            env->DeleteLocalRef(galileoSatelliteClockModelObj);
+        }
+        env->DeleteLocalRef(galileoSatelliteClockModelListObj);
+
+        // Set the satelliteOrbitModel of the satellite.
+        jobject satelliteOrbitModelObj =
+                env->CallObjectMethod(galileoSatelliteEphemerisObj,
+                                      method_galileoSatelliteEphemerisGetSatelliteOrbitModel);
+        GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+                                                   galileoSatelliteEphemeris.satelliteOrbitModel);
+        env->DeleteLocalRef(satelliteOrbitModelObj);
+
+        // Set the satellite health of the satellite clock model.
+        jobject galileoSvHealthObj =
+                env->CallObjectMethod(galileoSatelliteEphemerisObj,
+                                      method_galileoSatelliteEphemerisGetSatelliteHealth);
+        jint dataValidityStatusE1b =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetDataValidityStatusE1b);
+        galileoSvHealth.dataValidityStatusE1b =
+                static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE1b);
+        jint dataValidityStatusE5a =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetDataValidityStatusE5a);
+        galileoSvHealth.dataValidityStatusE5a =
+                static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE5a);
+        jint dataValidityStatusE5b =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetDataValidityStatusE5b);
+        galileoSvHealth.dataValidityStatusE5b =
+                static_cast<GalileoSvHealth::GalileoHealthDataVaidityType>(dataValidityStatusE5b);
+        jint signalHealthStatusE1b =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetSignalHealthStatusE1b);
+        galileoSvHealth.signalHealthStatusE1b =
+                static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE1b);
+        jint signalHealthStatusE5a =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetSignalHealthStatusE5a);
+        galileoSvHealth.signalHealthStatusE5a =
+                static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE5a);
+        jint signalHealthStatusE5b =
+                env->CallIntMethod(galileoSvHealthObj,
+                                   method_galileoSvHealthGetSignalHealthStatusE5b);
+        galileoSvHealth.signalHealthStatusE5b =
+                static_cast<GalileoSvHealth::GalileoHealthStatusType>(signalHealthStatusE5b);
+        galileoSatelliteEphemeris.svHealth = galileoSvHealth;
+        env->DeleteLocalRef(galileoSvHealthObj);
+
+        // Set the satelliteEphemerisTime of the satellite.
+        jobject satelliteEphemerisTimeObj =
+                env->CallObjectMethod(galileoSatelliteEphemerisObj,
+                                      method_galileoSatelliteEphemerisGetSatelliteEphemerisTime);
+        GnssAssistanceUtil::setSatelliteEphemerisTime(env, satelliteEphemerisTimeObj,
+                                                      galileoSatelliteEphemeris
+                                                              .satelliteEphemerisTime);
+        env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+        galileoSatelliteEphemerisList.push_back(galileoSatelliteEphemeris);
+        env->DeleteLocalRef(galileoSatelliteEphemerisObj);
+    }
+}
+
+void GnssAssistanceUtil::setBeidouAssistance(JNIEnv* env, jobject beidouAssistanceObj,
+                                             BeidouAssistance& beidouAssistance) {
+    jobject beidouAlmanacObj =
+            env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetAlmanac);
+    jobject ionosphericModelObj =
+            env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetIonosphericModel);
+    jobject utcModelObj =
+            env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetUtcModel);
+    jobject leapSecondsModelObj =
+            env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetLeapSecondsModel);
+    jobject timeModelsObj =
+            env->CallObjectMethod(beidouAssistanceObj, method_beidouAssistanceGetTimeModels);
+    jobject satelliteEphemerisObj =
+            env->CallObjectMethod(beidouAssistanceObj,
+                                  method_beidouAssistanceGetSatelliteEphemeris);
+    jobject realTimeIntegrityModelsObj =
+            env->CallObjectMethod(beidouAssistanceObj,
+                                  method_beidouAssistanceGetRealTimeIntegrityModels);
+    jobject satelliteCorrectionsObj =
+            env->CallObjectMethod(beidouAssistanceObj,
+                                  method_beidouAssistanceGetSatelliteCorrections);
+    setGnssAlmanac(env, beidouAlmanacObj, beidouAssistance.almanac);
+    setKlobucharIonosphericModel(env, ionosphericModelObj, beidouAssistance.ionosphericModel);
+    setUtcModel(env, utcModelObj, beidouAssistance.utcModel);
+    setLeapSecondsModel(env, leapSecondsModelObj, beidouAssistance.leapSecondsModel);
+    setTimeModels(env, timeModelsObj, beidouAssistance.timeModels);
+    setBeidouSatelliteEphemeris(env, satelliteEphemerisObj, beidouAssistance.satelliteEphemeris);
+    setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+                               beidouAssistance.realTimeIntegrityModels);
+    setSatelliteCorrections(env, satelliteCorrectionsObj, beidouAssistance.satelliteCorrections);
+    env->DeleteLocalRef(beidouAlmanacObj);
+    env->DeleteLocalRef(ionosphericModelObj);
+    env->DeleteLocalRef(utcModelObj);
+    env->DeleteLocalRef(leapSecondsModelObj);
+    env->DeleteLocalRef(timeModelsObj);
+    env->DeleteLocalRef(satelliteEphemerisObj);
+    env->DeleteLocalRef(realTimeIntegrityModelsObj);
+    env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+void GnssAssistanceUtil::setBeidouSatelliteEphemeris(
+        JNIEnv* env, jobject beidouSatelliteEphemerisListObj,
+        std::vector<BeidouSatelliteEphemeris>& beidouSatelliteEphemerisList) {
+    if (beidouSatelliteEphemerisListObj == nullptr) return;
+    auto len = env->CallIntMethod(beidouSatelliteEphemerisListObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject beidouSatelliteEphemerisObj =
+                env->CallObjectMethod(beidouSatelliteEphemerisListObj, method_listGet, i);
+        if (beidouSatelliteEphemerisObj == nullptr) continue;
+        BeidouSatelliteEphemeris beidouSatelliteEphemeris;
+
+        // Set the svid of the satellite.
+        jint svid = env->CallIntMethod(beidouSatelliteEphemerisObj,
+                                       method_beidouSatelliteEphemerisGetSvid);
+        beidouSatelliteEphemeris.svid = static_cast<int32_t>(svid);
+
+        // Set the satelliteClockModel of the satellite.
+        jobject satelliteClockModelObj =
+                env->CallObjectMethod(beidouSatelliteEphemerisObj,
+                                      method_beidouSatelliteEphemerisGetSatelliteClockModel);
+        jdouble af0 = env->CallDoubleMethod(satelliteClockModelObj,
+                                            method_beidouSatelliteClockModelGetAf0);
+        jdouble af1 = env->CallDoubleMethod(satelliteClockModelObj,
+                                            method_beidouSatelliteClockModelGetAf1);
+        jdouble af2 = env->CallDoubleMethod(satelliteClockModelObj,
+                                            method_beidouSatelliteClockModelGetAf2);
+        jdouble tgd1 = env->CallDoubleMethod(satelliteClockModelObj,
+                                             method_beidouSatelliteClockModelGetTgd1);
+        jdouble tgd2 = env->CallDoubleMethod(satelliteClockModelObj,
+                                             method_beidouSatelliteClockModelGetTgd2);
+        jdouble aodc = env->CallDoubleMethod(satelliteClockModelObj,
+                                             method_beidouSatelliteClockModelGetAodc);
+        jlong timeOfClockSeconds =
+                env->CallLongMethod(satelliteClockModelObj,
+                                    method_beidouSatelliteClockModelGetTimeOfClockSeconds);
+        beidouSatelliteEphemeris.satelliteClockModel.af0 = af0;
+        beidouSatelliteEphemeris.satelliteClockModel.af1 = af1;
+        beidouSatelliteEphemeris.satelliteClockModel.af2 = af2;
+        beidouSatelliteEphemeris.satelliteClockModel.tgd1 = tgd1;
+        beidouSatelliteEphemeris.satelliteClockModel.tgd2 = tgd2;
+        beidouSatelliteEphemeris.satelliteClockModel.aodc = aodc;
+        beidouSatelliteEphemeris.satelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+        env->DeleteLocalRef(satelliteClockModelObj);
+
+        // Set the satelliteOrbitModel of the satellite.
+        jobject satelliteOrbitModelObj =
+                env->CallObjectMethod(beidouSatelliteEphemerisObj,
+                                      method_beidouSatelliteEphemerisGetSatelliteOrbitModel);
+        GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+                                                   beidouSatelliteEphemeris.satelliteOrbitModel);
+        env->DeleteLocalRef(satelliteOrbitModelObj);
+
+        // Set the satelliteHealth of the satellite.
+        jobject satelliteHealthObj =
+                env->CallObjectMethod(beidouSatelliteEphemerisObj,
+                                      method_beidouSatelliteEphemerisGetSatelliteHealth);
+        jint satH1 = env->CallIntMethod(satelliteHealthObj, method_beidouSatelliteHealthGetSatH1);
+        jint svAccur =
+                env->CallIntMethod(satelliteHealthObj, method_beidouSatelliteHealthGetSvAccur);
+        beidouSatelliteEphemeris.satelliteHealth.satH1 = static_cast<int32_t>(satH1);
+        beidouSatelliteEphemeris.satelliteHealth.svAccur = static_cast<int32_t>(svAccur);
+        env->DeleteLocalRef(satelliteHealthObj);
+
+        // Set the satelliteEphemerisTime of the satellite.
+        jobject satelliteEphemerisTimeObj =
+                env->CallObjectMethod(beidouSatelliteEphemerisObj,
+                                      method_beidouSatelliteEphemerisGetSatelliteEphemerisTime);
+        jint iode = env->CallIntMethod(satelliteEphemerisTimeObj,
+                                       method_beidouSatelliteEphemerisTimeGetIode);
+        jint beidouWeekNumber =
+                env->CallIntMethod(satelliteEphemerisTimeObj,
+                                   method_beidouSatelliteEphemerisTimeGetBeidouWeekNumber);
+        jint toeSeconds = env->CallDoubleMethod(satelliteEphemerisTimeObj,
+                                                method_beidouSatelliteEphemerisTimeGetToeSeconds);
+        beidouSatelliteEphemeris.satelliteEphemerisTime.aode = static_cast<int32_t>(iode);
+        beidouSatelliteEphemeris.satelliteEphemerisTime.weekNumber =
+                static_cast<int32_t>(beidouWeekNumber);
+        beidouSatelliteEphemeris.satelliteEphemerisTime.toeSeconds =
+                static_cast<int32_t>(toeSeconds);
+        env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+        beidouSatelliteEphemerisList.push_back(beidouSatelliteEphemeris);
+        env->DeleteLocalRef(beidouSatelliteEphemerisObj);
+    }
+}
+
+void GnssAssistanceUtil::setGpsAssistance(JNIEnv* env, jobject gpsAssistanceObj,
+                                          GpsAssistance& gpsAssistance) {
+    jobject gnssAlmanacObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetAlmanac);
+    jobject ionosphericModelObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetIonosphericModel);
+    jobject utcModelObj = env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetUtcModel);
+    jobject leapSecondsModelObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetLeapSecondsModel);
+    jobject timeModelsObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetTimeModels);
+    jobject satelliteEphemerisObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetSatelliteEphemeris);
+    jobject realTimeIntegrityModelsObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetRealTimeIntegrityModels);
+    jobject satelliteCorrectionsObj =
+            env->CallObjectMethod(gpsAssistanceObj, method_gpsAssistanceGetSatelliteCorrections);
+
+    setGnssAlmanac(env, gnssAlmanacObj, gpsAssistance.almanac);
+    setKlobucharIonosphericModel(env, ionosphericModelObj, gpsAssistance.ionosphericModel);
+    setUtcModel(env, utcModelObj, gpsAssistance.utcModel);
+    setLeapSecondsModel(env, leapSecondsModelObj, gpsAssistance.leapSecondsModel);
+    setTimeModels(env, timeModelsObj, gpsAssistance.timeModels);
+    setGpsOrQzssSatelliteEphemeris<GpsSatelliteEphemeris>(env, satelliteEphemerisObj,
+                                                          gpsAssistance.satelliteEphemeris);
+    setRealTimeIntegrityModels(env, realTimeIntegrityModelsObj,
+                               gpsAssistance.realTimeIntegrityModels);
+    setSatelliteCorrections(env, satelliteCorrectionsObj, gpsAssistance.satelliteCorrections);
+    env->DeleteLocalRef(gnssAlmanacObj);
+    env->DeleteLocalRef(ionosphericModelObj);
+    env->DeleteLocalRef(utcModelObj);
+    env->DeleteLocalRef(leapSecondsModelObj);
+    env->DeleteLocalRef(timeModelsObj);
+    env->DeleteLocalRef(satelliteEphemerisObj);
+    env->DeleteLocalRef(realTimeIntegrityModelsObj);
+    env->DeleteLocalRef(satelliteCorrectionsObj);
+}
+
+/** Set the GPS/QZSS satellite ephemeris list. */
+template <class T>
+void GnssAssistanceUtil::setGpsOrQzssSatelliteEphemeris(JNIEnv* env,
+                                                        jobject satelliteEphemerisListObj,
+                                                        std::vector<T>& satelliteEphemerisList) {
+    if (satelliteEphemerisListObj == nullptr) return;
+    auto len = env->CallIntMethod(satelliteEphemerisListObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject satelliteEphemerisObj =
+                env->CallObjectMethod(satelliteEphemerisListObj, method_listGet, i);
+        if (satelliteEphemerisObj == nullptr) continue;
+        T satelliteEphemeris;
+        // Set the svid of the satellite.
+        jint svid = env->CallIntMethod(satelliteEphemerisObj, method_gpsSatelliteEphemerisGetSvid);
+        satelliteEphemeris.svid = static_cast<int32_t>(svid);
+
+        // Set the gpsL2Params of the satellite.
+        jobject gpsL2ParamsObj = env->CallObjectMethod(satelliteEphemerisObj,
+                                                       method_gpsSatelliteEphemerisGetGpsL2Params);
+        jint l2Code = env->CallIntMethod(gpsL2ParamsObj, method_gpsL2ParamsGetL2Code);
+        jint l2Flag = env->CallIntMethod(gpsL2ParamsObj, method_gpsL2ParamsGetL2Flag);
+        satelliteEphemeris.gpsL2Params.l2Code = static_cast<int32_t>(l2Code);
+        satelliteEphemeris.gpsL2Params.l2Flag = static_cast<int32_t>(l2Flag);
+        env->DeleteLocalRef(gpsL2ParamsObj);
+
+        // Set the satelliteClockModel of the satellite.
+        jobject satelliteClockModelObj =
+                env->CallObjectMethod(satelliteEphemerisObj,
+                                      method_gpsSatelliteEphemerisGetSatelliteClockModel);
+        jdouble af0 =
+                env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf0);
+        jdouble af1 =
+                env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf1);
+        jdouble af2 =
+                env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetAf2);
+        jdouble tgd =
+                env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetTgd);
+        jint iodc =
+                env->CallDoubleMethod(satelliteClockModelObj, method_gpsSatelliteClockModelGetIodc);
+        jlong timeOfClockSeconds =
+                env->CallLongMethod(satelliteClockModelObj,
+                                    method_gpsSatelliteClockModelGetTimeOfClockSeconds);
+        satelliteEphemeris.satelliteClockModel.af0 = af0;
+        satelliteEphemeris.satelliteClockModel.af1 = af1;
+        satelliteEphemeris.satelliteClockModel.af2 = af2;
+        satelliteEphemeris.satelliteClockModel.tgd = tgd;
+        satelliteEphemeris.satelliteClockModel.iodc = static_cast<int32_t>(iodc);
+        satelliteEphemeris.satelliteClockModel.timeOfClockSeconds = timeOfClockSeconds;
+        env->DeleteLocalRef(satelliteClockModelObj);
+
+        // Set the satelliteOrbitModel of the satellite.
+        jobject satelliteOrbitModelObj =
+                env->CallObjectMethod(satelliteEphemerisObj,
+                                      method_gpsSatelliteEphemerisGetSatelliteOrbitModel);
+        GnssAssistanceUtil::setKeplerianOrbitModel(env, satelliteOrbitModelObj,
+                                                   satelliteEphemeris.satelliteOrbitModel);
+        env->DeleteLocalRef(satelliteOrbitModelObj);
+
+        // Set the satelliteHealth of the satellite.
+        jobject satelliteHealthObj =
+                env->CallObjectMethod(satelliteEphemerisObj,
+                                      method_gpsSatelliteEphemerisGetSatelliteHealth);
+        jint svHealth =
+                env->CallIntMethod(satelliteHealthObj, method_gpsSatelliteHealthGetSvHealth);
+        jdouble svAccur =
+                env->CallDoubleMethod(satelliteHealthObj, method_gpsSatelliteHealthGetSvAccur);
+        jdouble fitInt = env->CallIntMethod(satelliteHealthObj, method_gpsSatelliteHealthGetFitInt);
+        satelliteEphemeris.satelliteHealth.svHealth = static_cast<int32_t>(svHealth);
+        satelliteEphemeris.satelliteHealth.svAccur = svAccur;
+        satelliteEphemeris.satelliteHealth.fitInt = fitInt;
+        env->DeleteLocalRef(satelliteHealthObj);
+
+        // Set the satelliteEphemerisTime of the satellite.
+        jobject satelliteEphemerisTimeObj =
+                env->CallObjectMethod(satelliteEphemerisObj,
+                                      method_gpsSatelliteEphemerisGetSatelliteEphemerisTime);
+        GnssAssistanceUtil::setSatelliteEphemerisTime(env, satelliteEphemerisTimeObj,
+                                                      satelliteEphemeris.satelliteEphemerisTime);
+        env->DeleteLocalRef(satelliteEphemerisTimeObj);
+
+        satelliteEphemerisList.push_back(satelliteEphemeris);
+        env->DeleteLocalRef(satelliteEphemerisObj);
+    }
+}
+
+void GnssAssistanceUtil::setSatelliteCorrections(
+        JNIEnv* env, jobject satelliteCorrectionsObj,
+        std::vector<GnssSatelliteCorrections>& gnssSatelliteCorrectionsList) {
+    if (satelliteCorrectionsObj == nullptr) return;
+    auto len = env->CallIntMethod(satelliteCorrectionsObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        GnssSatelliteCorrections gnssSatelliteCorrections;
+        jobject satelliteCorrectionObj =
+                env->CallObjectMethod(satelliteCorrectionsObj, method_listGet, i);
+        if (satelliteCorrectionObj == nullptr) continue;
+        jint svid = env->CallIntMethod(satelliteCorrectionObj, method_satelliteCorrectionGetSvid);
+        gnssSatelliteCorrections.svid = svid;
+        jobject ionosphericCorrectionsObj =
+                env->CallObjectMethod(satelliteCorrectionObj,
+                                      method_satelliteCorrectionGetIonosphericCorrections);
+        env->DeleteLocalRef(satelliteCorrectionObj);
+        auto size = env->CallIntMethod(ionosphericCorrectionsObj, method_listSize);
+        for (uint16_t j = 0; j < size; ++j) {
+            jobject ionosphericCorrectionObj =
+                    env->CallObjectMethod(ionosphericCorrectionsObj, method_listGet, j);
+            if (ionosphericCorrectionObj == nullptr) continue;
+            IonosphericCorrection ionosphericCorrection;
+            jlong carrierFrequencyHz =
+                    env->CallLongMethod(ionosphericCorrectionObj,
+                                        method_ionosphericCorrectionGetCarrierFrequencyHz);
+            ionosphericCorrection.carrierFrequencyHz = carrierFrequencyHz;
+
+            jobject gnssCorrectionComponentObj =
+                    env->CallObjectMethod(ionosphericCorrectionObj,
+                                          method_ionosphericCorrectionGetIonosphericCorrection);
+            env->DeleteLocalRef(ionosphericCorrectionObj);
+
+            jstring sourceKey = static_cast<jstring>(
+                    env->CallObjectMethod(gnssCorrectionComponentObj,
+                                          method_gnssCorrectionComponentGetSourceKey));
+            ScopedJniString jniSourceKey{env, sourceKey};
+            ionosphericCorrection.ionosphericCorrectionComponent.sourceKey =
+                    android::String16(jniSourceKey.c_str());
+
+            jobject pseudorangeCorrectionObj =
+                    env->CallObjectMethod(gnssCorrectionComponentObj,
+                                          method_gnssCorrectionComponentGetPseudorangeCorrection);
+            jdouble correctionMeters =
+                    env->CallDoubleMethod(pseudorangeCorrectionObj,
+                                          method_pseudorangeCorrectionGetCorrectionMeters);
+            jdouble correctionUncertaintyMeters = env->CallDoubleMethod(
+                    pseudorangeCorrectionObj,
+                    method_pseudorangeCorrectionGetCorrectionUncertaintyMeters);
+            jdouble correctionRateMetersPerSecond = env->CallDoubleMethod(
+                    pseudorangeCorrectionObj,
+                    method_pseudorangeCorrectionGetCorrectionRateMetersPerSecond);
+            ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+                    .correctionMeters = correctionMeters;
+            ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+                    .correctionUncertaintyMeters = correctionUncertaintyMeters;
+            ionosphericCorrection.ionosphericCorrectionComponent.pseudorangeCorrection
+                    .correctionRateMetersPerSecond = correctionRateMetersPerSecond;
+            env->DeleteLocalRef(pseudorangeCorrectionObj);
+
+            jobject gnssIntervalObj =
+                    env->CallObjectMethod(gnssCorrectionComponentObj,
+                                          method_gnssCorrectionComponentGetValidityInterval);
+            jdouble startMillisSinceGpsEpoch =
+                    env->CallDoubleMethod(gnssIntervalObj,
+                                          method_gnssIntervalGetStartMillisSinceGpsEpoch);
+            jdouble endMillisSinceGpsEpoch =
+                    env->CallDoubleMethod(gnssIntervalObj,
+                                          method_gnssIntervalGetEndMillisSinceGpsEpoch);
+            ionosphericCorrection.ionosphericCorrectionComponent.validityInterval
+                    .startMillisSinceGpsEpoch = startMillisSinceGpsEpoch;
+            ionosphericCorrection.ionosphericCorrectionComponent.validityInterval
+                    .endMillisSinceGpsEpoch = endMillisSinceGpsEpoch;
+            env->DeleteLocalRef(gnssIntervalObj);
+
+            env->DeleteLocalRef(gnssCorrectionComponentObj);
+            gnssSatelliteCorrections.ionosphericCorrections.push_back(ionosphericCorrection);
+        }
+        gnssSatelliteCorrectionsList.push_back(gnssSatelliteCorrections);
+        env->DeleteLocalRef(ionosphericCorrectionsObj);
+    }
+}
+
+void GnssAssistanceUtil::setRealTimeIntegrityModels(
+        JNIEnv* env, jobject realTimeIntegrityModelsObj,
+        std::vector<RealTimeIntegrityModel>& realTimeIntegrityModels) {
+    if (realTimeIntegrityModelsObj == nullptr) return;
+    auto len = env->CallIntMethod(realTimeIntegrityModelsObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject realTimeIntegrityModelObj =
+                env->CallObjectMethod(realTimeIntegrityModelsObj, method_listGet, i);
+        if (realTimeIntegrityModelObj == nullptr) continue;
+        RealTimeIntegrityModel realTimeIntegrityModel;
+        jint badSvid = env->CallIntMethod(realTimeIntegrityModelObj,
+                                          method_realTimeIntegrityModelGetBadSvid);
+        jobject badSignalTypesObj =
+                env->CallObjectMethod(realTimeIntegrityModelObj,
+                                      method_realTimeIntegrityModelGetBadSignalTypes);
+        auto badSignalTypesSize = env->CallIntMethod(badSignalTypesObj, method_listSize);
+        for (uint16_t j = 0; j < badSignalTypesSize; ++j) {
+            GnssSignalType badSignalType;
+            jobject badSignalTypeObj = env->CallObjectMethod(badSignalTypesObj, method_listGet, j);
+            if (badSignalTypeObj != nullptr) {
+                setGnssSignalType(env, badSignalTypeObj, badSignalType);
+                realTimeIntegrityModel.badSignalTypes.push_back(badSignalType);
+                env->DeleteLocalRef(badSignalTypeObj);
+            }
+        }
+
+        jlong startDateSeconds =
+                env->CallLongMethod(realTimeIntegrityModelObj,
+                                    method_realTimeIntegrityModelGetStartDateSeconds);
+        jlong endDateSeconds = env->CallLongMethod(realTimeIntegrityModelObj,
+                                                   method_realTimeIntegrityModelGetEndDateSeconds);
+        jlong publishDateSeconds =
+                env->CallLongMethod(realTimeIntegrityModelObj,
+                                    method_realTimeIntegrityModelGetPublishDateSeconds);
+        jstring advisoryNumber = static_cast<jstring>(
+                env->CallObjectMethod(realTimeIntegrityModelObj,
+                                      method_realTimeIntegrityModelGetAdvisoryNumber));
+        ScopedJniString jniAdvisoryNumber{env, advisoryNumber};
+        jstring advisoryType = static_cast<jstring>(
+                env->CallObjectMethod(realTimeIntegrityModelObj,
+                                      method_realTimeIntegrityModelGetAdvisoryType));
+        ScopedJniString jniAdvisoryType{env, advisoryType};
+
+        realTimeIntegrityModel.badSvid = badSvid;
+        realTimeIntegrityModel.startDateSeconds = startDateSeconds;
+        realTimeIntegrityModel.endDateSeconds = endDateSeconds;
+        realTimeIntegrityModel.publishDateSeconds = publishDateSeconds;
+        realTimeIntegrityModel.advisoryNumber = android::String16(jniAdvisoryNumber.c_str());
+        realTimeIntegrityModel.advisoryType = android::String16(jniAdvisoryType.c_str());
+        realTimeIntegrityModels.push_back(realTimeIntegrityModel);
+        env->DeleteLocalRef(badSignalTypesObj);
+        env->DeleteLocalRef(realTimeIntegrityModelObj);
+    }
+}
+
+void GnssAssistanceUtil::setGnssSignalType(JNIEnv* env, jobject gnssSignalTypeObj,
+                                           GnssSignalType& gnssSignalType) {
+    if (gnssSignalTypeObj == nullptr) {
+        ALOGE("gnssSignalTypeObj is null");
+        return;
+    }
+    jint constellationType =
+            env->CallIntMethod(gnssSignalTypeObj, method_gnssSignalTypeGetConstellationType);
+    jdouble carrierFrequencyHz =
+            env->CallIntMethod(gnssSignalTypeObj, method_gnssSignalTypeGetCarrierFrequencyHz);
+    jstring codeType = static_cast<jstring>(
+            env->CallObjectMethod(gnssSignalTypeObj, method_gnssSignalTypeGetCodeType));
+    ScopedJniString jniCodeType{env, codeType};
+
+    gnssSignalType.constellation = static_cast<GnssConstellationType>(constellationType);
+    gnssSignalType.carrierFrequencyHz = static_cast<int32_t>(carrierFrequencyHz);
+    gnssSignalType.codeType = std::string(jniCodeType.c_str());
+}
+
+void GnssAssistanceUtil::setTimeModels(JNIEnv* env, jobject timeModelsObj,
+                                       std::vector<TimeModel>& timeModels) {
+    if (timeModelsObj == nullptr) return;
+    auto len = env->CallIntMethod(timeModelsObj, method_listSize);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject timeModelObj = env->CallObjectMethod(timeModelsObj, method_listGet, i);
+        TimeModel timeModel;
+        jint toGnss = env->CallIntMethod(timeModelObj, method_timeModelsGetToGnss);
+        jlong timeOfWeek = env->CallLongMethod(timeModelObj, method_timeModelsGetTimeOfWeek);
+        jint weekNumber = env->CallIntMethod(timeModelObj, method_timeModelsGetWeekNumber);
+        jdouble a0 = env->CallDoubleMethod(timeModelObj, method_timeModelsGetA0);
+        jdouble a1 = env->CallDoubleMethod(timeModelObj, method_timeModelsGetA1);
+        timeModel.toGnss = static_cast<GnssConstellationType>(toGnss);
+        timeModel.timeOfWeek = timeOfWeek;
+        timeModel.weekNumber = static_cast<int32_t>(weekNumber);
+        timeModel.a0 = a0;
+        timeModel.a1 = a1;
+        timeModels.push_back(timeModel);
+        env->DeleteLocalRef(timeModelObj);
+    }
+}
+
+void GnssAssistanceUtil::setLeapSecondsModel(JNIEnv* env, jobject leapSecondsModelObj,
+                                             LeapSecondsModel& leapSecondsModel) {
+    if (leapSecondsModelObj == nullptr) {
+        leapSecondsModel.leapSeconds = -1;
+        return;
+    }
+    jint dayNumberLeapSecondsFuture =
+            env->CallIntMethod(leapSecondsModelObj,
+                               method_leapSecondsModelGetDayNumberLeapSecondsFuture);
+    jint leapSeconds =
+            env->CallIntMethod(leapSecondsModelObj, method_leapSecondsModelGetLeapSeconds);
+    jint leapSecondsFuture =
+            env->CallIntMethod(leapSecondsModelObj, method_leapSecondsModelGetLeapSecondsFuture);
+    jint weekNumberLeapSecondsFuture =
+            env->CallIntMethod(leapSecondsModelObj,
+                               method_leapSecondsModelGetWeekNumberLeapSecondsFuture);
+    leapSecondsModel.dayNumberLeapSecondsFuture = static_cast<int32_t>(dayNumberLeapSecondsFuture);
+    leapSecondsModel.leapSeconds = static_cast<int32_t>(leapSeconds);
+    leapSecondsModel.leapSecondsFuture = static_cast<int32_t>(leapSecondsFuture);
+    leapSecondsModel.weekNumberLeapSecondsFuture =
+            static_cast<int32_t>(weekNumberLeapSecondsFuture);
+}
+
+void GnssAssistanceUtil::setSatelliteEphemerisTime(JNIEnv* env, jobject satelliteEphemerisTimeObj,
+                                                   SatelliteEphemerisTime& satelliteEphemerisTime) {
+    if (satelliteEphemerisTimeObj == nullptr) return;
+    jdouble iode =
+            env->CallDoubleMethod(satelliteEphemerisTimeObj, method_satelliteEphemerisTimeGetIode);
+    jdouble toeSeconds = env->CallDoubleMethod(satelliteEphemerisTimeObj,
+                                               method_satelliteEphemerisTimeGetToeSeconds);
+    jint weekNumber = env->CallIntMethod(satelliteEphemerisTimeObj,
+                                         method_satelliteEphemerisTimeGetWeekNumber);
+    satelliteEphemerisTime.iode = iode;
+    satelliteEphemerisTime.toeSeconds = toeSeconds;
+    satelliteEphemerisTime.weekNumber = weekNumber;
+}
+
+void GnssAssistanceUtil::setKeplerianOrbitModel(JNIEnv* env, jobject keplerianOrbitModelObj,
+                                                KeplerianOrbitModel& keplerianOrbitModel) {
+    if (keplerianOrbitModelObj == nullptr) return;
+    jdouble rootA =
+            env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetRootA);
+    jdouble eccentricity = env->CallDoubleMethod(keplerianOrbitModelObj,
+                                                 method_keplerianOrbitModelGetEccentricity);
+    jdouble m0 = env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetM0);
+    jdouble omega =
+            env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetOmega);
+    jdouble omegaDot =
+            env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetOmegaDot);
+    jdouble deltaN =
+            env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetDeltaN);
+    jdouble iDot = env->CallDoubleMethod(keplerianOrbitModelObj, method_keplerianOrbitModelGetIDot);
+    jobject secondOrderHarmonicPerturbationObj =
+            env->CallObjectMethod(keplerianOrbitModelObj,
+                                  method_keplerianOrbitModelGetSecondOrderHarmonicPerturbation);
+    jdouble cic = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCic);
+    jdouble cis = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCis);
+    jdouble crs = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCrs);
+    jdouble crc = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCrc);
+    jdouble cuc = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCuc);
+    jdouble cus = env->CallDoubleMethod(secondOrderHarmonicPerturbationObj,
+                                        method_secondOrderHarmonicPerturbationGetCus);
+    keplerianOrbitModel.rootA = rootA;
+    keplerianOrbitModel.eccentricity = eccentricity;
+    keplerianOrbitModel.m0 = m0;
+    keplerianOrbitModel.omega = omega;
+    keplerianOrbitModel.omegaDot = omegaDot;
+    keplerianOrbitModel.deltaN = deltaN;
+    keplerianOrbitModel.iDot = iDot;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.cic = cic;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.cis = cis;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.crs = crs;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.crc = crc;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.cuc = cuc;
+    keplerianOrbitModel.secondOrderHarmonicPerturbation.cus = cus;
+    env->DeleteLocalRef(secondOrderHarmonicPerturbationObj);
+}
+
+void GnssAssistanceUtil::setKlobucharIonosphericModel(
+        JNIEnv* env, jobject klobucharIonosphericModelObj,
+        KlobucharIonosphericModel& klobucharIonosphericModel) {
+    if (klobucharIonosphericModelObj == nullptr) return;
+    jdouble alpha0 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                           method_klobucharIonosphericModelGetAlpha0);
+    jdouble alpha1 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                           method_klobucharIonosphericModelGetAlpha1);
+    jdouble alpha2 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                           method_klobucharIonosphericModelGetAlpha2);
+    jdouble alpha3 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                           method_klobucharIonosphericModelGetAlpha3);
+    jdouble beta0 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                          method_klobucharIonosphericModelGetBeta0);
+    jdouble beta1 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                          method_klobucharIonosphericModelGetBeta1);
+    jdouble beta2 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                          method_klobucharIonosphericModelGetBeta2);
+    jdouble beta3 = env->CallDoubleMethod(klobucharIonosphericModelObj,
+                                          method_klobucharIonosphericModelGetBeta3);
+    klobucharIonosphericModel.alpha0 = alpha0;
+    klobucharIonosphericModel.alpha1 = alpha1;
+    klobucharIonosphericModel.alpha2 = alpha2;
+    klobucharIonosphericModel.alpha3 = alpha3;
+    klobucharIonosphericModel.beta0 = beta0;
+    klobucharIonosphericModel.beta1 = beta1;
+    klobucharIonosphericModel.beta2 = beta2;
+    klobucharIonosphericModel.beta3 = beta3;
+}
+
+void GnssAssistanceUtil::setUtcModel(JNIEnv* env, jobject utcModelObj, UtcModel& utcModel) {
+    if (utcModelObj == nullptr) {
+        utcModel.weekNumber = -1;
+        return;
+    }
+    jdouble a0 = env->CallDoubleMethod(utcModelObj, method_utcModelGetA0);
+    jdouble a1 = env->CallDoubleMethod(utcModelObj, method_utcModelGetA1);
+    jlong timeOfWeek = env->CallLongMethod(utcModelObj, method_utcModelGetTimeOfWeek);
+    jint weekNumber = env->CallIntMethod(utcModelObj, method_utcModelGetWeekNumber);
+    utcModel.a0 = a0;
+    utcModel.a1 = a1;
+    utcModel.timeOfWeek = timeOfWeek;
+    utcModel.weekNumber = static_cast<int32_t>(weekNumber);
+}
+
+void GnssAssistanceUtil::setGnssAlmanac(JNIEnv* env, jobject gnssAlmanacObj,
+                                        GnssAlmanac& gnssAlmanac) {
+    if (gnssAlmanacObj == nullptr) {
+        gnssAlmanac.weekNumber = -1;
+        return;
+    }
+    jlong issueDateMillis =
+            env->CallLongMethod(gnssAlmanacObj, method_gnssAlmanacGetIssueDateMillis);
+    jint ioda = env->CallIntMethod(gnssAlmanacObj, method_gnssAlmanacGetIoda);
+    jint weekNumber = env->CallIntMethod(gnssAlmanacObj, method_gnssAlmanacGetWeekNumber);
+    jlong toaSeconds = env->CallLongMethod(gnssAlmanacObj, method_gnssAlmanacGetToaSeconds);
+    jboolean isCompleteAlmanacProvided =
+            env->CallBooleanMethod(gnssAlmanacObj, method_gnssAlmanacIsCompleteAlmanacProvided);
+    gnssAlmanac.issueDateMs = issueDateMillis;
+    gnssAlmanac.ioda = ioda;
+    gnssAlmanac.weekNumber = weekNumber;
+    gnssAlmanac.toaSeconds = toaSeconds;
+    gnssAlmanac.isCompleteAlmanacProvided = isCompleteAlmanacProvided;
+
+    jobject satelliteAlmanacsListObj =
+            env->CallObjectMethod(gnssAlmanacObj, method_gnssAlmanacGetSatelliteAlmanacs);
+    auto len = env->CallIntMethod(satelliteAlmanacsListObj, method_listSize);
+    std::vector<GnssSatelliteAlmanac> list(len);
+    for (uint16_t i = 0; i < len; ++i) {
+        jobject gnssSatelliteAlmanacObj =
+                env->CallObjectMethod(satelliteAlmanacsListObj, method_listGet, i);
+        if (gnssSatelliteAlmanacObj == nullptr) continue;
+        GnssSatelliteAlmanac gnssSatelliteAlmanac;
+        jint svid = env->CallIntMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetSvid);
+        jint svHealth =
+                env->CallIntMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetSvHealth);
+        jdouble af0 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetAf0);
+        jdouble af1 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetAf1);
+        jdouble eccentricity = env->CallDoubleMethod(gnssSatelliteAlmanacObj,
+                                                     method_satelliteAlmanacGetEccentricity);
+        jdouble inclination = env->CallDoubleMethod(gnssSatelliteAlmanacObj,
+                                                    method_satelliteAlmanacGetInclination);
+        jdouble m0 = env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetM0);
+        jdouble omega =
+                env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmega);
+        jdouble omega0 =
+                env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmega0);
+        jdouble omegaDot =
+                env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetOmegaDot);
+        jdouble rootA =
+                env->CallDoubleMethod(gnssSatelliteAlmanacObj, method_satelliteAlmanacGetRootA);
+        gnssSatelliteAlmanac.svid = static_cast<int32_t>(svid);
+        gnssSatelliteAlmanac.svHealth = static_cast<int32_t>(svHealth);
+        gnssSatelliteAlmanac.af0 = af0;
+        gnssSatelliteAlmanac.af1 = af1;
+        gnssSatelliteAlmanac.eccentricity = eccentricity;
+        gnssSatelliteAlmanac.inclination = inclination;
+        gnssSatelliteAlmanac.m0 = m0;
+        gnssSatelliteAlmanac.omega = omega;
+        gnssSatelliteAlmanac.omega0 = omega0;
+        gnssSatelliteAlmanac.omegaDot = omegaDot;
+        gnssSatelliteAlmanac.rootA = rootA;
+        list.at(i) = gnssSatelliteAlmanac;
+        env->DeleteLocalRef(gnssSatelliteAlmanacObj);
+    }
+    gnssAlmanac.satelliteAlmanacs = list;
+    env->DeleteLocalRef(satelliteAlmanacsListObj);
+}
+
+void GnssAssistanceUtil::setAuxiliaryInformation(JNIEnv* env, jobject auxiliaryInformationObj,
+                                                 AuxiliaryInformation& auxiliaryInformation) {
+    if (auxiliaryInformationObj == nullptr) {
+        auxiliaryInformation.svid = -1;
+        return;
+    }
+    jint svid = env->CallIntMethod(auxiliaryInformationObj, method_auxiliaryInformationGetSvid);
+    jobject availableSignalTypesObj =
+            env->CallObjectMethod(auxiliaryInformationObj,
+                                  method_auxiliaryInformationGetAvailableSignalTypes);
+    auto size = env->CallIntMethod(availableSignalTypesObj, method_listSize);
+    std::vector<GnssSignalType> availableSignalTypes(size);
+    for (uint16_t i = 0; i < size; ++i) {
+        jobject availableSignalTypeObj =
+                env->CallObjectMethod(availableSignalTypesObj, method_listGet, i);
+        GnssSignalType availableSignalType;
+        setGnssSignalType(env, availableSignalTypeObj, availableSignalType);
+        availableSignalTypes.at(i) = availableSignalType;
+        env->DeleteLocalRef(availableSignalTypeObj);
+    }
+    jint frequencyChannelNumber =
+            env->CallIntMethod(auxiliaryInformationObj,
+                               method_auxiliaryInformationGetFrequencyChannelNumber);
+    jint satType =
+            env->CallIntMethod(auxiliaryInformationObj, method_auxiliaryInformationGetSatType);
+    auxiliaryInformation.svid = static_cast<int32_t>(svid);
+    auxiliaryInformation.availableSignalTypes = availableSignalTypes;
+    auxiliaryInformation.frequencyChannelNumber = static_cast<int32_t>(frequencyChannelNumber);
+    auxiliaryInformation.satType = static_cast<BeidouB1CSatelliteOrbitType>(satType);
+    env->DeleteLocalRef(availableSignalTypesObj);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAssistance.h b/services/core/jni/gnss/GnssAssistance.h
new file mode 100644
index 0000000..ee97e19
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistance.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSSASSITANCE_H
+#define _ANDROID_SERVER_GNSSASSITANCE_H
+
+#include <sys/stat.h>
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/gnss_assistance/BnGnssAssistanceInterface.h>
+#include <log/log.h>
+
+#include "GnssAssistanceCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+using IGnssAssistanceInterface = android::hardware::gnss::gnss_assistance::IGnssAssistanceInterface;
+using IGnssAssistanceCallback = android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback;
+using BeidouAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::BeidouAssistance;
+using BeidouSatelliteEphemeris = android::hardware::gnss::gnss_assistance::BeidouSatelliteEphemeris;
+using GalileoAssistance =
+        android::hardware::gnss::gnss_assistance::GnssAssistance::GalileoAssistance;
+using GalileoSatelliteEphemeris =
+        android::hardware::gnss::gnss_assistance::GalileoSatelliteEphemeris;
+using GalileoIonosphericModel = android::hardware::gnss::gnss_assistance::GalileoIonosphericModel;
+using GlonassAssistance =
+        android::hardware::gnss::gnss_assistance::GnssAssistance::GlonassAssistance;
+using GlonassAlmanac = android::hardware::gnss::gnss_assistance::GlonassAlmanac;
+using GlonassSatelliteEphemeris =
+        android::hardware::gnss::gnss_assistance::GlonassSatelliteEphemeris;
+using GnssAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance;
+using GnssSignalType = android::hardware::gnss::GnssSignalType;
+using GpsAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::GpsAssistance;
+using QzssAssistance = android::hardware::gnss::gnss_assistance::GnssAssistance::QzssAssistance;
+using GnssAlmanac = android::hardware::gnss::gnss_assistance::GnssAlmanac;
+using GnssSatelliteCorrections =
+        android::hardware::gnss::gnss_assistance::GnssAssistance::GnssSatelliteCorrections;
+using GpsSatelliteEphemeris = android::hardware::gnss::gnss_assistance::GpsSatelliteEphemeris;
+using SatelliteEphemerisTime = android::hardware::gnss::gnss_assistance::SatelliteEphemerisTime;
+using UtcModel = android::hardware::gnss::gnss_assistance::UtcModel;
+using LeapSecondsModel = android::hardware::gnss::gnss_assistance::LeapSecondsModel;
+using KeplerianOrbitModel = android::hardware::gnss::gnss_assistance::KeplerianOrbitModel;
+using KlobucharIonosphericModel =
+        android::hardware::gnss::gnss_assistance::KlobucharIonosphericModel;
+using TimeModel = android::hardware::gnss::gnss_assistance::TimeModel;
+using RealTimeIntegrityModel = android::hardware::gnss::gnss_assistance::RealTimeIntegrityModel;
+using AuxiliaryInformation = android::hardware::gnss::gnss_assistance::AuxiliaryInformation;
+
+void GnssAssistance_class_init_once(JNIEnv* env, jclass clazz);
+
+class GnssAssistanceInterface {
+public:
+    GnssAssistanceInterface(const sp<IGnssAssistanceInterface>& iGnssAssistance);
+    jboolean injectGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj);
+    jboolean setCallback(const sp<IGnssAssistanceCallback>& callback);
+
+private:
+    const sp<IGnssAssistanceInterface> mGnssAssistanceInterface;
+};
+
+struct GnssAssistanceUtil {
+    static void setGlonassAssistance(JNIEnv* env, jobject glonassAssistanceObj,
+                                     GlonassAssistance& galileoAssistance);
+    static void setGlonassSatelliteEphemeris(
+            JNIEnv* env, jobject glonassSatelliteEphemerisObj,
+            std::vector<GlonassSatelliteEphemeris>& glonassSatelliteEphemerisList);
+    static void setGlonassAlmanac(JNIEnv* env, jobject glonassAlmanacObj,
+                                  GlonassAlmanac& glonassAlmanac);
+    static void setBeidouAssistance(JNIEnv* env, jobject beidouAssistanceObj,
+                                    BeidouAssistance& beidouAssistance);
+    static void setBeidouSatelliteEphemeris(
+            JNIEnv* env, jobject beidouSatelliteEphemerisObj,
+            std::vector<BeidouSatelliteEphemeris>& beidouSatelliteEphemerisList);
+    static void setGalileoAssistance(JNIEnv* env, jobject galileoAssistanceObj,
+                                     GalileoAssistance& galileoAssistance);
+    static void setGalileoSatelliteEphemeris(
+            JNIEnv* env, jobject galileoSatelliteEphemerisObj,
+            std::vector<GalileoSatelliteEphemeris>& galileoSatelliteEphemerisList);
+    static void setGaliloKlobucharIonosphericModel(JNIEnv* env, jobject galileoIonosphericModelObj,
+                                                   GalileoIonosphericModel& ionosphericModel);
+    static void setGnssAssistance(JNIEnv* env, jobject gnssAssistanceObj,
+                                  GnssAssistance& gnssAssistance);
+    static void setGpsAssistance(JNIEnv* env, jobject gpsAssistanceObj,
+                                 GpsAssistance& gpsAssistance);
+    template <class T>
+    static void setGpsOrQzssSatelliteEphemeris(JNIEnv* env, jobject satelliteEphemerisObj,
+                                               std::vector<T>& satelliteEphemeris);
+    static void setQzssAssistance(JNIEnv* env, jobject qzssAssistanceObj,
+                                  QzssAssistance& qzssAssistance);
+    static void setGnssAlmanac(JNIEnv* env, jobject gnssAlmanacObj, GnssAlmanac& gnssAlmanac);
+    static void setGnssSignalType(JNIEnv* env, jobject gnssSignalTypeObj,
+                                  GnssSignalType& gnssSignalType);
+    static void setKeplerianOrbitModel(JNIEnv* env, jobject keplerianOrbitModelObj,
+                                       KeplerianOrbitModel& keplerianOrbitModel);
+    static void setKlobucharIonosphericModel(JNIEnv* env, jobject klobucharIonosphericModelObj,
+                                             KlobucharIonosphericModel& klobucharIonosphericModel);
+    static void setTimeModels(JNIEnv* env, jobject timeModelsObj,
+                              std::vector<TimeModel>& timeModels);
+    static void setLeapSecondsModel(JNIEnv* env, jobject leapSecondsModelObj,
+                                    LeapSecondsModel& leapSecondsModel);
+    static void setRealTimeIntegrityModels(
+            JNIEnv* env, jobject realTimeIntegrityModelsObj,
+            std::vector<RealTimeIntegrityModel>& realTimeIntegrityModels);
+
+    static void setSatelliteEphemerisTime(JNIEnv* env, jobject satelliteEphemerisTimeObj,
+                                          SatelliteEphemerisTime& satelliteEphemerisTime);
+    static void setUtcModel(JNIEnv* env, jobject utcModelObj, UtcModel& utcModel);
+    static void setSatelliteCorrections(
+            JNIEnv* env, jobject satelliteCorrectionsObj,
+            std::vector<GnssSatelliteCorrections>& satelliteCorrections);
+    static void setAuxiliaryInformation(JNIEnv* env, jobject auxiliaryInformationObj,
+                                        AuxiliaryInformation& auxiliaryInformation);
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSSASSITANCE__H
diff --git a/services/core/jni/gnss/GnssAssistanceCallback.cpp b/services/core/jni/gnss/GnssAssistanceCallback.cpp
new file mode 100644
index 0000000..dbb27d7
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistanceCallback.cpp
@@ -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.
+ */
+
+#define LOG_TAG "GnssAssistanceCbJni"
+
+#include "GnssAssistanceCallback.h"
+
+#include "Utils.h"
+
+namespace {
+jmethodID method_gnssAssistanceInjectRequest;
+} // anonymous namespace
+
+namespace android::gnss {
+
+using binder::Status;
+using hardware::Return;
+using hardware::Void;
+
+void GnssAssistanceCallback_class_init_once(JNIEnv* env, jclass clazz) {
+    method_gnssAssistanceInjectRequest =
+            env->GetStaticMethodID(clazz, "gnssAssistanceInjectRequest", "()V");
+}
+
+// Implementation of android::hardware::gnss::gnss_assistance::GnssAssistanceCallback.
+
+Status GnssAssistanceCallback::injectRequestCb() {
+    ALOGD("%s.", __func__);
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_gnssAssistanceInjectRequest);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAssistanceCallback.h b/services/core/jni/gnss/GnssAssistanceCallback.h
new file mode 100644
index 0000000..4c8c5fc
--- /dev/null
+++ b/services/core/jni/gnss/GnssAssistanceCallback.h
@@ -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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/gnss_assistance/BnGnssAssistanceCallback.h>
+#include <log/log.h>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+void GnssAssistanceCallback_class_init_once(JNIEnv* env, jclass clazz);
+
+/*
+ * GnssAssistanceCallback class implements the callback methods required by the
+ * android::hardware::gnss::gnss_assistance::IGnssAssistanceCallback interface.
+ */
+class GnssAssistanceCallback : public hardware::gnss::gnss_assistance::BnGnssAssistanceCallback {
+public:
+    GnssAssistanceCallback() {}
+    binder::Status injectRequestCb() override;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSASSITANCECALLBACK_H
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 42e457c..bc5c427 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -51,7 +51,6 @@
 import android.credentials.PrepareGetCredentialResponseInternal;
 import android.credentials.RegisterCredentialDescriptionRequest;
 import android.credentials.UnregisterCredentialDescriptionRequest;
-import android.credentials.flags.Flags;
 import android.os.Binder;
 import android.os.CancellationSignal;
 import android.os.IBinder;
@@ -538,34 +537,31 @@
 
             final int userId = UserHandle.getCallingUserId();
             final int callingUid = Binder.getCallingUid();
-            if (Flags.safeguardCandidateCredentialsApiCaller()) {
-                try {
-                    String credentialManagerAutofillCompName = mContext.getResources().getString(
-                            R.string.config_defaultCredentialManagerAutofillService);
-                    ComponentName componentName = ComponentName.unflattenFromString(
-                            credentialManagerAutofillCompName);
-                    if (componentName == null) {
-                        throw new SecurityException(
-                                "Credential Autofill service does not exist on this device.");
-                    }
-                    PackageManager pm = mContext.createContextAsUser(
-                            UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
-                    String callingProcessPackage = pm.getNameForUid(callingUid);
-                    if (callingProcessPackage == null) {
-                        throw new SecurityException(
-                                "Couldn't determine the identity of the caller.");
-                    }
-                    if (!Objects.equals(componentName.getPackageName(), callingProcessPackage)) {
-                        throw new SecurityException(callingProcessPackage
-                                + " is not the device's credential autofill package.");
-                    }
-                } catch (Resources.NotFoundException e) {
+            try {
+                String credentialManagerAutofillCompName = mContext.getResources().getString(
+                        R.string.config_defaultCredentialManagerAutofillService);
+                ComponentName componentName = ComponentName.unflattenFromString(
+                        credentialManagerAutofillCompName);
+                if (componentName == null) {
                     throw new SecurityException(
                             "Credential Autofill service does not exist on this device.");
                 }
+                PackageManager pm = mContext.createContextAsUser(
+                        UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
+                String callingProcessPackage = pm.getNameForUid(callingUid);
+                if (callingProcessPackage == null) {
+                    throw new SecurityException(
+                            "Couldn't determine the identity of the caller.");
+                }
+                if (!Objects.equals(componentName.getPackageName(), callingProcessPackage)) {
+                    throw new SecurityException(callingProcessPackage
+                            + " is not the device's credential autofill package.");
+                }
+            } catch (Resources.NotFoundException e) {
+                throw new SecurityException(
+                        "Credential Autofill service does not exist on this device.");
             }
 
-
             // New request session, scoped for this request only.
             final GetCandidateRequestSession session =
                     new GetCandidateRequestSession(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index a81a0b3..4b98a72 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -50,6 +50,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
+import android.util.PrintWriterPrinter;
 
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
@@ -1484,5 +1485,12 @@
             pw.print("mProvisioningContext=");
             pw.println(mProvisioningContext);
         }
+
+        if (info != null) {
+            pw.println("DeviceAdminInfo:");
+            pw.increaseIndent();
+            info.dump(new PrintWriterPrinter(pw), "");
+            pw.decreaseIndent();
+        }
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 215d6ca..f055feb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -245,6 +245,7 @@
 import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED;
 import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
 import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
+import static android.content.Context.RECEIVER_NOT_EXPORTED;
 import static android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE;
 import static android.content.Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -275,6 +276,7 @@
 import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
 import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
 import static com.android.server.devicepolicy.DevicePolicyEngine.DEFAULT_POLICY_SIZE_LIMIT;
+import static com.android.server.devicepolicy.DevicePolicyEngine.SYSTEM_SUPERVISION_ROLE;
 import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE;
 import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__COPE;
 import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__DEVICE_OWNER;
@@ -486,6 +488,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
+import android.telephony.euicc.EuiccManager;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -643,6 +646,14 @@
     static final String ACTION_PROFILE_OFF_DEADLINE =
             "com.android.server.ACTION_PROFILE_OFF_DEADLINE";
 
+    /** Broadcast action invoked when a managed eSIM is removed while deleting work profile. */
+    private static final String ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE =
+            "com.android.server.ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE";
+
+    /** Extra for the subscription ID of the managed eSIM removed while deleting work profile. */
+    private static final String EXTRA_REMOVED_ESIM_SUBSCRIPTION_ID =
+            "com.android.server.EXTRA_ESIM_REMOVED_WITH_MANAGED_PROFILE_SUBSCRIPTION_ID";
+
     private static final String CALLED_FROM_PARENT = "calledFromParent";
     private static final String NOT_CALLED_FROM_PARENT = "notCalledFromParent";
 
@@ -1266,6 +1277,8 @@
                 removeCredentialManagementApp(intent.getData().getSchemeSpecificPart());
             } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)) {
                 clearWipeProfileNotification();
+            } else if (Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+                removeManagedEmbeddedSubscriptionsForUser(userHandle);
             } else if (Intent.ACTION_DATE_CHANGED.equals(action)
                     || Intent.ACTION_TIME_CHANGED.equals(action)) {
                 // Update freeze period record when clock naturally progresses to the next day
@@ -1298,6 +1311,13 @@
                 triggerPolicyComplianceCheckIfNeeded(userHandle, suspended);
             } else if (LOGIN_ACCOUNTS_CHANGED_ACTION.equals(action)) {
                 calculateHasIncompatibleAccounts();
+            } else if (ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE.equals(action)) {
+                int removedSubscriptionId = intent.getIntExtra(EXTRA_REMOVED_ESIM_SUBSCRIPTION_ID,
+                        -1);
+                Slogf.i(LOG_TAG,
+                        "Deleted subscription with ID %d because owning managed profile was "
+                                + "removed",
+                        removedSubscriptionId);
             }
         }
 
@@ -2219,9 +2239,14 @@
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
         filter = new IntentFilter();
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
         filter.addAction(Intent.ACTION_TIME_CHANGED);
         filter.addAction(Intent.ACTION_DATE_CHANGED);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+        filter = new IntentFilter();
+        filter.addAction(ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE);
+        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null,
+                mHandler, RECEIVER_NOT_EXPORTED);
 
         LocalServices.addService(DevicePolicyManagerInternal.class, mLocalService);
 
@@ -3782,9 +3807,10 @@
                 // Update user switcher message to activity manager.
                 ActivityManagerInternal activityManagerInternal =
                         mInjector.getActivityManagerInternal();
-                activityManagerInternal.setSwitchingFromSystemUserMessage(
+                int deviceOwnerUserId = UserHandle.getUserId(deviceOwner.getUid());
+                activityManagerInternal.setSwitchingFromUserMessage(deviceOwnerUserId,
                         deviceOwner.startUserSessionMessage);
-                activityManagerInternal.setSwitchingToSystemUserMessage(
+                activityManagerInternal.setSwitchingToUserMessage(deviceOwnerUserId,
                         deviceOwner.endUserSessionMessage);
             }
 
@@ -3970,6 +3996,7 @@
             deletedUsers.remove(userInfo.id);
         }
         for (Integer userId : deletedUsers) {
+            removeManagedEmbeddedSubscriptionsForUser(userId);
             removeUserData(userId);
             mDevicePolicyEngine.handleUserRemoved(userId);
         }
@@ -8099,6 +8126,45 @@
         mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PROFILE_WIPED);
     }
 
+    /**
+     * Remove eSIM subscriptions that are managed by any of the admin packages of the given
+     * userHandle.
+     */
+    private void removeManagedEmbeddedSubscriptionsForUser(int userHandle) {
+        if (!Flags.removeManagedEsimOnWorkProfileDeletion()) {
+            return;
+        }
+
+        Slogf.i(LOG_TAG,
+                "Managed profile with ID=%d deleted: going to remove managed embedded "
+                        + "subscriptions", userHandle);
+        String profileOwnerPackage = mOwners.getProfileOwnerPackage(userHandle);
+        if (profileOwnerPackage == null) {
+            Slogf.wtf(LOG_TAG, "Profile owner package for managed profile is null");
+            return;
+        }
+        IntArray managedSubscriptionIds = getSubscriptionIdsInternal(profileOwnerPackage);
+        deleteEmbeddedSubscriptions(managedSubscriptionIds);
+    }
+
+    private void deleteEmbeddedSubscriptions(IntArray subscriptionIds) {
+        EuiccManager euiccManager = mContext.getSystemService(EuiccManager.class);
+        for (int subscriptionId : subscriptionIds.toArray()) {
+            Slogf.i(LOG_TAG, "Deleting embedded subscription with ID %d", subscriptionId);
+            euiccManager.deleteSubscription(subscriptionId,
+                    createCallbackPendingIntentForRemovingManagedSubscription(
+                            subscriptionId));
+        }
+    }
+
+    private PendingIntent createCallbackPendingIntentForRemovingManagedSubscription(
+            Integer subscriptionId) {
+        Intent intent = new Intent(ACTION_ESIM_REMOVED_WITH_MANAGED_PROFILE);
+        intent.putExtra(EXTRA_REMOVED_ESIM_SUBSCRIPTION_ID, subscriptionId);
+        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+    }
+
+
     @Override
     public void setFactoryResetProtectionPolicy(ComponentName who, String callerPackageName,
             @Nullable FactoryResetProtectionPolicy policy) {
@@ -16231,6 +16297,13 @@
         return null;
     }
 
+    /**
+     * When multiple admins enforce a policy, this method returns an admin according to this order:
+     * 1. Supervision
+     * 2. DPC
+     *
+     * Otherwise, it returns any other admin.
+     */
     private android.app.admin.EnforcingAdmin getEnforcingAdminInternal(int userId,
             String identifier) {
         Objects.requireNonNull(identifier);
@@ -16239,16 +16312,22 @@
         if (admins.isEmpty()) {
             return null;
         }
-
-        final EnforcingAdmin admin;
         if (admins.size() == 1) {
-            admin = admins.iterator().next();
-        } else {
-            Optional<EnforcingAdmin> dpc = admins.stream()
-                    .filter(a -> a.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)).findFirst();
-            admin = dpc.orElseGet(() -> admins.stream().findFirst().get());
+            return admins.iterator().next().getParcelableAdmin();
         }
-        return admin == null ? null : admin.getParcelableAdmin();
+        Optional<EnforcingAdmin> supervision = admins.stream()
+                .filter(a -> a.hasAuthority(
+                        EnforcingAdmin.getRoleAuthorityOf(SYSTEM_SUPERVISION_ROLE)))
+                .findFirst();
+        if (supervision.isPresent()) {
+            return supervision.get().getParcelableAdmin();
+        }
+        Optional<EnforcingAdmin> dpc = admins.stream()
+                .filter(a -> a.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)).findFirst();
+        if (dpc.isPresent()) {
+            return dpc.get().getParcelableAdmin();
+        }
+        return admins.iterator().next().getParcelableAdmin();
     }
 
     private <V> Set<EnforcingAdmin> getEnforcingAdminsForIdentifier(int userId, String identifier) {
@@ -19652,7 +19731,7 @@
         }
 
         mInjector.getActivityManagerInternal()
-                .setSwitchingFromSystemUserMessage(startUserSessionMessageString);
+                .setSwitchingFromUserMessage(caller.getUserId(), startUserSessionMessageString);
     }
 
     @Override
@@ -19677,7 +19756,7 @@
         }
 
         mInjector.getActivityManagerInternal()
-                .setSwitchingToSystemUserMessage(endUserSessionMessageString);
+                .setSwitchingToUserMessage(caller.getUserId(), endUserSessionMessageString);
     }
 
     @Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 543e32f..9ff6eb6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -34,6 +34,7 @@
 import android.app.admin.PolicyKey;
 import android.app.admin.PolicyValue;
 import android.app.admin.UserRestrictionPolicyKey;
+import android.app.admin.flags.Flags;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IntentFilter;
@@ -282,7 +283,9 @@
 
     static PolicyDefinition<Set<String>> PERMITTED_INPUT_METHODS = new PolicyDefinition<>(
             new NoArgsPolicyKey(DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY),
-            new MostRecent<>(),
+            (Flags.usePolicyIntersectionForPermittedInputMethods()
+                ? new StringSetIntersection()
+                : new MostRecent<>()),
             POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE,
             PolicyEnforcerCallbacks::noOp,
             new PackageSetPolicySerializer());
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java
new file mode 100644
index 0000000..bc075b02
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.devicepolicy;
+
+import android.annotation.NonNull;
+import android.app.admin.PolicyValue;
+import android.app.admin.PackageSetPolicyValue;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Objects;
+import java.util.Set;
+
+final class StringSetIntersection extends ResolutionMechanism<Set<String>> {
+
+    @Override
+    PolicyValue<Set<String>> resolve(
+            @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<Set<String>>> adminPolicies) {
+        Objects.requireNonNull(adminPolicies);
+        Set<String> intersectionOfPolicies = null;
+        for (PolicyValue<Set<String>> policy : adminPolicies.values()) {
+            if (intersectionOfPolicies == null) {
+                intersectionOfPolicies = new HashSet<>(policy.getValue());
+            } else {
+                intersectionOfPolicies.retainAll(policy.getValue());
+            }
+        }
+        if (intersectionOfPolicies == null) {
+            return null;
+        }
+        // Note that the resulting set below may be empty, but that's fine:
+        // particular policy should decide what is the meaning of an empty set.
+        return new PackageSetPolicyValue(intersectionOfPolicies);
+    }
+
+    @Override
+    android.app.admin.StringSetIntersection getParcelableResolutionMechanism() {
+        return new android.app.admin.StringSetIntersection();
+    }
+
+    @Override
+    public String toString() {
+        return "StringSetIntersection {}";
+    }
+}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index 8e74952..69e856d 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -20,6 +20,7 @@
 import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE;
 import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL;
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
+import static android.os.Trace.TRACE_TAG_SYSTEM_SERVER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.TYPE_EXTERNAL;
 
@@ -324,6 +325,12 @@
             }
 
             if (newState != INVALID_DEVICE_STATE_IDENTIFIER && newState != mLastReportedState) {
+                if (mLastHingeAngleSensorEvent != null
+                        && Trace.isTagEnabled(TRACE_TAG_SYSTEM_SERVER)) {
+                    Trace.instant(TRACE_TAG_SYSTEM_SERVER,
+                            "[Device state changed] Last hinge sensor event timestamp: "
+                                    + mLastHingeAngleSensorEvent.timestamp);
+                }
                 mLastReportedState = newState;
                 stateToReport = newState;
             }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 860b6fb..788b3b8 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -366,6 +366,8 @@
             "com.android.clockwork.time.WearTimeService";
     private static final String WEAR_SETTINGS_SERVICE_CLASS =
             "com.android.clockwork.settings.WearSettingsService";
+    private static final String WEAR_GESTURE_SERVICE_CLASS =
+            "com.android.clockwork.gesture.WearGestureService";
     private static final String WRIST_ORIENTATION_SERVICE_CLASS =
             "com.android.clockwork.wristorientation.WristOrientationService";
     private static final String IOT_SERVICE_CLASS =
@@ -2844,6 +2846,13 @@
                 mSystemServiceManager.startService(WRIST_ORIENTATION_SERVICE_CLASS);
                 t.traceEnd();
             }
+
+            if (android.server.Flags.wearGestureApi()
+                    && SystemProperties.getBoolean("config.enable_gesture_api", false)) {
+                t.traceBegin("StartWearGestureService");
+                mSystemServiceManager.startService(WEAR_GESTURE_SERVICE_CLASS);
+                t.traceEnd();
+            }
         }
 
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) {
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 86ccd87..7a6bd75 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -65,4 +65,12 @@
      namespace: "package_manager_service"
      description: "Remove AppIntegrityManagerService"
      bug: "364200023"
+}
+
+flag {
+     name: "wear_gesture_api"
+     namespace: "wear_frameworks"
+     description: "Whether the Wear Gesture API is available."
+     bug: "396154116"
+     is_exported: true
 }
\ No newline at end of file
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 2dd16f6..80a3a87 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -109,6 +109,7 @@
 import com.android.server.LocalServices;
 import com.android.server.backup.BackupAgentConnectionManager;
 import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.BackupRestoreTask.CancellationReason;
 import com.android.server.backup.BackupWakeLock;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.KeyValueBackupJob;
@@ -2412,7 +2413,7 @@
         KeyValueBackupTask task = spy(createKeyValueBackupTask(transportMock, PACKAGE_1));
         doNothing().when(task).waitCancel();
 
-        task.handleCancel(true);
+        task.handleCancel(CancellationReason.EXTERNAL);
 
         InOrder inOrder = inOrder(task);
         inOrder.verify(task).markCancel();
@@ -2420,12 +2421,14 @@
     }
 
     @Test
-    public void testHandleCancel_whenCancelAllFalse_throws() throws Exception {
+    public void testHandleCancel_timeout_throws() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
         setUpAgentWithData(PACKAGE_1);
         KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
 
-        expectThrows(IllegalArgumentException.class, () -> task.handleCancel(false));
+        expectThrows(
+                IllegalArgumentException.class,
+                () -> task.handleCancel(CancellationReason.TIMEOUT));
     }
 
     /** Do not update backup token if no data was moved. */
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/Android.bp b/services/tests/DynamicInstrumentationManagerServiceTests/Android.bp
index 2c2e5fd..b354e36 100644
--- a/services/tests/DynamicInstrumentationManagerServiceTests/Android.bp
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/Android.bp
@@ -29,6 +29,7 @@
     static_libs: [
         "androidx.test.core",
         "androidx.test.runner",
+        "flag-junit",
         "hamcrest-library",
         "platform-test-annotations",
         "services.core",
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/OWNERS b/services/tests/DynamicInstrumentationManagerServiceTests/OWNERS
new file mode 100644
index 0000000..2522426
--- /dev/null
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/UprobeStats:/OWNERS
\ No newline at end of file
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.java
index 085f595..d3a287d 100644
--- a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.java
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.java
@@ -17,6 +17,9 @@
 package com.android.server;
 
 public class TestClass {
+    TestClass() {
+    }
+
     void primitiveParams(boolean a, boolean[] b, byte c, byte[] d, char e, char[] f, short g,
             short[] h, int i, int[] j, long k, long[] l, float m, float[] n, double o, double[] p) {
     }
diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
index 6e14bad..905c244 100644
--- a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
+++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java
@@ -22,6 +22,9 @@
 import android.os.instrumentation.MethodDescriptor;
 import android.os.instrumentation.MethodDescriptorParser;
 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 androidx.test.filters.SmallTest;
 
@@ -31,9 +34,10 @@
 import com.android.server.TestInterface;
 import com.android.server.TestInterfaceImpl;
 
+import org.junit.Rule;
 import org.junit.Test;
 
-import java.lang.reflect.Method;
+import java.lang.reflect.Executable;
 
 
 /**
@@ -53,6 +57,10 @@
     private static final String[] CLASS_PARAMS =
             new String[]{"java.lang.String", "java.lang.String[]"};
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Test
     public void primitiveParams() {
         assertNotNull(parseMethodDescriptor(TestClass.class.getName(), "primitiveParams",
@@ -66,6 +74,13 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.art.flags.Flags.FLAG_EXECUTABLE_METHOD_FILE_OFFSETS_V2)
+    public void constructor() {
+        assertNotNull(
+                parseMethodDescriptor(TestClass.class.getName(), "<init>"));
+    }
+
+    @Test
     public void publicMethod() {
         assertNotNull(
                 parseMethodDescriptor(TestClass.class.getName(), "publicMethod"));
@@ -119,13 +134,14 @@
                         new String[]{"int"}));
     }
 
-    private Method parseMethodDescriptor(String fqcn, String methodName) {
+    private Executable parseMethodDescriptor(String fqcn, String methodName) {
         return MethodDescriptorParser.parseMethodDescriptor(
                 getClass().getClassLoader(),
                 getMethodDescriptor(fqcn, methodName, new String[]{}));
     }
 
-    private Method parseMethodDescriptor(String fqcn, String methodName, String[] fqParameters) {
+    private Executable parseMethodDescriptor(
+            String fqcn, String methodName, String[] fqParameters) {
         return MethodDescriptorParser.parseMethodDescriptor(
                 getClass().getClassLoader(),
                 getMethodDescriptor(fqcn, methodName, fqParameters));
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index ae9a34e..c1d8382 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -73,10 +73,7 @@
     static_libs: [
         "androidx.annotation_annotation",
         "androidx.test.rules",
-        "framework",
-        "ravenwood-runtime",
-        "ravenwood-utils",
-        "services",
+        "services.core",
     ],
     libs: [
         "android.test.base.stubs.system",
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 05615f6..2339a94 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -287,6 +287,7 @@
                 mMockRemoteAccessibilityInputConnection /* remoteAccessibilityInputConnection */,
                 mTargetSdkVersion /* unverifiedTargetSdkVersion */,
                 mUserId /* userId */,
-                mMockImeOnBackInvokedDispatcher /* imeDispatcher */);
+                mMockImeOnBackInvokedDispatcher /* imeDispatcher */,
+                true /* imeRequestedVisible */);
     }
 }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index 70eeae6..aa77919 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -267,7 +267,8 @@
             // visibility state will be preserved to the current window state.
             final ImeTargetWindowState stateWithUnChangedFlag = initImeTargetWindowState(
                     mWindowToken);
-            mComputer.computeState(stateWithUnChangedFlag, true /* allowVisible */);
+            mComputer.computeState(stateWithUnChangedFlag, true /* allowVisible */,
+                    true /* imeRequestedVisible */);
             assertThat(stateWithUnChangedFlag.isRequestedImeVisible()).isEqualTo(
                     lastState.isRequestedImeVisible());
         }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
index 11abc94..b81b570 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
@@ -320,7 +320,8 @@
                 mMockRemoteAccessibilityInputConnection /* remoteAccessibilityInputConnection */,
                 mTargetSdkVersion /* unverifiedTargetSdkVersion */,
                 mUserId /* userId */,
-                mMockImeOnBackInvokedDispatcher /* imeDispatcher */);
+                mMockImeOnBackInvokedDispatcher /* imeDispatcher */,
+                true /* imeRequestedVisible */);
     }
 
     @Test
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
index 6adb01c..e898c833 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
@@ -18,7 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.view.WindowManager;
 
 import androidx.annotation.NonNull;
@@ -28,7 +27,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -42,10 +40,6 @@
 
     private static final int ANY_USER_ID = 1;
 
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true).build();
-
     @Mock
     private InputMethodManagerService mMockInputMethodManagerService;
 
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
index e545a49..554b5b4 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
@@ -19,8 +19,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt
index afb18f5..5c9ba40 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt
@@ -32,7 +32,7 @@
 
     private var ignore = false
 
-    override fun apply(base: Statement?, description: Description?): Statement {
+    override fun apply(base: Statement, description: Description): Statement {
         return object : Statement() {
             override fun evaluate() {
                 ignore = false
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 9117cc8..a38ecc8 100644
--- a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
+++ b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
@@ -3186,6 +3186,32 @@
         assertEquals(profile, ikev2VpnProfile.toVpnProfile());
     }
 
+    @Test
+    public void testStartAlwaysOnVpnOnSafeMode() throws Exception {
+        final Vpn vpn = createVpn(PRIMARY_USER.id);
+        setMockedUsers(PRIMARY_USER);
+
+        // UID checks must return a different UID; otherwise it'll be treated as already prepared.
+        final int uid = Process.myUid() + 1;
+        when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+                .thenReturn(uid);
+        when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+                .thenReturn(mVpnProfile.encode());
+
+        setAndVerifyAlwaysOnPackage(vpn, uid, false);
+        assertTrue(vpn.startAlwaysOnVpn());
+        assertEquals(TEST_VPN_PKG, vpn.getAlwaysOnPackage());
+
+        // Simulate safe mode and restart the always-on VPN to verify the always-on package is not
+        // reset.
+        doReturn(null).when(mVpnProfileStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG));
+        doReturn(null).when(mPackageManager).queryIntentServicesAsUser(
+                any(), any(), eq(PRIMARY_USER.id));
+        doReturn(true).when(mPackageManager).isSafeMode();
+        assertFalse(vpn.startAlwaysOnVpn());
+        assertEquals(TEST_VPN_PKG, vpn.getAlwaysOnPackage());
+    }
+
     // Make it public and un-final so as to spy it
     public class TestDeps extends Vpn.Dependencies {
         TestDeps() {}
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 1f8ccde..2770caa8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -324,7 +324,8 @@
             return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
                     new VirtualDisplayAdapter.SurfaceControlDisplayFactory() {
                         @Override
-                        public IBinder createDisplay(String name, boolean secure, String uniqueId,
+                        public IBinder createDisplay(String name, boolean secure,
+                                boolean optimizeForPower, String uniqueId,
                                 float requestedRefreshRate) {
                             return mMockDisplayToken;
                         }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
index 29f0722..fecbc7c8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
@@ -25,7 +25,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.hardware.Sensor;
@@ -396,7 +396,7 @@
         assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
         assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
         assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
-        verifyZeroInteractions(mWakelockController);
+        verifyNoMoreInteractions(mWakelockController);
     }
 
     private void setScreenOffBecauseOfPositiveProximityState() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index 206c90d..6dc7361 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -47,7 +47,7 @@
     private val mockTopology = mock<DisplayTopology>()
     private val mockTopologyCopy = mock<DisplayTopology>()
     private val mockTopologyGraph = mock<DisplayTopologyGraph>()
-    private val mockIsExtendedDisplayEnabled = mock<() -> Boolean>()
+    private val mockIsExtendedDisplayAllowed = mock<() -> Boolean>()
     private val mockTopologySavedCallback = mock<() -> Unit>()
     private val mockTopologyChangedCallback =
         mock<(android.util.Pair<DisplayTopology, DisplayTopologyGraph>) -> Unit>()
@@ -73,10 +73,10 @@
             ) =
                 mockTopologyStore
         }
-        whenever(mockIsExtendedDisplayEnabled()).thenReturn(true)
+        whenever(mockIsExtendedDisplayAllowed()).thenReturn(true)
         whenever(mockTopology.copy()).thenReturn(mockTopologyCopy)
         whenever(mockTopologyCopy.getGraph(any())).thenReturn(mockTopologyGraph)
-        coordinator = DisplayTopologyCoordinator(injector, mockIsExtendedDisplayEnabled,
+        coordinator = DisplayTopologyCoordinator(injector, mockIsExtendedDisplayAllowed,
             mockTopologyChangedCallback, topologyChangeExecutor, DisplayManagerService.SyncRoot(),
             mockTopologySavedCallback)
     }
@@ -195,7 +195,7 @@
 
     @Test
     fun addDisplay_external_extendedDisplaysDisabled() {
-        whenever(mockIsExtendedDisplayEnabled()).thenReturn(false)
+        whenever(mockIsExtendedDisplayAllowed()).thenReturn(false)
 
         for (displayInfo in displayInfos) {
             coordinator.onDisplayAdded(displayInfo)
@@ -208,7 +208,7 @@
     @Test
     fun addDisplay_overlay_extendedDisplaysDisabled() {
         displayInfos[0].type = Display.TYPE_OVERLAY
-        whenever(mockIsExtendedDisplayEnabled()).thenReturn(false)
+        whenever(mockIsExtendedDisplayAllowed()).thenReturn(false)
 
         for (displayInfo in displayInfos) {
             coordinator.onDisplayAdded(displayInfo)
@@ -314,7 +314,7 @@
 
     @Test
     fun updateDisplay_external_extendedDisplaysDisabled() {
-        whenever(mockIsExtendedDisplayEnabled()).thenReturn(false)
+        whenever(mockIsExtendedDisplayAllowed()).thenReturn(false)
 
         for (displayInfo in displayInfos) {
             coordinator.onDisplayChanged(displayInfo)
@@ -328,7 +328,7 @@
     @Test
     fun updateDisplay_overlay_extendedDisplaysDisabled() {
         displayInfos[0].type = Display.TYPE_OVERLAY
-        whenever(mockIsExtendedDisplayEnabled()).thenReturn(false)
+        whenever(mockIsExtendedDisplayAllowed()).thenReturn(false)
 
         coordinator.onDisplayChanged(displayInfos[0])
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index f8b4113..b8a5f34 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -234,6 +234,7 @@
 
         doReturn(true).when(mFlags).isDisplayOffloadEnabled();
         doReturn(true).when(mFlags).isEvenDimmerEnabled();
+        doReturn(true).when(mFlags).isDisplayContentModeManagementEnabled();
         initDisplayOffloadSession();
     }
 
@@ -1468,6 +1469,103 @@
         assertFalse(mDisplayOffloadSession.isActive());
     }
 
+    @Test
+    public void testAllowsContentSwitch_firstDisplay() throws Exception {
+        // Set up a first display
+        setUpDisplay(new FakeDisplay(PORT_A));
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // The first display should be allowed to use the content mode switch
+        DisplayDevice firstDisplayDevice = mListener.addedDisplays.get(0);
+        assertTrue((firstDisplayDevice.getDisplayDeviceInfoLocked().flags
+                & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+    }
+
+    @Test
+    public void testAllowsContentSwitch_secondaryDisplayPublicAndNotShouldShowOwnContent()
+            throws Exception {
+        // Set up a first display and a secondary display
+        setUpDisplay(new FakeDisplay(PORT_A));
+        setUpDisplay(new FakeDisplay(PORT_B));
+        updateAvailableDisplays();
+
+        // Set the secondary display to be a public display
+        doReturn(new int[0]).when(mMockedResources)
+                .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
+        // Disable FLAG_OWN_CONTENT_ONLY for the secondary display
+        doReturn(true).when(mMockedResources)
+                .getBoolean(com.android.internal.R.bool.config_localDisplaysMirrorContent);
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // This secondary display should be allowed to use the content mode switch
+        DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+        assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+                & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+    }
+
+    @Test
+    public void testAllowsContentSwitch_privateDisplay() throws Exception {
+        // Set up a first display and a secondary display
+        setUpDisplay(new FakeDisplay(PORT_A));
+        setUpDisplay(new FakeDisplay(PORT_B));
+        updateAvailableDisplays();
+
+        // Set the secondary display to be a private display
+        doReturn(new int[]{ PORT_B }).when(mMockedResources)
+                .getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // The private display should not be allowed to use the content mode switch
+        DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+        assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+                & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0);
+    }
+
+    @Test
+    public void testAllowsContentSwitch_ownContentOnlyDisplay() throws Exception {
+        // Set up a first display and a secondary display
+        setUpDisplay(new FakeDisplay(PORT_A));
+        setUpDisplay(new FakeDisplay(PORT_B));
+        updateAvailableDisplays();
+
+        // Enable FLAG_OWN_CONTENT_ONLY for the secondary display
+        doReturn(false).when(mMockedResources)
+                .getBoolean(com.android.internal.R.bool.config_localDisplaysMirrorContent);
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // The secondary display with FLAG_OWN_CONTENT_ONLY enabled should not be allowed to use the
+        // content mode switch
+        DisplayDevice secondaryDisplayDevice = mListener.addedDisplays.get(1);
+        assertTrue((secondaryDisplayDevice.getDisplayDeviceInfoLocked().flags
+                & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) == 0);
+    }
+
+    @Test
+    public void testAllowsContentSwitch_flagShouldShowSystemDecorations() throws Exception {
+        // Set up a display
+        FakeDisplay display = new FakeDisplay(PORT_A);
+        setUpDisplay(display);
+        updateAvailableDisplays();
+
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        // Display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled should not be allowed to use the
+        // content mode switch
+        DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+        int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
+        boolean allowsContentModeSwitch =
+                ((flags & DisplayDeviceInfo.FLAG_ALLOWS_CONTENT_MODE_SWITCH) != 0);
+        boolean shouldShowSystemDecorations =
+                ((flags & DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0);
+        assertFalse(allowsContentModeSwitch && shouldShowSystemDecorations);
+    }
+
     private void initDisplayOffloadSession() {
         when(mDisplayOffloader.startOffload()).thenReturn(true);
         when(mDisplayOffloader.allowAutoBrightnessInDoze()).thenReturn(true);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 0bef3b89..10bea7d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -416,7 +416,7 @@
         final String uniqueId = "uniqueId";
         final IBinder displayToken = new Binder();
         when(mMockSufaceControlDisplayFactory.createDisplay(
-                any(), anyBoolean(), eq(uniqueId), anyFloat()))
+                any(), anyBoolean(), anyBoolean(), eq(uniqueId), anyFloat()))
                 .thenReturn(displayToken);
 
         // The display needs to be public, otherwise it will be considered never blank.
@@ -456,6 +456,49 @@
         verify(mMockCallback).onPaused();
     }
 
+    @EnableFlags(
+            android.companion.virtualdevice.flags.Flags.FLAG_CORRECT_VIRTUAL_DISPLAY_POWER_STATE)
+    @Test
+    public void createVirtualDisplayLocked_neverBlank_optimizesForPower() {
+        final String uniqueId = "uniqueId";
+        final IBinder displayToken = new Binder();
+        final String name = "name";
+        when(mVirtualDisplayConfigMock.getName()).thenReturn(name);
+        when(mMockSufaceControlDisplayFactory.createDisplay(
+                any(), anyBoolean(), anyBoolean(), eq(uniqueId), anyFloat()))
+                .thenReturn(displayToken);
+
+        // Use a private display to cause the display to be never blank.
+        mAdapter.createVirtualDisplayLocked(mMockCallback,
+                /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
+                uniqueId, /* surface= */ mSurfaceMock, 0, mVirtualDisplayConfigMock);
+
+        verify(mMockSufaceControlDisplayFactory).createDisplay(eq(name), eq(false), eq(true),
+                eq(uniqueId), anyFloat());
+    }
+
+    @EnableFlags(
+            android.companion.virtualdevice.flags.Flags.FLAG_CORRECT_VIRTUAL_DISPLAY_POWER_STATE)
+    @Test
+    public void createVirtualDisplayLocked_blankable_optimizesForPerformance() {
+        final String uniqueId = "uniqueId";
+        final IBinder displayToken = new Binder();
+        final String name = "name";
+        when(mVirtualDisplayConfigMock.getName()).thenReturn(name);
+        when(mMockSufaceControlDisplayFactory.createDisplay(
+                any(), anyBoolean(), anyBoolean(), eq(uniqueId), anyFloat()))
+                .thenReturn(displayToken);
+
+        // Use a public display to cause the display to be blankable
+        mAdapter.createVirtualDisplayLocked(mMockCallback,
+                /* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
+                uniqueId, /* surface= */ mSurfaceMock, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
+                mVirtualDisplayConfigMock);
+
+        verify(mMockSufaceControlDisplayFactory).createDisplay(eq(name), eq(false), eq(false),
+                eq(uniqueId), anyFloat());
+    }
+
     private IVirtualDisplayCallback createCallback() {
         return new IVirtualDisplayCallback.Stub() {
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
index 019b70e..f067fa1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.hardware.display.DisplayManagerInternal;
 
@@ -210,7 +210,7 @@
 
         // Validate one suspend blocker was released
         assertFalse(mWakelockController.isProximityPositiveAcquired());
-        verifyZeroInteractions(mDisplayPowerCallbacks);
+        verifyNoMoreInteractions(mDisplayPowerCallbacks);
     }
 
     @Test
@@ -238,7 +238,7 @@
 
         // Validate one suspend blocker was released
         assertFalse(mWakelockController.isProximityNegativeAcquired());
-        verifyZeroInteractions(mDisplayPowerCallbacks);
+        verifyNoMoreInteractions(mDisplayPowerCallbacks);
     }
 
     @Test
@@ -265,7 +265,7 @@
 
         // Validate one suspend blocker was released
         assertFalse(mWakelockController.isOnStateChangedPending());
-        verifyZeroInteractions(mDisplayPowerCallbacks);
+        verifyNoMoreInteractions(mDisplayPowerCallbacks);
     }
 
     @Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index 49de801..bf05439 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -28,7 +28,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -422,7 +421,7 @@
         mDisplayBrightnessController.setAutomaticBrightnessController(
                 automaticBrightnessController);
         assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
-        verifyZeroInteractions(automaticBrightnessController);
+        verifyNoMoreInteractions(automaticBrightnessController);
         verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
         verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
     }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index f9fbf1b..b41f03b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -17,9 +17,9 @@
 package com.android.server.display.whitebalance;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java b/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
index 7c239ef..586ff52 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
@@ -328,6 +328,7 @@
                 case DREAM_STATE_STARTED -> startDream();
                 case DREAM_STATE_WOKEN -> wakeDream();
             }
+            mTestableLooper.processAllMessages();
         } while (mCurrentDreamState < state);
 
         return true;
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index eda5e86..77d6701 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -67,6 +67,9 @@
 
 /**
  * Test RescueParty.
+ * TODO: b/354112511 delete this file
+ * Moved to frameworks/base/tests/PackageWatchdog/src/com/android/server/RescuePartyTest
+ *
  */
 public class RescuePartyTest {
     private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 2a513ae..f0e61ec 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -109,7 +109,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.Manifest;
 import android.app.ActivityManager;
@@ -953,11 +953,13 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND)
-    public void testWakelockOrdering() throws Exception {
+    public void testWakelockOrderingFirstAlarm() throws Exception {
         final long triggerTime = mNowElapsedTest + 5000;
         final PendingIntent alarmPi = getNewMockPendingIntent();
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
 
+        // Pretend that it is the first alarm in this batch, or no other alarms are still processing
+        mService.mBroadcastRefCount = 0;
         mNowElapsedTest = mTestTimer.getElapsed();
         mTestTimer.expire();
 
@@ -975,20 +977,51 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND)
-    public void testWakelockReleasedWhenSendFails() throws Exception {
+    public void testWakelockOrderingNonFirst() throws Exception {
         final long triggerTime = mNowElapsedTest + 5000;
         final PendingIntent alarmPi = getNewMockPendingIntent();
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
 
+        // Pretend that some previous alarms are still processing.
+        mService.mBroadcastRefCount = 3;
+        mNowElapsedTest = mTestTimer.getElapsed();
+        mTestTimer.expire();
+
+        final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
+                ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
+        verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), onFinishedCaptor.capture(),
+                any(Handler.class), isNull(), any());
+        onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null);
+
+        verify(mWakeLock, never()).acquire();
+        verify(mWakeLock, never()).release();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND)
+    public void testWakelockReleasedWhenSendFails() throws Exception {
+        final PendingIntent alarmPi = getNewMockPendingIntent();
         doThrow(new PendingIntent.CanceledException("test")).when(alarmPi).send(eq(mMockContext),
                 eq(0), any(Intent.class), any(), any(Handler.class), isNull(), any());
 
+        setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5000, alarmPi);
+
+        // Pretend that it is the first alarm in this batch, or no other alarms are still processing
+        mService.mBroadcastRefCount = 0;
         mNowElapsedTest = mTestTimer.getElapsed();
         mTestTimer.expire();
 
         final InOrder inOrder = Mockito.inOrder(mWakeLock);
         inOrder.verify(mWakeLock).acquire();
         inOrder.verify(mWakeLock).release();
+
+        setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5000, alarmPi);
+
+        // Pretend that some previous alarms are still processing.
+        mService.mBroadcastRefCount = 4;
+        mNowElapsedTest = mTestTimer.getElapsed();
+        mTestTimer.expire();
+        inOrder.verifyNoMoreInteractions();
     }
 
     @Test
@@ -3691,8 +3724,8 @@
         setDeviceConfigInt(KEY_TEMPORARY_QUOTA_BUMP, 0);
 
         mAppStandbyListener.triggerTemporaryQuotaBump(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
-        verifyZeroInteractions(mPackageManagerInternal);
-        verifyZeroInteractions(mService.mHandler);
+        verifyNoMoreInteractions(mPackageManagerInternal);
+        verifyNoMoreInteractions(mService.mHandler);
     }
 
     private void testTemporaryQuota_bumpedAfterDeferral(int standbyBucket) throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index 7dab1c8..859d2d2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -30,7 +30,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -244,7 +244,7 @@
         addAlarmsToStore(simpleAlarm, alarmClock);
 
         mAlarmStore.remove(simpleAlarm::equals);
-        verifyZeroInteractions(onRemoved);
+        verifyNoMoreInteractions(onRemoved);
 
         mAlarmStore.remove(alarmClock::equals);
         verify(onRemoved).run();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 35ab2d2..acc06d0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -74,7 +74,6 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.Manifest;
 import android.app.ActivityManager;
@@ -584,7 +583,7 @@
             if (app.uid == uidRec.getUid() && expectedBlockState == NETWORK_STATE_BLOCK) {
                 verify(app.getThread()).setNetworkBlockSeq(uidRec.curProcStateSeq);
             } else {
-                verifyZeroInteractions(app.getThread());
+                verifyNoMoreInteractions(app.getThread());
             }
             Mockito.reset(app.getThread());
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 194bf4b..84110ae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -34,9 +34,9 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index e678acc..987b9c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -21,6 +21,7 @@
 import static com.android.server.am.ActivityManagerService.Injector;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -625,6 +626,60 @@
         assertTrue(startInfo.equals(startInfoFromParcel));
     }
 
+    /** Test that new timestamps are added to the correct record (the most recently created one). */
+    @Test
+    public void testTimestampAddedToCorrectRecord() throws Exception {
+        // Use a different start timestamp for each record so we can identify which was added to.
+        final long startTimeRecord1 = 123L;
+        final long startTimeRecord2 = 456L;
+
+        final long forkTime = 789L;
+
+        // Create a process record to use with all starts.
+        ProcessRecord app = makeProcessRecord(
+                APP_1_PID_1,                     // pid
+                APP_1_UID,                       // uid
+                APP_1_UID,                       // packageUid
+                null,                            // definingUid
+                APP_1_PROCESS_NAME,              // processName
+                APP_1_PACKAGE_NAME);             // packageName
+
+        // Trigger a start info record.
+        mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord1, app,
+                buildIntent(COMPONENT), false /* isAlarm */);
+
+        // Wait at least 1 ms for monotonic time to increase.
+        sleep(1);
+
+        // Verify the record was added successfully.
+        ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
+        mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+        assertEquals(1, list.size());
+        assertEquals(startTimeRecord1, list.get(0).getStartupTimestamps().get(0).longValue());
+
+        // Now trigger another start info record.
+        mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord2, app,
+                buildIntent(COMPONENT), false /* isAlarm */);
+
+        // Add a timestamp to the most recent record.
+        mAppStartInfoTracker.addTimestampToStart(
+                app, forkTime, ApplicationStartInfo.START_TIMESTAMP_FORK);
+
+        // Verify the record was added successfully.
+        list.clear();
+        mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+        assertEquals(2, list.size());
+        assertEquals(startTimeRecord2, list.get(0).getStartupTimestamps().get(0).longValue());
+        assertEquals(startTimeRecord1, list.get(1).getStartupTimestamps().get(0).longValue());
+
+        // Verify that the new timestamp is set correctly on the 2nd record that was added and not
+        // on the first.
+        assertEquals(forkTime, list.get(0).getStartupTimestamps()
+                .get(ApplicationStartInfo.START_TIMESTAMP_FORK).longValue());
+        assertFalse(list.get(1).getStartupTimestamps().containsKey(
+                ApplicationStartInfo.START_TIMESTAMP_FORK));
+    }
+
     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
         try {
             Field field = clazz.getDeclaredField(fieldName);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index 637c73f..dd5924a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -88,7 +88,7 @@
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyLong;
-import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.clearInvocations;
@@ -747,7 +747,7 @@
             mCurrentTimeMillis = 10_000L;
             doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
             doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
-            doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(anyObject());
+            doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(any());
             mAppFGSTracker.onForegroundServiceStateChanged(testPkgName, testUid,
                     testPid, true);
             mAppFGSTracker.onForegroundServiceNotificationUpdated(
@@ -1925,7 +1925,7 @@
             mCurrentTimeMillis = 10_000L;
             doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
             doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
-            doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(anyObject());
+            doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(any());
 
             // Run with a media playback service which starts/stops immediately, we should
             // goto the restricted bucket.
@@ -3170,7 +3170,7 @@
                         inv.getArgument(2));
                 return null;
             }).when(telephonyManager).registerCarrierPrivilegesCallback(
-                    anyInt(), anyObject(), anyObject());
+                    anyInt(), any(), any());
         }
 
         public void registerCarrierPrivilegesCallback(int phoneId, Executor executor,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 3a9c99d..d540b2e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -155,7 +155,6 @@
         doAnswer((invocation) -> {
             Log.v(TAG, "Intercepting startProcessLocked() for "
                     + Arrays.toString(invocation.getArguments()));
-            assertHealth();
             final String processName = invocation.getArgument(0);
             final ProcessStartBehavior behavior = mNewProcessStartBehaviors.getOrDefault(
                     processName, mNextProcessStartBehavior.getAndSet(ProcessStartBehavior.SUCCESS));
@@ -206,6 +205,9 @@
                             mActiveProcesses.remove(res);
                             res.setKilled(true);
                             break;
+                        case MISSING_RESPONSE:
+                            res.setPendingStart(true);
+                            break;
                         default:
                             throw new UnsupportedOperationException();
                     }
@@ -244,6 +246,7 @@
         mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
         mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500;
         mConstants.MAX_FROZEN_OUTGOING_BROADCASTS = 10;
+        mConstants.PENDING_COLD_START_ABANDON_TIMEOUT_MILLIS = 2000;
     }
 
     @After
@@ -279,6 +282,8 @@
         FAIL_NULL,
         /** Process is killed without reporting to BroadcastQueue */
         KILLED_WITHOUT_NOTIFY,
+        /** Process start fails without no response */
+        MISSING_RESPONSE,
     }
 
     private enum ProcessBehavior {
@@ -1173,6 +1178,37 @@
         verifyScheduleReceiver(times(1), receiverOrangeApp, timezone);
     }
 
+    @Test
+    public void testProcessStartWithMissingResponse() throws Exception {
+        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+        final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+
+        mNewProcessStartBehaviors.put(PACKAGE_GREEN, ProcessStartBehavior.MISSING_RESPONSE);
+
+        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of(
+                withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10),
+                withPriority(makeRegisteredReceiver(receiverBlueApp), 5),
+                withPriority(makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW), 0))));
+
+        final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_ORANGE, CLASS_ORANGE))));
+
+        waitForIdle();
+        final ProcessRecord receiverGreenApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+                getUidForPackage(PACKAGE_GREEN));
+        final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+                getUidForPackage(PACKAGE_YELLOW));
+        final ProcessRecord receiverOrangeApp = mAms.getProcessRecordLocked(PACKAGE_ORANGE,
+                getUidForPackage(PACKAGE_ORANGE));
+
+        verifyScheduleReceiver(times(1), receiverGreenApp, airplane);
+        verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
+        verifyScheduleReceiver(times(1), receiverYellowApp, airplane);
+        verifyScheduleReceiver(times(1), receiverOrangeApp, timezone);
+    }
+
     /**
      * Verify that a broadcast sent to a frozen app, which gets killed as part of unfreezing
      * process due to pending sync binder transactions, is delivered as expected.
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 5d8f578..773114e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -74,6 +74,10 @@
 import static com.android.server.am.ProcessList.SERVICE_B_ADJ;
 import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
 import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -544,7 +548,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).hasActivities();
-        doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE)
+        doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE)
                 .when(wpc).getActivityStateFlags();
         setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
@@ -553,28 +557,58 @@
         assertFalse(app.mState.isCached());
         assertFalse(app.mState.isEmpty());
         assertEquals("vis-activity", app.mState.getAdjType());
+        assertCpuTime(app);
 
-        doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+        doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE
                 | WindowProcessController.ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN)
                 .when(wpc).getActivityStateFlags();
         updateOomAdj(app);
         assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
         assertEquals("resumed-split-screen-activity", app.mState.getAdjType());
+        assertCpuTime(app);
 
-        doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+        doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE
                 | WindowProcessController.ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM)
                 .when(wpc).getActivityStateFlags();
         updateOomAdj(app);
         assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
         assertEquals("perceptible-freeform-activity", app.mState.getAdjType());
+        assertCpuTime(app);
 
-        doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+        doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE
                 | WindowProcessController.ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE)
                 .when(wpc).getActivityStateFlags();
         updateOomAdj(app);
         assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ,
                 SCHED_GROUP_FOREGROUND_WINDOW);
         assertEquals("vis-multi-window-activity", app.mState.getAdjType());
+        assertCpuTime(app);
+    }
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoOne_PausingStoppingActivities() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+        WindowProcessController wpc = app.getWindowProcessController();
+        doReturn(true).when(wpc).hasActivities();
+        doReturn(ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED).when(wpc).getActivityStateFlags();
+        setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        updateOomAdj(app);
+        assertProcStates(app, PROCESS_STATE_TOP, PERCEPTIBLE_APP_ADJ, SCHED_GROUP_DEFAULT,
+                "pause-activity");
+        assertCpuTime(app);
+
+        doReturn(ACTIVITY_STATE_FLAG_IS_STOPPING).when(wpc).getActivityStateFlags();
+        updateOomAdj(app);
+        assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PERCEPTIBLE_APP_ADJ,
+                SCHED_GROUP_BACKGROUND, "stop-activity");
+        assertCpuTime(app);
+
+        doReturn(ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING).when(wpc).getActivityStateFlags();
+        updateOomAdj(app);
+        assertProcStates(app, PROCESS_STATE_CACHED_ACTIVITY, CACHED_APP_MIN_ADJ,
+                SCHED_GROUP_BACKGROUND, "cch-act");
+        assertNoCpuTime(app);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -746,6 +780,37 @@
     @SuppressWarnings("GuardedBy")
     @Test
     @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
+    public void testUpdateOomAdjFreezeState_bindingWithAllowFreeze() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+        WindowProcessController wpc = app.getWindowProcessController();
+        doReturn(true).when(wpc).hasActivities();
+        doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE).when(wpc).getActivityStateFlags();
+
+        final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+
+        // App with a visible activity binds to app2 without any special flag.
+        bindService(app2, app, null, null, 0, mock(IBinder.class));
+
+        final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+                MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+
+        // App with a visible activity binds to app3 with ALLOW_FREEZE.
+        bindService(app3, app, null, null, Context.BIND_ALLOW_FREEZE, mock(IBinder.class));
+
+        setProcessesToLru(app, app2, app3);
+
+        updateOomAdj(app);
+
+        assertCpuTime(app);
+        assertCpuTime(app2);
+        assertNoCpuTime(app3);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
+    @EnableFlags(Flags.FLAG_USE_CPU_TIME_CAPABILITY)
     @DisableFlags(Flags.FLAG_PROTOTYPE_AGGRESSIVE_FREEZING)
     public void testUpdateOomAdjFreezeState_bindingFromFgs() {
         final ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
@@ -3453,12 +3518,14 @@
         // EXPECT: stale-perceptible-act adjustment
         doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
                 .when(wpc).getActivityStateFlags();
+        assertNoCpuTime(app);
 
         doReturn(now - OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS).when(
                 wpc).getPerceptibleTaskStoppedTimeMillis();
         updateOomAdj(app);
         assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
                 SCHED_GROUP_BACKGROUND, "stale-perceptible-act");
+        assertNoCpuTime(app);
 
         // GIVEN: perceptible adjustment is is disabled
         // EXPECT: no perceptible adjustment
@@ -3468,15 +3535,17 @@
         updateOomAdj(app);
         assertProcStates(app, PROCESS_STATE_CACHED_ACTIVITY, CACHED_APP_MIN_ADJ,
                 SCHED_GROUP_BACKGROUND, "cch-act");
+        assertNoCpuTime(app);
 
         // GIVEN: perceptible app is in foreground
         // EXPECT: no perceptible adjustment
-        doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE)
+        doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE)
                 .when(wpc).getActivityStateFlags();
         doReturn(now).when(wpc).getPerceptibleTaskStoppedTimeMillis();
         updateOomAdj(app);
         assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ,
                 SCHED_GROUP_DEFAULT, "vis-activity");
+        assertCpuTime(app);
     }
 
     @SuppressWarnings("GuardedBy")
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
index 8aaa723..33bd95e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
@@ -51,6 +51,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupRestoreTask.CancellationReason;
 import com.android.server.backup.internal.LifecycleOperationStorage;
 
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
@@ -368,9 +369,12 @@
         mConnectionManager.agentDisconnected(TEST_PACKAGE);
 
         mTestThread.join();
-        verify(mUserBackupManagerService).handleCancel(eq(123), eq(true));
-        verify(mUserBackupManagerService).handleCancel(eq(456), eq(true));
-        verify(mUserBackupManagerService).handleCancel(eq(789), eq(true));
+        verify(mUserBackupManagerService)
+                .handleCancel(eq(123), eq(CancellationReason.AGENT_DISCONNECTED));
+        verify(mUserBackupManagerService)
+                .handleCancel(eq(456), eq(CancellationReason.AGENT_DISCONNECTED));
+        verify(mUserBackupManagerService)
+                .handleCancel(eq(789), eq(CancellationReason.AGENT_DISCONNECTED));
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
index ae0452a..b7087c7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/FullBackupUtilsTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.Presubmit;
@@ -105,7 +105,7 @@
         } catch (EOFException expected) {
         }
 
-        verifyZeroInteractions(mOutputStreamMock);
+        verifyNoMoreInteractions(mOutputStreamMock);
         assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
     }
 
@@ -126,7 +126,7 @@
         } catch (EOFException expected) {
         }
 
-        verifyZeroInteractions(mOutputStreamMock);
+        verifyNoMoreInteractions(mOutputStreamMock);
         assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
     }
 
@@ -141,7 +141,7 @@
 
         FullBackupUtils.routeSocketDataToOutput(mTemporaryFileDescriptor, mOutputStreamMock);
 
-        verifyZeroInteractions(mOutputStreamMock);
+        verifyNoMoreInteractions(mOutputStreamMock);
         assertThat(mTemporaryFileDescriptor.getFileDescriptor().valid()).isTrue();
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index bf7e3a0..346d5f7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -32,7 +32,6 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.internal.verification.VerificationModeFactory.times;
 
 import android.app.backup.IBackupManagerMonitor;
@@ -239,7 +238,7 @@
                 mMockPackageManagerInternal, mUserId, mContext);
 
         assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
-        verifyZeroInteractions(mBackupManagerMonitorMock);
+        verifyNoMoreInteractions(mBackupManagerMonitorMock);
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 3e87943..2d84887 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1029,6 +1029,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
     public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1127,6 +1128,54 @@
         }
     }
 
+    @Test
+    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+            Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+    public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes_Tuning() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+
+        ExecutionStats expectedStats = new ExecutionStats();
+
+        // Exempted
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+        expectedStats.expirationTimeElapsed = now + 34 * MINUTE_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 5;
+        expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 1;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            EXEMPTED_INDEX));
+        }
+
+        // Active
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+        // There is only one session in the past active bucket window, the empty time for this
+        // window is the bucket window size - duration of the session.
+        expectedStats.expirationTimeElapsed = now + 54 * MINUTE_IN_MILLIS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            ACTIVE_INDEX));
+        }
+    }
+
     /**
      * Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
      */
@@ -1195,6 +1244,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
     public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes() {
         // Set time to 3 minutes after boot.
         advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
@@ -1206,10 +1256,10 @@
         ExecutionStats expectedStats = new ExecutionStats();
 
         // Exempted
-        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
         expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
-        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
-        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
         expectedStats.expirationTimeElapsed = 10 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
         expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 2;
@@ -1268,6 +1318,49 @@
         }
     }
 
+    @Test
+    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+            Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+    public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes_Tunning() {
+        // Set time to 3 minutes after boot.
+        advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
+        advanceElapsedClock(3 * MINUTE_IN_MILLIS);
+
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false);
+
+        ExecutionStats expectedStats = new ExecutionStats();
+
+        // Exempted
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+        expectedStats.expirationTimeElapsed = 30 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+        expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInWindow = 2;
+        expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS;
+        expectedStats.bgJobCountInMaxPeriod = 2;
+        expectedStats.sessionCountInWindow = 1;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            EXEMPTED_INDEX));
+        }
+
+        // Active
+        expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+        expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+        expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+        expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+        expectedStats.expirationTimeElapsed = 50 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+        synchronized (mQuotaController.mLock) {
+            assertEquals(expectedStats,
+                    mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+                            ACTIVE_INDEX));
+        }
+    }
+
     /**
      * Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
      */
@@ -1425,6 +1518,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
     public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes() {
         for (int i = 0; i < 20; ++i) {
             mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1581,6 +1675,165 @@
         }
     }
 
+    @Test
+    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+            Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+    public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes_Tuning() {
+        for (int i = 0; i < 20; ++i) {
+            mQuotaController.saveTimingSession(0, "com.android.test",
+                    createTimingSession(
+                            JobSchedulerService.sElapsedRealtimeClock.millis(),
+                            5 * MINUTE_IN_MILLIS, 5), false);
+            advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+            advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+            for (int j = 0; j < 5; ++j) {
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(),
+                                MINUTE_IN_MILLIS, 2), false);
+                advanceElapsedClock(MINUTE_IN_MILLIS);
+                advanceElapsedClock(54 * SECOND_IN_MILLIS);
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false);
+                advanceElapsedClock(500);
+                advanceElapsedClock(400);
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false);
+                advanceElapsedClock(100);
+                advanceElapsedClock(5 * SECOND_IN_MILLIS);
+            }
+            advanceElapsedClock(40 * MINUTE_IN_MILLIS);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(16, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(64, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(192, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(320, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(11, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_WORKING_MS * 5 TimingSessions are coalesced
+            assertEquals(44, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_FREQUENT_MS * 5 TimingSessions are coalesced
+            assertEquals(132, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_RARE_MS * 5 TimingSessions are coalesced
+            assertEquals(220, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(11, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(44, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(132, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(220, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                5 * SECOND_IN_MILLIS);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(7, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_WORKING_MS * 9 TimingSessions are coalesced
+            assertEquals(28, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_FREQUENT_MS * 9 TimingSessions are coalesced
+            assertEquals(84, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            // WINDOW_SIZE_RARE_MS * 9 TimingSessions are coalesced
+            assertEquals(140, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                MINUTE_IN_MILLIS);
+
+        // Only two TimingSessions there for every hour.
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(2, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(8, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(24, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(40, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                5 * MINUTE_IN_MILLIS);
+
+        // Only one TimingSessions there for every hour
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(1, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(4, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(12, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(20, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                15 * MINUTE_IN_MILLIS);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(1, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(4, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(12, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(20, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+
+        // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
+        // between an hour and 15 minutes.
+        setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS);
+
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.invalidateAllExecutionStatsLocked();
+            assertEquals(1, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+            assertEquals(4, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+            assertEquals(12, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+            assertEquals(20, mQuotaController.getExecutionStatsLocked(
+                    0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+        }
+    }
+
     /**
      * Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
      */
@@ -2231,32 +2484,6 @@
         }
     }
 
-    @Test
-    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
-    public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
-        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false);
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
-                false);
-
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
-                20 * MINUTE_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
-        // window size = allowed time, so jobs can essentially run non-stop until they reach the
-        // max execution time.
-        setStandbyBucket(EXEMPTED_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(10 * MINUTE_IN_MILLIS,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-    }
-
     /**
      * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
      * window.
@@ -2327,6 +2554,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+    @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
     public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         // Close to RARE boundary.
@@ -2390,7 +2618,30 @@
 
     @Test
     @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+            Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+    public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes_Tuning() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        // Close to ACTIVE boundary.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS -  MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5), false);
+
+        // ACTIVE window != allowed time.
+        setStandbyBucket(ACTIVE_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(17 * MINUTE_IN_MILLIS,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(20 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
             Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER})
+    @DisableFlags(Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS)
     public void testGetTimeUntilQuotaConsumedLocked_Installer() {
         PackageInfo pi = new PackageInfo();
         pi.packageName = SOURCE_PACKAGE;
@@ -2412,7 +2663,7 @@
         // Far away from FREQUENT boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
-                        now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS -  HOUR_IN_MILLIS),
+                        now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
                         2 * MINUTE_IN_MILLIS, 5), false);
         // Overlap WORKING_SET boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -2422,12 +2673,12 @@
         // Close to ACTIVE boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
-                        now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS -  MINUTE_IN_MILLIS),
+                        now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
                         2 * MINUTE_IN_MILLIS, 5), false);
         // Close to EXEMPTED boundary.
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(
-                        now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS -  MINUTE_IN_MILLIS),
+                        now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
                         2 * MINUTE_IN_MILLIS, 5), false);
 
         // No additional quota for the system installer when the app is in RARE, FREQUENT,
@@ -2486,6 +2737,42 @@
         }
     }
 
+    @Test
+    @EnableFlags({Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS,
+            Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER,
+            Flags.FLAG_TUNE_QUOTA_WINDOW_DEFAULT_PARAMETERS})
+    public void testGetTimeUntilQuotaConsumedLocked_Installer_Tuning() {
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = SOURCE_PACKAGE;
+        pi.requestedPermissions = new String[]{Manifest.permission.INSTALL_PACKAGES};
+        pi.requestedPermissionsFlags = new int[]{PackageInfo.REQUESTED_PERMISSION_GRANTED};
+        pi.applicationInfo = new ApplicationInfo();
+        pi.applicationInfo.uid = mSourceUid;
+        doReturn(List.of(pi)).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt());
+        doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission(
+                eq(Manifest.permission.INSTALL_PACKAGES), anyInt(), eq(mSourceUid));
+        mQuotaController.onSystemServicesReady();
+
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        // Close to EXEMPTED boundary.
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(
+                        now - (mQcConstants.WINDOW_SIZE_EXEMPTED_MS - MINUTE_IN_MILLIS),
+                        2 * MINUTE_IN_MILLIS, 5), false);
+
+        // Additional quota for the system installer when the app is in EXEMPTED bucket.
+        // EXEMPTED window == allowed time.
+        setStandbyBucket(EXEMPTED_INDEX);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(38 * MINUTE_IN_MILLIS,
+                    mQuotaController.getRemainingExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 2 * MINUTE_IN_MILLIS,
+                    mQuotaController.getTimeUntilQuotaConsumedLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+    }
+
     /**
      * Test getTimeUntilQuotaConsumedLocked when the app is close to the max execution limit.
      */
@@ -2637,33 +2924,6 @@
         }
     }
 
-    @Test
-    @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
-    public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
-        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (24 * HOUR_IN_MILLIS),
-                        mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS, 5),
-                false);
-        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
-                createTimingSession(now - (20 * MINUTE_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5),
-                false);
-
-        setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
-                20 * MINUTE_IN_MILLIS);
-        setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
-        // window size != allowed time.
-        setStandbyBucket(EXEMPTED_INDEX);
-        synchronized (mQuotaController.mLock) {
-            assertEquals(0,
-                    mQuotaController.getRemainingExecutionTimeLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-            assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS,
-                    mQuotaController.getTimeUntilQuotaConsumedLocked(
-                            SOURCE_USER_ID, SOURCE_PACKAGE));
-        }
-    }
-
     /**
      * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
      * window and the session is rolling out of the window.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index 3d0c637..29af7d2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -27,7 +27,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.testng.AssertJUnit.assertEquals;
 
@@ -128,7 +128,7 @@
                 AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
         clearInvocations(mNotificationManager);
         mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
-        verifyZeroInteractions(mNotificationManager);
+        verifyNoMoreInteractions(mNotificationManager);
     }
 
     @Test
@@ -143,7 +143,7 @@
                 Build.VERSION.SDK_INT);
         clearInvocations(mNotificationManager);
         mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
-        verifyZeroInteractions(mNotificationManager);
+        verifyNoMoreInteractions(mNotificationManager);
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
index 8ce05e2..c9f86b0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
@@ -23,7 +23,6 @@
 import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
 import static android.view.Display.DEFAULT_DISPLAY_GROUP;
 
-import static com.android.server.power.ScreenUndimDetector.DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS;
 import static com.android.server.power.ScreenUndimDetector.KEY_KEEP_SCREEN_ON_ENABLED;
 import static com.android.server.power.ScreenUndimDetector.KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS;
 import static com.android.server.power.ScreenUndimDetector.KEY_UNDIMS_REQUIRED;
@@ -49,6 +48,7 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Tests for {@link com.android.server.power.ScreenUndimDetector}
@@ -61,7 +61,8 @@
                     POLICY_DIM,
                     POLICY_BRIGHT);
     private static final int OTHER_DISPLAY_GROUP = DEFAULT_DISPLAY_GROUP + 1;
-
+    private static final long DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS =
+            TimeUnit.MINUTES.toMillis(5);
     @ClassRule
     public static final TestableContext sContext = new TestableContext(
             InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@@ -88,7 +89,8 @@
     @Before
     public void setup() {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_KEEP_SCREEN_ON_ENABLED, Boolean.TRUE.toString(), false /*makeDefault*/);
         DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
                 KEY_UNDIMS_REQUIRED,
                 Integer.toString(1), false /*makeDefault*/);
@@ -108,10 +110,10 @@
 
     @Test
     public void recordScreenPolicy_disabledByFlag_noop() {
+        setup();
         DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
                 KEY_KEEP_SCREEN_ON_ENABLED, Boolean.FALSE.toString(), false /*makeDefault*/);
         mScreenUndimDetector.readValuesFromDeviceConfig();
-
         mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM);
         mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT);
 
diff --git a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
rename to services/tests/mockingservicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
index dc04b6a..bf3fe8c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
@@ -29,7 +29,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -176,9 +176,9 @@
         prepareCameraPrivacyLightController(List.of(getNextLight(true)), Collections.EMPTY_SET,
                 true, new int[0], mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis);
 
-        verifyZeroInteractions(mLightsManager);
-        verifyZeroInteractions(mAppOpsManager);
-        verifyZeroInteractions(mSensorManager);
+        verifyNoMoreInteractions(mLightsManager);
+        verifyNoMoreInteractions(mAppOpsManager);
+        verifyNoMoreInteractions(mSensorManager);
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java
index 52cd29c..1415dd6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java
@@ -22,10 +22,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.contains;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.matches;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.matches;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
index 49c37f1..4f74667 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
@@ -33,27 +33,29 @@
 
 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.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.mockito.MockitoAnnotations.initMocks;
 
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.internal.R;
 
 import org.junit.AfterClass;
 import org.junit.Before;
@@ -70,10 +72,11 @@
 import java.io.IOException;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Unit tests for the most important helpers of {@link WallpaperCropper}, in particular
- * {@link WallpaperCropper#getCrop(Point, Point, SparseArray, boolean)}.
+ * {@link WallpaperCropper#getCrop(Point, WallpaperDefaultDisplayInfo, Point, SparseArray, boolean)}.
  */
 @Presubmit
 @RunWith(AndroidJUnit4.class)
@@ -83,7 +86,12 @@
 
     @Mock
     private WallpaperDisplayHelper mWallpaperDisplayHelper;
-    private WallpaperCropper mWallpaperCropper;
+
+    @Mock
+    private WindowManager mWindowManager;
+
+    @Mock
+    private Resources mResources;
 
     private static final Point PORTRAIT_ONE = new Point(500, 800);
     private static final Point PORTRAIT_TWO = new Point(400, 1000);
@@ -159,7 +167,6 @@
             return getWallpaperTestDir(userId);
         }).when(() -> WallpaperUtils.getWallpaperDir(anyInt()));
 
-        mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
     }
 
     private File getWallpaperTestDir(int userId) {
@@ -175,14 +182,21 @@
         return tempDir;
     }
 
-    private void setUpWithDisplays(List<Point> displaySizes) {
+    private WallpaperDefaultDisplayInfo setUpWithDisplays(List<Point> displaySizes) {
         mDisplaySizes = new SparseArray<>();
         displaySizes.forEach(size -> {
             mDisplaySizes.put(getOrientation(size), size);
             Point rotated = new Point(size.y, size.x);
             mDisplaySizes.put(getOrientation(rotated), rotated);
         });
+        Set<WindowMetrics> windowMetrics = new ArraySet<>();
+        for (Point displaySize : displaySizes) {
+            windowMetrics.add(
+                    new WindowMetrics(new Rect(0, 0, displaySize.x, displaySize.y),
+                            new WindowInsets.Builder().build()));
+        }
         when(mWallpaperDisplayHelper.getDefaultDisplaySizes()).thenReturn(mDisplaySizes);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt())).thenReturn(windowMetrics);
         if (displaySizes.size() == 2) {
             Point largestDisplay = displaySizes.stream().max(
                     Comparator.comparingInt(p -> p.x * p.y)).get();
@@ -192,11 +206,16 @@
             mFolded = getOrientation(smallestDisplay);
             mUnfoldedRotated = getRotatedOrientation(mUnfolded);
             mFoldedRotated = getRotatedOrientation(mFolded);
+            // foldable
+            doReturn(new int[]{0}).when(mResources).getIntArray(R.array.config_foldedDeviceStates);
+        } else {
+            // no foldable
+            doReturn(new int[]{}).when(mResources).getIntArray(R.array.config_foldedDeviceStates);
         }
-        doAnswer(invocation -> getFoldedOrientation(invocation.getArgument(0)))
-                .when(mWallpaperDisplayHelper).getFoldedOrientation(anyInt());
-        doAnswer(invocation -> getUnfoldedOrientation(invocation.getArgument(0)))
-                .when(mWallpaperDisplayHelper).getUnfoldedOrientation(anyInt());
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+        when(mWallpaperDisplayHelper.getDefaultDisplayInfo()).thenReturn(defaultDisplayInfo);
+        return defaultDisplayInfo;
     }
 
     private int getFoldedOrientation(int orientation) {
@@ -435,7 +454,7 @@
      */
     @Test
     public void testGetCrop_noSuggestedCrops() {
-        setUpWithDisplays(STANDARD_DISPLAY);
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(STANDARD_DISPLAY);
         Point bitmapSize = new Point(800, 1000);
         Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
         SparseArray<Rect> suggestedCrops = new SparseArray<>();
@@ -455,8 +474,9 @@
             for (boolean rtl : List.of(false, true)) {
                 Rect expectedCrop = rtl ? rightOf(bitmapRect, expectedCropSize)
                         : leftOf(bitmapRect, expectedCropSize);
-                assertThat(mWallpaperCropper.getCrop(
-                        displaySize, bitmapSize, suggestedCrops, rtl))
+                assertThat(
+                        WallpaperCropper.getCrop(
+                                displaySize, defaultDisplayInfo, bitmapSize, suggestedCrops, rtl))
                         .isEqualTo(expectedCrop);
             }
         }
@@ -469,7 +489,7 @@
      */
     @Test
     public void testGetCrop_hasSuggestedCrop() {
-        setUpWithDisplays(STANDARD_DISPLAY);
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(STANDARD_DISPLAY);
         Point bitmapSize = new Point(800, 1000);
         SparseArray<Rect> suggestedCrops = new SparseArray<>();
         suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(0, 0, 400, 800));
@@ -479,11 +499,13 @@
         }
 
         for (boolean rtl : List.of(false, true)) {
-            assertThat(mWallpaperCropper.getCrop(
-                    new Point(300, 800), bitmapSize, suggestedCrops, rtl))
+            assertThat(
+                    WallpaperCropper.getCrop(new Point(300, 800), defaultDisplayInfo, bitmapSize,
+                            suggestedCrops, rtl))
                     .isEqualTo(suggestedCrops.get(ORIENTATION_PORTRAIT));
-            assertThat(mWallpaperCropper.getCrop(
-                    new Point(500, 800), bitmapSize, suggestedCrops, rtl))
+            assertThat(
+                    WallpaperCropper.getCrop(new Point(500, 800), defaultDisplayInfo, bitmapSize,
+                            suggestedCrops, rtl))
                     .isEqualTo(new Rect(0, 0, 500, 800));
         }
     }
@@ -499,7 +521,7 @@
      */
     @Test
     public void testGetCrop_hasRotatedSuggestedCrop() {
-        setUpWithDisplays(STANDARD_DISPLAY);
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(STANDARD_DISPLAY);
         Point bitmapSize = new Point(2000, 1800);
         Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
         SparseArray<Rect> suggestedCrops = new SparseArray<>();
@@ -510,12 +532,14 @@
         suggestedCrops.put(ORIENTATION_PORTRAIT, centerOf(bitmapRect, portrait));
         suggestedCrops.put(ORIENTATION_SQUARE_LANDSCAPE, centerOf(bitmapRect, squareLandscape));
         for (boolean rtl : List.of(false, true)) {
-            assertThat(mWallpaperCropper.getCrop(
-                    landscape, bitmapSize, suggestedCrops, rtl))
+            assertThat(
+                    WallpaperCropper.getCrop(landscape, defaultDisplayInfo, bitmapSize,
+                            suggestedCrops, rtl))
                     .isEqualTo(centerOf(bitmapRect, landscape));
 
-            assertThat(mWallpaperCropper.getCrop(
-                    squarePortrait, bitmapSize, suggestedCrops, rtl))
+            assertThat(
+                    WallpaperCropper.getCrop(squarePortrait, defaultDisplayInfo, bitmapSize,
+                            suggestedCrops, rtl))
                     .isEqualTo(centerOf(bitmapRect, squarePortrait));
         }
     }
@@ -532,7 +556,7 @@
     @Test
     public void testGetCrop_hasUnfoldedSuggestedCrop() {
         for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) {
-            setUpWithDisplays(displaySizes);
+            WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(displaySizes);
             Point bitmapSize = new Point(2000, 2400);
             Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
 
@@ -569,8 +593,9 @@
                         expectedCrop.right = Math.min(
                                 unfoldedCrop.right, unfoldedCrop.right + maxParallax);
                     }
-                    assertThat(mWallpaperCropper.getCrop(
-                            foldedDisplay, bitmapSize, suggestedCrops, rtl))
+                    assertThat(
+                            WallpaperCropper.getCrop(foldedDisplay, defaultDisplayInfo, bitmapSize,
+                                    suggestedCrops, rtl))
                             .isEqualTo(expectedCrop);
                 }
             }
@@ -588,7 +613,7 @@
     @Test
     public void testGetCrop_hasFoldedSuggestedCrop() {
         for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) {
-            setUpWithDisplays(displaySizes);
+            WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(displaySizes);
             Point bitmapSize = new Point(2000, 2000);
             Rect bitmapRect = new Rect(0, 0, 2000, 2000);
 
@@ -610,12 +635,14 @@
             Point unfoldedDisplayTwo = mDisplaySizes.get(unfoldedTwo);
 
             for (boolean rtl : List.of(false, true)) {
-                assertThat(centerOf(mWallpaperCropper.getCrop(
-                        unfoldedDisplayOne, bitmapSize, suggestedCrops, rtl), foldedDisplayOne))
+                assertThat(centerOf(
+                        WallpaperCropper.getCrop(unfoldedDisplayOne, defaultDisplayInfo, bitmapSize,
+                                suggestedCrops, rtl), foldedDisplayOne))
                         .isEqualTo(foldedCropOne);
 
-                assertThat(centerOf(mWallpaperCropper.getCrop(
-                        unfoldedDisplayTwo, bitmapSize, suggestedCrops, rtl), foldedDisplayTwo))
+                assertThat(centerOf(
+                        WallpaperCropper.getCrop(unfoldedDisplayTwo, defaultDisplayInfo, bitmapSize,
+                                suggestedCrops, rtl), foldedDisplayTwo))
                         .isEqualTo(foldedCropTwo);
             }
         }
@@ -633,7 +660,7 @@
     @Test
     public void testGetCrop_hasRotatedUnfoldedSuggestedCrop() {
         for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) {
-            setUpWithDisplays(displaySizes);
+            WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(displaySizes);
             Point bitmapSize = new Point(2000, 2000);
             Rect bitmapRect = new Rect(0, 0, 2000, 2000);
             Point largestDisplay = displaySizes.stream().max(
@@ -650,8 +677,9 @@
                 Point rotatedFoldedDisplay = mDisplaySizes.get(rotatedFolded);
 
                 for (boolean rtl : List.of(false, true)) {
-                    assertThat(mWallpaperCropper.getCrop(
-                            rotatedFoldedDisplay, bitmapSize, suggestedCrops, rtl))
+                    assertThat(
+                            WallpaperCropper.getCrop(rotatedFoldedDisplay, defaultDisplayInfo,
+                                    bitmapSize, suggestedCrops, rtl))
                             .isEqualTo(centerOf(rotatedUnfoldedCrop, rotatedFoldedDisplay));
                 }
             }
@@ -670,7 +698,7 @@
     @Test
     public void testGetCrop_hasRotatedFoldedSuggestedCrop() {
         for (List<Point> displaySizes : ALL_FOLDABLE_DISPLAYS) {
-            setUpWithDisplays(displaySizes);
+            WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(displaySizes);
             Point bitmapSize = new Point(2000, 2000);
             Rect bitmapRect = new Rect(0, 0, 2000, 2000);
 
@@ -689,8 +717,8 @@
                 Point rotatedUnfoldedDisplay = mDisplaySizes.get(rotatedUnfolded);
 
                 for (boolean rtl : List.of(false, true)) {
-                    Rect rotatedUnfoldedCrop = mWallpaperCropper.getCrop(
-                            rotatedUnfoldedDisplay, bitmapSize, suggestedCrops, rtl);
+                    Rect rotatedUnfoldedCrop = WallpaperCropper.getCrop(rotatedUnfoldedDisplay,
+                            defaultDisplayInfo, bitmapSize, suggestedCrops, rtl);
                     assertThat(centerOf(rotatedUnfoldedCrop, rotatedFoldedDisplay))
                             .isEqualTo(rotatedFoldedCrop);
                 }
@@ -705,13 +733,13 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 2560;
         displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(DEFAULT_DISPLAY));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
                 new Point(100, 100));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(DEFAULT_DISPLAY,
-                        wallpaperData)).isTrue();
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                DEFAULT_DISPLAY, wallpaperData)).isTrue();
     }
 
     // Test isWallpaperCompatibleForDisplay always return true for the stock wallpaper.
@@ -722,49 +750,67 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 2560;
         displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ true,
                 new Point(100, 100));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
-                        wallpaperData)).isTrue();
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isTrue();
     }
 
     // Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
     // aspect ratio meets the hard-coded aspect ratio.
     @Test
-    public void isWallpaperCompatibleForDisplay_wallpaperSizeSuitableForDisplayAndMeetAspectRatio_returnTrue()
+    public void isWallpaperCompatibleForDisplay_wallpaperSizeLargerThanDisplayAndMeetAspectRatio_returnTrue()
             throws Exception {
         final int displayId = 2;
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 2560;
         displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
                 new Point(4000, 3000));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
-                        wallpaperData)).isTrue();
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isTrue();
     }
 
-    // Test isWallpaperCompatibleForDisplay wallpaper is not suitable for the display and wallpaper
-    // aspect ratio meets the hard-coded aspect ratio.
+    // Test isWallpaperCompatibleForDisplay wallpaper is smaller than the display but larger than
+    // the threshold and wallpaper aspect ratio meets the hard-coded aspect ratio.
     @Test
-    public void isWallpaperCompatibleForDisplay_wallpaperSizeNotSuitableForDisplayAndMeetAspectRatio_returnFalse()
+    public void isWallpaperCompatibleForDisplay_wallpaperSizeSmallerThanDisplayButBeyondThresholdAndMeetAspectRatio_returnTrue()
             throws Exception {
         final int displayId = 2;
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 2560;
         displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
-                new Point(1000, 500));
+                new Point(2000, 900));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
-                        wallpaperData)).isFalse();
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isTrue();
+    }
+
+    // Test isWallpaperCompatibleForDisplay wallpaper is smaller than the display but larger than
+    // the threshold and wallpaper aspect ratio meets the hard-coded aspect ratio.
+    @Test
+    public void isWallpaperCompatibleForDisplay_wallpaperSizeSmallerThanDisplayButAboveThresholdAndMeetAspectRatio_returnFalse()
+            throws Exception {
+        final int displayId = 2;
+        DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = 2560;
+        displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
+        doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
+        WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
+                new Point(2000, 800));
+
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isFalse();
     }
 
     // Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
@@ -776,13 +822,13 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 2560;
         displayInfo.logicalHeight = 1044;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
                 new Point(2000, 4000));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
-                        wallpaperData)).isFalse();
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isFalse();
     }
 
     // Test isWallpaperCompatibleForDisplay, portrait display, wallpaper is suitable for the display
@@ -794,24 +840,13 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.logicalWidth = 1044;
         displayInfo.logicalHeight = 2560;
+        setUpWithDisplays(List.of(new Point(displayInfo.logicalWidth, displayInfo.logicalHeight)));
         doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
         WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
                 new Point(2000, 4000));
 
-        assertThat(
-                mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
-                        wallpaperData)).isTrue();
-    }
-
-    private void mockDisplay(int displayId, Point displayResolution) {
-        final Display mockDisplay = mock(Display.class);
-        when(mockDisplay.getDisplayInfo(any(DisplayInfo.class))).thenAnswer(invocation -> {
-            DisplayInfo displayInfo = invocation.getArgument(0);
-            displayInfo.displayId = displayId;
-            displayInfo.logicalWidth = displayResolution.x;
-            displayInfo.logicalHeight = displayResolution.y;
-            return true;
-        });
+        assertThat(new WallpaperCropper(mWallpaperDisplayHelper).isWallpaperCompatibleForDisplay(
+                displayId, wallpaperData)).isTrue();
     }
 
     private WallpaperData createWallpaperData(boolean isStockWallpaper, Point wallpaperSize)
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperDefaultDisplayInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperDefaultDisplayInfoTest.java
new file mode 100644
index 0000000..312db91
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperDefaultDisplayInfoTest.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.wallpaper;
+
+import static android.app.WallpaperManager.ORIENTATION_LANDSCAPE;
+import static android.app.WallpaperManager.ORIENTATION_PORTRAIT;
+import static android.app.WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE;
+import static android.app.WallpaperManager.ORIENTATION_SQUARE_PORTRAIT;
+import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+import com.android.server.wallpaper.WallpaperDefaultDisplayInfo.FoldableOrientations;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.Set;
+
+/** Unit tests for {@link WallpaperDefaultDisplayInfo}. */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class WallpaperDefaultDisplayInfoTest {
+    @Mock
+    private WindowManager mWindowManager;
+
+    @Mock
+    private Resources mResources;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+    }
+
+    @Test
+    public void defaultDisplayInfo_foldable_shouldHaveExpectedContent() {
+        doReturn(new int[]{0}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect innerDisplayBounds = new Rect(0, 0, 2076, 2152);
+        Rect outerDisplayBounds = new Rect(0, 0, 1080, 2424);
+        WindowMetrics innerDisplayMetrics =
+                new WindowMetrics(innerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        WindowMetrics outerDisplayMetrics =
+                new WindowMetrics(outerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                .thenReturn(Set.of(innerDisplayMetrics, outerDisplayMetrics));
+
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        SparseArray<Point> displaySizes = new SparseArray<>();
+        displaySizes.put(ORIENTATION_PORTRAIT, new Point(1080, 2424));
+        displaySizes.put(ORIENTATION_LANDSCAPE, new Point(2424, 1080));
+        displaySizes.put(ORIENTATION_SQUARE_PORTRAIT, new Point(2076, 2152));
+        displaySizes.put(ORIENTATION_SQUARE_LANDSCAPE, new Point(2152, 2076));
+        assertThat(defaultDisplayInfo.defaultDisplaySizes.contentEquals(displaySizes)).isTrue();
+        assertThat(defaultDisplayInfo.isFoldable).isTrue();
+        assertThat(defaultDisplayInfo.isLargeScreen).isFalse();
+        assertThat(defaultDisplayInfo.foldableOrientations).containsExactly(
+                new FoldableOrientations(
+                        /* foldedOrientation= */ ORIENTATION_PORTRAIT,
+                        /* unfoldedOrientation= */ ORIENTATION_SQUARE_PORTRAIT),
+                new FoldableOrientations(
+                        /* foldedOrientation= */ ORIENTATION_LANDSCAPE,
+                        /* unfoldedOrientation= */ ORIENTATION_SQUARE_LANDSCAPE));
+    }
+
+    @Test
+    public void defaultDisplayInfo_tablet_shouldHaveExpectedContent() {
+        doReturn(new int[]{}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect displayBounds = new Rect(0, 0, 2560, 1600);
+        WindowMetrics displayMetrics =
+                new WindowMetrics(displayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                .thenReturn(Set.of(displayMetrics));
+
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        SparseArray<Point> displaySizes = new SparseArray<>();
+        displaySizes.put(ORIENTATION_PORTRAIT, new Point(1600, 2560));
+        displaySizes.put(ORIENTATION_LANDSCAPE, new Point(2560, 1600));
+        assertThat(defaultDisplayInfo.defaultDisplaySizes.contentEquals(displaySizes)).isTrue();
+        assertThat(defaultDisplayInfo.isFoldable).isFalse();
+        assertThat(defaultDisplayInfo.isLargeScreen).isTrue();
+        assertThat(defaultDisplayInfo.foldableOrientations).isEmpty();
+    }
+
+    @Test
+    public void defaultDisplayInfo_phone_shouldHaveExpectedContent() {
+        doReturn(new int[]{}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect displayBounds = new Rect(0, 0, 1280, 2856);
+        WindowMetrics displayMetrics =
+                new WindowMetrics(displayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 3f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                .thenReturn(Set.of(displayMetrics));
+
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        SparseArray<Point> displaySizes = new SparseArray<>();
+        displaySizes.put(ORIENTATION_PORTRAIT, new Point(1280, 2856));
+        displaySizes.put(ORIENTATION_LANDSCAPE, new Point(2856, 1280));
+        assertThat(defaultDisplayInfo.defaultDisplaySizes.contentEquals(displaySizes)).isTrue();
+        assertThat(defaultDisplayInfo.isFoldable).isFalse();
+        assertThat(defaultDisplayInfo.isLargeScreen).isFalse();
+        assertThat(defaultDisplayInfo.foldableOrientations).isEmpty();
+    }
+
+    @Test
+    public void defaultDisplayInfo_equals_sameContent_shouldEqual() {
+        doReturn(new int[]{0}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect innerDisplayBounds = new Rect(0, 0, 2076, 2152);
+        Rect outerDisplayBounds = new Rect(0, 0, 1080, 2424);
+        WindowMetrics innerDisplayMetrics =
+                new WindowMetrics(innerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        WindowMetrics outerDisplayMetrics =
+                new WindowMetrics(outerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                .thenReturn(Set.of(innerDisplayMetrics, outerDisplayMetrics));
+
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+        WallpaperDefaultDisplayInfo otherDefaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        assertThat(defaultDisplayInfo).isEqualTo(otherDefaultDisplayInfo);
+    }
+
+    @Test
+    public void defaultDisplayInfo_equals_differentBounds_shouldNotEqual() {
+        doReturn(new int[]{0}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect innerDisplayBounds = new Rect(0, 0, 2076, 2152);
+        Rect outerDisplayBounds = new Rect(0, 0, 1080, 2424);
+        WindowMetrics innerDisplayMetrics =
+                new WindowMetrics(innerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        WindowMetrics outerDisplayMetrics =
+                new WindowMetrics(outerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                // For the first call
+                .thenReturn(Set.of(innerDisplayMetrics, outerDisplayMetrics))
+                // For the second+ call
+                .thenReturn(Set.of(innerDisplayMetrics));
+
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+        WallpaperDefaultDisplayInfo otherDefaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        assertThat(defaultDisplayInfo).isNotEqualTo(otherDefaultDisplayInfo);
+    }
+
+    @Test
+    public void defaultDisplayInfo_hashCode_sameContent_shouldEqual() {
+        doReturn(new int[]{0}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect innerDisplayBounds = new Rect(0, 0, 2076, 2152);
+        Rect outerDisplayBounds = new Rect(0, 0, 1080, 2424);
+        WindowMetrics innerDisplayMetrics =
+                new WindowMetrics(innerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        WindowMetrics outerDisplayMetrics =
+                new WindowMetrics(outerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                .thenReturn(Set.of(innerDisplayMetrics, outerDisplayMetrics));
+
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+        WallpaperDefaultDisplayInfo otherDefaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        assertThat(defaultDisplayInfo.hashCode()).isEqualTo(otherDefaultDisplayInfo.hashCode());
+    }
+
+    @Test
+    public void defaultDisplayInfo_hashCode_differentBounds_shouldNotEqual() {
+        doReturn(new int[]{0}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect innerDisplayBounds = new Rect(0, 0, 2076, 2152);
+        Rect outerDisplayBounds = new Rect(0, 0, 1080, 2424);
+        WindowMetrics innerDisplayMetrics =
+                new WindowMetrics(innerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        WindowMetrics outerDisplayMetrics =
+                new WindowMetrics(outerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                // For the first call
+                .thenReturn(Set.of(innerDisplayMetrics, outerDisplayMetrics))
+                // For the second+ call
+                .thenReturn(Set.of(innerDisplayMetrics));
+
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+        WallpaperDefaultDisplayInfo otherDefaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        assertThat(defaultDisplayInfo.hashCode()).isNotEqualTo(otherDefaultDisplayInfo.hashCode());
+    }
+
+    @Test
+    public void getFoldedOrientation_foldable_shouldReturnExpectedOrientation() {
+        doReturn(new int[]{0}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect innerDisplayBounds = new Rect(0, 0, 2076, 2152);
+        Rect outerDisplayBounds = new Rect(0, 0, 1080, 2424);
+        WindowMetrics innerDisplayMetrics =
+                new WindowMetrics(innerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        WindowMetrics outerDisplayMetrics =
+                new WindowMetrics(outerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                .thenReturn(Set.of(innerDisplayMetrics, outerDisplayMetrics));
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        assertThat(defaultDisplayInfo.getFoldedOrientation(ORIENTATION_SQUARE_PORTRAIT))
+                .isEqualTo(ORIENTATION_PORTRAIT);
+        assertThat(defaultDisplayInfo.getFoldedOrientation(ORIENTATION_SQUARE_LANDSCAPE))
+                .isEqualTo(ORIENTATION_LANDSCAPE);
+        // Use a folded orientation for a folded orientation should return unknown.
+        assertThat(defaultDisplayInfo.getFoldedOrientation(ORIENTATION_PORTRAIT))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+        assertThat(defaultDisplayInfo.getFoldedOrientation(ORIENTATION_LANDSCAPE))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+    }
+
+    @Test
+    public void getUnfoldedOrientation_foldable_shouldReturnExpectedOrientation() {
+        doReturn(new int[]{0}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect innerDisplayBounds = new Rect(0, 0, 2076, 2152);
+        Rect outerDisplayBounds = new Rect(0, 0, 1080, 2424);
+        WindowMetrics innerDisplayMetrics =
+                new WindowMetrics(innerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        WindowMetrics outerDisplayMetrics =
+                new WindowMetrics(outerDisplayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2.4375f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                .thenReturn(Set.of(innerDisplayMetrics, outerDisplayMetrics));
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        assertThat(defaultDisplayInfo.getUnfoldedOrientation(ORIENTATION_PORTRAIT))
+                .isEqualTo(ORIENTATION_SQUARE_PORTRAIT);
+        assertThat(defaultDisplayInfo.getUnfoldedOrientation(ORIENTATION_LANDSCAPE))
+                .isEqualTo(ORIENTATION_SQUARE_LANDSCAPE);
+        // Use an unfolded orientation for an unfolded orientation should return unknown.
+        assertThat(defaultDisplayInfo.getUnfoldedOrientation(ORIENTATION_SQUARE_PORTRAIT))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+        assertThat(defaultDisplayInfo.getUnfoldedOrientation(ORIENTATION_SQUARE_LANDSCAPE))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+    }
+
+    @Test
+    public void getFoldedOrientation_nonFoldable_shouldReturnUnknown() {
+        doReturn(new int[]{}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect displayBounds = new Rect(0, 0, 2560, 1600);
+        WindowMetrics displayMetrics =
+                new WindowMetrics(displayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                .thenReturn(Set.of(displayMetrics));
+
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        assertThat(defaultDisplayInfo.getFoldedOrientation(ORIENTATION_SQUARE_PORTRAIT))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+        assertThat(defaultDisplayInfo.getFoldedOrientation(ORIENTATION_SQUARE_LANDSCAPE))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+        assertThat(defaultDisplayInfo.getFoldedOrientation(ORIENTATION_PORTRAIT))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+        assertThat(defaultDisplayInfo.getFoldedOrientation(ORIENTATION_LANDSCAPE))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+    }
+
+    @Test
+    public void getUnFoldedOrientation_nonFoldable_shouldReturnUnknown() {
+        doReturn(new int[]{}).when(mResources).getIntArray(eq(R.array.config_foldedDeviceStates));
+        Rect displayBounds = new Rect(0, 0, 2560, 1600);
+        WindowMetrics displayMetrics =
+                new WindowMetrics(displayBounds, new WindowInsets.Builder().build(),
+                        /* density= */ 2f);
+        when(mWindowManager.getPossibleMaximumWindowMetrics(anyInt()))
+                .thenReturn(Set.of(displayMetrics));
+
+        WallpaperDefaultDisplayInfo defaultDisplayInfo = new WallpaperDefaultDisplayInfo(
+                mWindowManager, mResources);
+
+        assertThat(defaultDisplayInfo.getUnfoldedOrientation(ORIENTATION_SQUARE_PORTRAIT))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+        assertThat(defaultDisplayInfo.getUnfoldedOrientation(ORIENTATION_SQUARE_LANDSCAPE))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+        assertThat(defaultDisplayInfo.getUnfoldedOrientation(ORIENTATION_PORTRAIT))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+        assertThat(defaultDisplayInfo.getUnfoldedOrientation(ORIENTATION_LANDSCAPE))
+                .isEqualTo(ORIENTATION_UNKNOWN);
+    }
+}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 4e56422..94ce723 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -39,9 +39,9 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -53,8 +53,8 @@
 import android.os.BatteryStatsInternal;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.IWakeLockCallback;
 import android.os.IScreenTimeoutPolicyListener;
+import android.os.IWakeLockCallback;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -205,7 +205,7 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
-        verifyZeroInteractions(mVibrator);
+        verifyNoMoreInteractions(mVibrator);
     }
 
     @Test
@@ -238,7 +238,7 @@
         mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
-        verifyZeroInteractions(mVibrator);
+        verifyNoMoreInteractions(mVibrator);
     }
 
     @Test
@@ -725,10 +725,11 @@
 
         final int uid = 1234;
         final int pid = 5678;
+
         mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
                 "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
                 exceptingCallback);
-        verifyZeroInteractions(mWakeLockLog);
+        verifyNoMoreInteractions(mWakeLockLog);
         mTestLooper.dispatchAll();
         verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
         clearInvocations(mBatteryStats);
@@ -790,6 +791,55 @@
     }
 
     @Test
+    public void test_wakeLockLogUsesWorkSource() {
+        createNotifier();
+        clearInvocations(mWakeLockLog);
+        IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
+            @Override public void onStateChanged(boolean enabled) throws RemoteException {
+                throw new RemoteException("Just testing");
+            }
+        };
+
+        final int uid = 1234;
+        final int pid = 5678;
+        WorkSource worksource = new WorkSource(1212);
+        WorkSource worksource2 = new WorkSource(3131);
+
+        mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, worksource, /* historyTag= */ null,
+                exceptingCallback);
+        verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
+                PowerManager.PARTIAL_WAKE_LOCK, -1);
+
+        // Release the wakelock
+        mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
+                "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
+                exceptingCallback);
+        verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, -1);
+
+        // clear the handler
+        mTestLooper.dispatchAll();
+
+        // Now test with improveWakelockLatency flag true
+        clearInvocations(mWakeLockLog);
+        when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
+
+        mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, worksource, /* historyTag= */ null,
+                exceptingCallback);
+        mTestLooper.dispatchAll();
+        verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", 1212,
+                PowerManager.PARTIAL_WAKE_LOCK, 1);
+
+        // Release the wakelock
+        mNotifier.onWakeLockReleased(PowerManager.FULL_WAKE_LOCK, "wakelockTag2",
+                "my.package.name", uid, pid, worksource2, /* historyTag= */ null,
+                exceptingCallback);
+        mTestLooper.dispatchAll();
+        verify(mWakeLockLog).onWakeLockReleased("wakelockTag2", 3131, 1);
+    }
+
+    @Test
     public void
             test_notifierProcessesWorkSourceDeepCopy_OnWakelockChanging() throws RemoteException {
         when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
@@ -845,7 +895,7 @@
                 exceptingCallback);
 
         // No interaction because we expect that to happen in async
-        verifyZeroInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
+        verifyNoMoreInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
 
         // Progressing the looper, and validating all the interactions
         mTestLooper.dispatchAll();
@@ -944,15 +994,23 @@
         assertEquals(mNotifier.getWakelockMonitorTypeForLogging(PowerManager.PARTIAL_WAKE_LOCK),
                 PowerManager.PARTIAL_WAKE_LOCK);
         assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
+                        PowerManager.DOZE_WAKE_LOCK), -1);
+    }
+
+    @Test
+    public void getWakelockMonitorTypeForLogging_evaluateProximityLevel() {
+        // How proximity wakelock is evaluated depends on boolean configuration. Test both.
+        when(mResourcesSpy.getBoolean(
+                com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
+                .thenReturn(false);
+        createNotifier();
+        assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
                         PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK),
                 PowerManager.PARTIAL_WAKE_LOCK);
-        assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
-                        PowerManager.DOZE_WAKE_LOCK), -1);
 
         when(mResourcesSpy.getBoolean(
                 com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity))
                 .thenReturn(true);
-
         createNotifier();
         assertEquals(mNotifier.getWakelockMonitorTypeForLogging(
                         PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK), -1);
@@ -1239,7 +1297,7 @@
                     }
 
                     @Override
-                    public WakeLockLog getWakeLockLog(Context context) {
+                    public @NonNull WakeLockLog getWakeLockLog(Context context) {
                         return mWakeLockLog;
                     }
 
diff --git a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
index c1d7c7b..534337e 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
@@ -27,6 +27,8 @@
 import android.os.PowerManager;
 import android.os.Process;
 
+import com.android.server.power.WakeLockLog.TagData;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -62,8 +64,9 @@
     @Test
     public void testAddTwoItems_withNoEventTimeSupplied() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
         when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
         log.onWakeLockAcquired("TagPartial", 101,
@@ -93,8 +96,9 @@
     @Test
     public void testAddTwoItems() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101,
@@ -117,8 +121,9 @@
     @Test
     public void testAddTwoItemsWithTimeReset() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -136,9 +141,10 @@
 
     @Test
     public void testAddTwoItemsWithTagOverwrite() {
-        final int tagDatabaseSize = 2;
+        final int tagDatabaseSize = 1;
+        final int tagStartingSize = 1;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -157,8 +163,9 @@
     @Test
     public void testAddFourItemsWithRingBufferOverflow() {
         final int tagDatabaseSize = 6;
+        final int tagStartingSize = 2;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         // Wake lock 1 acquired - log size = 3
@@ -206,8 +213,9 @@
     @Test
     public void testAddItemWithBadTag() {
         final int tagDatabaseSize = 6;
+        final int tagStartingSize = 2;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         // Bad tag means it wont get written
@@ -224,8 +232,9 @@
     @Test
     public void testAddItemWithReducedTagName() {
         final int tagDatabaseSize = 6;
+        final int tagStartingSize = 2;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("*job*/com.one.two.3hree/.one..Last", 101,
@@ -242,9 +251,10 @@
 
     @Test
     public void testAddAcquireAndReleaseWithRepeatTagName() {
-        final int tagDatabaseSize = 6;
+        final int tagDatabaseSize = 5;
+        final int tagStartingSize = 5;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
@@ -263,8 +273,9 @@
     @Test
     public void testAddAcquireAndReleaseWithTimeTravel() {
         final int tagDatabaseSize = 6;
+        final int tagStartingSize = 2;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1100L);
@@ -283,8 +294,9 @@
     @Test
     public void testAddSystemWakelock() {
         final int tagDatabaseSize = 6;
+        final int tagStartingSize = 2;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101,
@@ -302,8 +314,9 @@
     @Test
     public void testAddItemWithNoPackageName() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(mPackageManager.getPackagesForUid(101)).thenReturn(null);
@@ -322,8 +335,9 @@
     @Test
     public void testAddItemWithMultiplePackageNames() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         when(mPackageManager.getPackagesForUid(101)).thenReturn(
@@ -344,8 +358,9 @@
     @Test
     public void testAddItemsWithRepeatOwnerUid_UsesCache() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 20;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101,
@@ -375,8 +390,9 @@
     @Test
     public void testAddItemsWithRepeatOwnerUid_SavedAcquisitions_UsesCache() {
         final int tagDatabaseSize = 128;
+        final int tagStartingSize = 16;
         final int logSize = 10;
-        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+        TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, tagStartingSize, logSize));
         WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
 
         log.onWakeLockAcquired("TagPartial", 101,
@@ -420,6 +436,34 @@
         verify(mPackageManager, times(1)).getPackagesForUid(101);
     }
 
+    @Test
+    public void testTagDatabaseGrowsBeyondStartingSize() {
+        final int tagDatabaseSize = 3;
+        final int tagStartingSize = 1;
+        final int logSize = 10;
+        // start with size = 1 and max size
+        TestInjector injector = new TestInjector(tagDatabaseSize, tagStartingSize, logSize);
+        WakeLockLog.TagDatabase td = new WakeLockLog.TagDatabase(injector);
+
+        // Add one
+        TagData data1 = td.findOrCreateTag("Tagname1", 1001, /* shouldCreate= */ true);
+        assertEquals(0, td.getTagIndex(data1));
+
+        // Check that it grows by adding 1 more
+        TagData data2 = td.findOrCreateTag("Tagname2", 1001, /* shouldCreate= */ true);
+        assertEquals(1, td.getTagIndex(data2));
+
+        // Lets add the last one to fill up the DB to maxSize
+        TagData data3 = td.findOrCreateTag("Tagname3", 1001, /* shouldCreate= */ true);
+        assertEquals(2, td.getTagIndex(data3));
+
+        // Adding a fourth one should replace the oldest one (Tagname1)
+        TagData data4 = td.findOrCreateTag("Tagname4", 1001, /* shouldCreate= */ true);
+        assertEquals(0, td.getTagIndex(data4));
+        assertEquals(tagDatabaseSize, td.getTagIndex(data1));
+
+    }
+
     private String dumpLog(WakeLockLog log, boolean includeTagDb) {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
@@ -429,10 +473,12 @@
 
     public static class TestInjector extends WakeLockLog.Injector {
         private final int mTagDatabaseSize;
+        private final int mTagStartingSize;
         private final int mLogSize;
 
-        public TestInjector(int tagDatabaseSize, int logSize) {
+        public TestInjector(int tagDatabaseSize, int tagStartingSize, int logSize) {
             mTagDatabaseSize = tagDatabaseSize;
+            mTagStartingSize = tagStartingSize;
             mLogSize = logSize;
         }
 
@@ -442,6 +488,11 @@
         }
 
         @Override
+        public int getTagDatabaseStartingSize() {
+            return mTagStartingSize;
+        }
+
+        @Override
         public int getLogSize() {
             return mLogSize;
         }
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java
index 4b91d84..28d39fd 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java
@@ -21,7 +21,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.BatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.view.Display;
 
 import androidx.test.filters.SmallTest;
@@ -35,15 +34,10 @@
 @SmallTest
 @SuppressWarnings("GuardedBy")
 public class AmbientDisplayPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
     private static final long MINUTE_IN_MS = 60 * 1000;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0, 10.0)
             .setNumDisplays(1);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AudioPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AudioPowerCalculatorTest.java
index ce451c2..7f03953 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AudioPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AudioPowerCalculatorTest.java
@@ -21,7 +21,6 @@
 import android.os.BatteryConsumer;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,16 +34,11 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class AudioPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_AUDIO, 360.0);
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
index ec8ede0..7faa295 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.os.BatteryManager;
 import android.os.BatteryUsageStats;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -38,14 +37,9 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class BatteryChargeCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
                     .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java
index ad05b51..8791c2f 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java
@@ -25,13 +25,11 @@
 import android.app.ActivityManager;
 import android.os.BatteryStats;
 import android.os.WorkSource;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.ArrayMap;
 import android.view.Display;
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
@@ -39,11 +37,6 @@
  */
 public class BatteryStatsBackgroundStatsTest {
 
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final int UID = 10500;
 
     /** Test that BatteryStatsImpl.Uid.mOnBatteryBackgroundTimeBase works correctly. */
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
index 4dfc3fc..c0d33cb 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
@@ -20,7 +20,6 @@
 
 import android.os.Binder;
 import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.ArraySet;
 
 import androidx.test.filters.SmallTest;
@@ -28,7 +27,6 @@
 import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.BinderTransactionNameResolver;
 
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -41,11 +39,6 @@
 @android.platform.test.annotations.DisabledOnRavenwood(blockedBy = BinderCallsStats.class)
 public class BatteryStatsBinderCallStatsTest {
 
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final int TRANSACTION_CODE1 = 100;
     private static final int TRANSACTION_CODE2 = 101;
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
index eff1b7b..b73895c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
@@ -40,7 +40,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseArray;
 import android.util.SparseLongArray;
 import android.view.Display;
@@ -57,7 +56,6 @@
 import com.android.internal.util.ArrayUtils;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -88,11 +86,6 @@
 @RunWith(AndroidJUnit4.class)
 @SuppressWarnings("SynchronizeOnNonFinalField")
 public class BatteryStatsCpuTimesTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     @Mock
     KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader;
     @Mock
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
index 7e40e6b..503e233 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
@@ -22,7 +22,6 @@
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -31,7 +30,6 @@
 import com.android.internal.os.MonotonicClock;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -44,11 +42,6 @@
 @SmallTest
 @SuppressWarnings("GuardedBy")
 public class BatteryStatsHistoryIteratorTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
 
     private final MockClock mMockClock = new MockClock();
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 5165e34..fc864dd 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
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
@@ -330,31 +331,24 @@
             return invocation.callRealMethod();
         }).when(mHistory).readFragmentToParcel(any(), any());
 
-        // Prepare history for iteration
-        mHistory.iterate(0, MonotonicClock.UNDEFINED);
+        int eventsRead = 0;
+        BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED);
+        while (iterator.hasNext()) {
+            HistoryItem item = iterator.next();
+            if (item.eventCode == HistoryItem.EVENT_JOB_START) {
+                eventsRead++;
+                assertThat(mReadFiles).containsExactly("123.bh");
+            } else if (item.eventCode == HistoryItem.EVENT_JOB_FINISH) {
+                eventsRead++;
+                assertThat(mReadFiles).containsExactly("123.bh", "1000.bh");
+            } else if (item.eventCode == HistoryItem.EVENT_ALARM) {
+                eventsRead++;
+                assertThat(mReadFiles).containsExactly("123.bh", "1000.bh", "2000.bh");
+            }
+        }
 
-        Parcel parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
-        assertThat(parcel).isNotNull();
-        assertThat(mReadFiles).containsExactly("123.bh");
-
-        // Skip to the end to force reading the next parcel
-        parcel.setDataPosition(parcel.dataSize());
-        mReadFiles.clear();
-        parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
-        assertThat(parcel).isNotNull();
-        assertThat(mReadFiles).containsExactly("1000.bh");
-
-        parcel.setDataPosition(parcel.dataSize());
-        mReadFiles.clear();
-        parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
-        assertThat(parcel).isNotNull();
-        assertThat(mReadFiles).containsExactly("2000.bh");
-
-        parcel.setDataPosition(parcel.dataSize());
-        mReadFiles.clear();
-        parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
-        assertThat(parcel).isNull();
-        assertThat(mReadFiles).isEmpty();
+        assertThat(eventsRead).isEqualTo(3);
+        assertThat(mReadFiles).containsExactly("123.bh", "1000.bh", "2000.bh", "3000.bh");
     }
 
     @Test
@@ -372,25 +366,19 @@
             return invocation.callRealMethod();
         }).when(mHistory).readFragmentToParcel(any(), any());
 
-        // Prepare history for iteration
-        mHistory.iterate(1000, 3000);
+        BatteryStatsHistoryIterator iterator = mHistory.iterate(1000, 3000);
+        while (iterator.hasNext()) {
+            HistoryItem item = iterator.next();
+            if (item.eventCode == HistoryItem.EVENT_JOB_START) {
+                fail("Event outside the range");
+            } else if (item.eventCode == HistoryItem.EVENT_JOB_FINISH) {
+                assertThat(mReadFiles).containsExactly("1000.bh");
+            } else if (item.eventCode == HistoryItem.EVENT_ALARM) {
+                fail("Event outside the range");
+            }
+        }
 
-        Parcel parcel = mHistory.getNextParcel(1000, 3000);
-        assertThat(parcel).isNotNull();
-        assertThat(mReadFiles).containsExactly("1000.bh");
-
-        // Skip to the end to force reading the next parcel
-        parcel.setDataPosition(parcel.dataSize());
-        mReadFiles.clear();
-        parcel = mHistory.getNextParcel(1000, 3000);
-        assertThat(parcel).isNotNull();
-        assertThat(mReadFiles).containsExactly("2000.bh");
-
-        parcel.setDataPosition(parcel.dataSize());
-        mReadFiles.clear();
-        parcel = mHistory.getNextParcel(1000, 3000);
-        assertThat(parcel).isNull();
-        assertThat(mReadFiles).isEmpty();
+        assertThat(mReadFiles).containsExactly("1000.bh", "2000.bh");
     }
 
     private void prepareMultiFileHistory() {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index db32687..1744093 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -92,7 +92,6 @@
 public class BatteryStatsImplTest {
     @Rule(order = 0)
     public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
             .setSystemPropertyImmutable("persist.sys.com.android.server.power.feature.flags."
                 + "framework_wakelock_info-override", null)
             .build();
@@ -150,7 +149,7 @@
         File systemDir = Files.createTempDirectory("BatteryStatsHistoryTest").toFile();
 
         Context context;
-        if (RavenwoodRule.isUnderRavenwood()) {
+        if (RavenwoodRule.isOnRavenwood()) {
             context = mock(Context.class);
             SensorManager sensorManager = mock(SensorManager.class);
             when(sensorManager.getSensorList(anyInt())).thenReturn(List.of());
@@ -856,7 +855,7 @@
     }
 
     private UidTraffic createUidTraffic(int appUid, long rxBytes, long txBytes) {
-        if (RavenwoodRule.isUnderRavenwood()) {
+        if (RavenwoodRule.isOnRavenwood()) {
             UidTraffic uidTraffic = mock(UidTraffic.class);
             when(uidTraffic.getUid()).thenReturn(appUid);
             when(uidTraffic.getRxBytes()).thenReturn(rxBytes);
@@ -881,7 +880,7 @@
             long controllerIdleTimeMs,
             long controllerEnergyUsed,
             UidTraffic... uidTraffic) {
-        if (RavenwoodRule.isUnderRavenwood()) {
+        if (RavenwoodRule.isOnRavenwood()) {
             BluetoothActivityEnergyInfo info = mock(BluetoothActivityEnergyInfo.class);
             when(info.getTimestampMillis()).thenReturn(timestamp);
             when(info.getControllerTxTimeMillis()).thenReturn(controllerTxTimeMs);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 30ff800..ffb309a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -91,7 +91,6 @@
     @Rule
     public final RavenwoodRule mRavenwood =
             new RavenwoodRule.Builder()
-                    .setProvideMainThread(true)
                     .setSystemPropertyImmutable(
                             "persist.sys.com.android.server.power.feature.flags."
                                     + "framework_wakelock_info-override",
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
index b6d49d0..550df3a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
@@ -23,7 +23,6 @@
 
 import android.content.Context;
 import android.os.BatteryManager;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -40,11 +39,6 @@
 public class BatteryStatsResetTest {
 
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .createTempDirectory();
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsSensorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsSensorTest.java
index 96780c3..018e8c2 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsSensorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsSensorTest.java
@@ -22,12 +22,10 @@
 
 import android.app.ActivityManager;
 import android.os.BatteryStats;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.view.Display;
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
@@ -36,11 +34,6 @@
 @SmallTest
 public class BatteryStatsSensorTest {
 
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final int UID = 10500;
     private static final int UID_2 = 10501; // second uid for testing pool usage
     private static final int SENSOR_ID = -10000;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsServTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsServTest.java
index 6f683ae..882e48d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsServTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsServTest.java
@@ -18,13 +18,11 @@
 
 import android.os.BatteryStats;
 import android.os.Parcel;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 
 import junit.framework.Assert;
 
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
@@ -32,11 +30,6 @@
  */
 @SmallTest
 public class BatteryStatsServTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final String TAG = "BatteryStatsServTest";
 
     public static class TestServ extends BatteryStatsImpl.Uid.Pkg.Serv {
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 93fe8d3..8a1d37b5 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
@@ -68,18 +68,13 @@
 @RunWith(AndroidJUnit4.class)
 public class BatteryUsageStatsProviderTest {
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
     private static final long MINUTE_IN_MS = 60 * 1000;
     private static final double PRECISION = 0.00001;
 
-    @Rule(order = 2)
+    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule =
             new BatteryUsageStatsRule(12345)
                     .createTempDirectory()
@@ -94,7 +89,7 @@
 
     @Before
     public void setup() throws IOException {
-        if (RavenwoodRule.isUnderRavenwood()) {
+        if (RavenwoodRule.isOnRavenwood()) {
             mContext = mock(Context.class);
             SensorManager sensorManager = mock(SensorManager.class);
             when(mContext.getSystemService(SensorManager.class)).thenReturn(sensorManager);
@@ -420,7 +415,7 @@
         Parcel parcel = Parcel.obtain();
         parcel.writeParcelable(batteryUsageStats, 0);
 
-        if (!RavenwoodRule.isUnderRavenwood()) {
+        if (!RavenwoodRule.isOnRavenwood()) {
             assertThat(parcel.dataSize()).isAtMost(128_000);
         }
 
@@ -579,7 +574,7 @@
         MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
         accumulateBatteryUsageStats(batteryStats, 10000000, 0);
         // Accumulate every 200 bytes of battery history
-        accumulateBatteryUsageStats(batteryStats, 200, 2);
+        accumulateBatteryUsageStats(batteryStats, 200, 1);
         accumulateBatteryUsageStats(batteryStats, 50, 5);
         // Accumulate on every invocation of accumulateBatteryUsageStats
         accumulateBatteryUsageStats(batteryStats, 0, 7);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 097a60e..dc1c3b4 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -37,7 +37,6 @@
 import android.os.Parcel;
 import android.os.UidBatteryConsumer;
 import android.os.UserBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Xml;
 
 import androidx.test.filters.SmallTest;
@@ -46,7 +45,6 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -64,11 +62,6 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BatteryUsageStatsTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final int USER_ID = 42;
     private static final int APP_UID1 = 271;
     private static final int APP_UID2 = 314;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java
index c9cb0df..9552808 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java
@@ -50,15 +50,10 @@
 @SmallTest
 @SuppressWarnings("GuardedBy")
 public class BluetoothPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE, 10.0)
             .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX, 50.0)
@@ -335,7 +330,7 @@
     }
 
     private UidTraffic createUidTraffic(int appUid, long rxBytes, long txBytes) {
-        if (RavenwoodRule.isUnderRavenwood()) {
+        if (RavenwoodRule.isOnRavenwood()) {
             UidTraffic uidTraffic = mock(UidTraffic.class);
             when(uidTraffic.getUid()).thenReturn(appUid);
             when(uidTraffic.getRxBytes()).thenReturn(rxBytes);
@@ -356,7 +351,7 @@
 
     private BluetoothActivityEnergyInfo createBtEnergyInfo(long timestamp, int stackState,
             long txTime, long rxTime, long idleTime, long energyUsed, List<UidTraffic> traffic) {
-        if (RavenwoodRule.isUnderRavenwood()) {
+        if (RavenwoodRule.isOnRavenwood()) {
             BluetoothActivityEnergyInfo info = mock(BluetoothActivityEnergyInfo.class);
             when(info.getTimestampMillis()).thenReturn(timestamp);
             when(info.getBluetoothStackState()).thenReturn(stackState);
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 3895cb4..98ec6de 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
@@ -57,11 +57,6 @@
     private static final int ISOLATED_UID = 99123;
 
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, 1000);
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java
index 4cd3857..6745ffd 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java
@@ -21,7 +21,6 @@
 import android.os.BatteryConsumer;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,17 +34,12 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class CameraPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
     private static final int APP1_UID = Process.FIRST_APPLICATION_UID + 42;
     private static final int APP2_UID = Process.FIRST_APPLICATION_UID + 43;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_CAMERA, 360.0)
             .initMeasuredEnergyStatsLocked();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
index 527db67..ee58d7e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
@@ -32,7 +32,6 @@
 import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
@@ -56,11 +55,6 @@
 @SmallTest
 @SuppressWarnings("GuardedBy")
 public class CpuPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
@@ -68,7 +62,7 @@
 
     private static final int NUM_CPU_FREQS = 2 + 2;  // 2 clusters * 2 freqs each
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
             .setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
index 1fea462..70f0370 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -32,7 +32,6 @@
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.IndentingPrintWriter;
 import android.util.SparseArray;
 
@@ -46,7 +45,6 @@
 import com.android.server.power.stats.format.CpuPowerStatsLayout;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -60,11 +58,6 @@
 @SmallTest
 public class CpuPowerStatsCollectorTest {
 
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final int ISOLATED_UID = 99123;
     private static final int UID_1 = 42;
     private static final int UID_2 = 99;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java
index 5636242..b89310f 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java
@@ -21,7 +21,6 @@
 import android.os.BatteryConsumer;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.SparseLongArray;
 
 import androidx.test.filters.SmallTest;
@@ -35,16 +34,11 @@
 @SmallTest
 @SuppressWarnings("GuardedBy")
 public class CustomEnergyConsumerPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .initMeasuredEnergyStatsLocked(new String[]{"CUSTOM_COMPONENT1", "CUSTOM_COMPONENT2"});
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java
index 757025e..0d21349 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java
@@ -21,7 +21,6 @@
 import android.os.BatteryConsumer;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,16 +34,11 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class FlashlightPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0);
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java
index 506bab4..b7a0d3d5 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java
@@ -21,7 +21,6 @@
 import android.os.BatteryConsumer;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -36,16 +35,12 @@
 @SmallTest
 @SuppressWarnings("GuardedBy")
 public class GnssPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
     private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 222;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_GPS_ON, 360.0)
             .setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/IdlePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/IdlePowerCalculatorTest.java
index 487d864..f9f2119 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/IdlePowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/IdlePowerCalculatorTest.java
@@ -19,7 +19,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.BatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -33,14 +32,9 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class IdlePowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_CPU_IDLE, 720.0)
             .setAveragePower(PowerProfile.POWER_CPU_SUSPEND, 360.0);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MemoryPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MemoryPowerCalculatorTest.java
index 3a27188..fc0ddeb 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MemoryPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MemoryPowerCalculatorTest.java
@@ -19,7 +19,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.BatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -33,14 +32,9 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MemoryPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_MEMORY, new double[] {360.0, 720.0, 1080.0});
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
index eba820e..b95ff4c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
@@ -62,18 +62,13 @@
 @SmallTest
 @SuppressWarnings("GuardedBy")
 public class MobileRadioPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
     private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
     @Mock
     NetworkStatsManager mNetworkStatsManager;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
 
     @Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
index cd3683b..645411c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
@@ -75,11 +75,6 @@
     private static final int ISOLATED_UID = 99123;
 
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule =
             new BatteryUsageStatsRule().setPowerStatsThrottlePeriodMillis(
                     BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 10000);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
index a04f721..8e7e437 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
@@ -32,7 +32,6 @@
 import android.os.HandlerThread;
 import android.os.PersistableBundle;
 import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.power.PowerStatsInternal;
 
 import androidx.test.filters.SmallTest;
@@ -41,7 +40,6 @@
 import com.android.internal.os.PowerStats;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -51,11 +49,6 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PowerStatsCollectorTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private final MockClock mMockClock = new MockClock();
     private final HandlerThread mHandlerThread = new HandlerThread("test");
     private Handler mHandler;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
index 143d046..2378715 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
@@ -18,11 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.platform.test.ravenwood.RavenwoodRule;
-
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -33,11 +30,6 @@
 
 @RunWith(AndroidJUnit4.class)
 public class PowerStatsSchedulerTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     @Test
     public void alignToWallClock() {
         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
index dc8d920..064d999b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
@@ -21,7 +21,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -29,7 +28,6 @@
 import com.android.modules.utils.TypedXmlSerializer;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParser;
@@ -45,11 +43,6 @@
 public class PowerStatsStoreTest {
     private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 2 * 1024;
 
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private PowerStatsStore mPowerStatsStore;
     private File mStoreDirectory;
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java
index 7f20035..101c362 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java
@@ -25,7 +25,6 @@
 import android.os.BatteryConsumer;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.view.Display;
 
 import androidx.test.filters.SmallTest;
@@ -39,18 +38,13 @@
 @SmallTest
 @SuppressWarnings("GuardedBy")
 public class ScreenPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
     private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
     private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 43;
     private static final long MINUTE_IN_MS = 60 * 1000;
     private static final long MINUTE_IN_US = 60 * 1000 * 1000;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 0, 36.0)
             .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0, 48.0)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
index 8c09d1d..c87f04d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
@@ -28,7 +28,6 @@
 import android.os.BatteryConsumer;
 import android.os.BatteryStats;
 import android.os.Handler;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
@@ -47,11 +46,6 @@
     private static final int ISOLATED_UID = 99123;
 
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, 1000);
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerCalculatorTest.java
index c01f05f..0a42170 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerCalculatorTest.java
@@ -41,11 +41,6 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class SensorPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
     private static final int SENSOR_HANDLE_1 = 1;
@@ -53,7 +48,7 @@
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
 
     @Test
@@ -93,7 +88,7 @@
     }
 
     private Sensor createSensor(int handle, int type, float power) {
-        if (RavenwoodRule.isUnderRavenwood()) {
+        if (RavenwoodRule.isOnRavenwood()) {
             Sensor sensor = mock(Sensor.class);
 
             when(sensor.getHandle()).thenReturn(handle);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/VideoPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/VideoPowerCalculatorTest.java
index b9b7101..e70f7df 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/VideoPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/VideoPowerCalculatorTest.java
@@ -21,7 +21,6 @@
 import android.os.BatteryConsumer;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,16 +34,11 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class VideoPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_VIDEO, 360.0);
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java
index 9645e90..cfae6b1 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java
@@ -43,7 +43,6 @@
     @Rule(order = 0)
     public final RavenwoodRule mRavenwood =
             new RavenwoodRule.Builder()
-                    .setProvideMainThread(true)
                     .setSystemPropertyImmutable(
                             "persist.sys.com.android.server.power.feature.flags."
                                     + "framework_wakelock_info-override",
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java
index 827d2f8..0ec0575 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java
@@ -54,11 +54,6 @@
 @SmallTest
 @SuppressWarnings("GuardedBy")
 public class WifiPowerCalculatorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@@ -66,7 +61,7 @@
     @Mock
     NetworkStatsManager mNetworkStatsManager;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0)
             .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX, 480.0)
@@ -92,7 +87,7 @@
 
     private NetworkStats buildNetworkStats(long elapsedRealtime, long rxBytes, long rxPackets,
             long txBytes, long txPackets) {
-        if (RavenwoodRule.isUnderRavenwood()) {
+        if (RavenwoodRule.isOnRavenwood()) {
             NetworkStats stats = mock(NetworkStats.class);
 //        when(stats.getElapsedRealtime()).thenReturn(elapsedRealtime);
 
@@ -358,7 +353,7 @@
     private WifiActivityEnergyInfo buildWifiActivityEnergyInfo(long timeSinceBoot,
             int stackState, long txDuration, long rxDuration, long scanDuration,
             long idleDuration) {
-        if (RavenwoodRule.isUnderRavenwood()) {
+        if (RavenwoodRule.isOnRavenwood()) {
             WifiActivityEnergyInfo info = mock(WifiActivityEnergyInfo.class);
             when(info.getTimeSinceBootMillis()).thenReturn(timeSinceBoot);
             when(info.getStackState()).thenReturn(stackState);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
index a26b2c9..3e15c0e2 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
@@ -68,11 +68,6 @@
     private static final int ISOLATED_UID = 99123;
 
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_WIFI, 1000);
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
index 58e9d1e..7ca3a9d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
@@ -31,7 +31,6 @@
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.BatteryConsumer;
 import android.os.Handler;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerProfile;
@@ -52,11 +51,6 @@
 public class AmbientDisplayPowerStatsProcessorTest {
 
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setNumDisplays(2)
             .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 0, 180.0)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
index e6e7f6e..10d5e80 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
@@ -35,7 +35,6 @@
 import android.os.BatteryStats;
 import android.os.PersistableBundle;
 import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.annotation.NonNull;
 
@@ -44,17 +43,11 @@
 import com.android.server.power.stats.MockClock;
 import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
 
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.function.Supplier;
 
 public class BinaryStatePowerStatsProcessorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
     private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
     private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
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 6d7119d..d8328bf 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
@@ -69,18 +69,13 @@
 
 public class BluetoothPowerStatsProcessorTest {
 
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
     private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
     private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
     private static final int BLUETOOTH_ENERGY_CONSUMER_ID = 1;
     private static final int VOLTAGE_MV = 3500;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX, 50.0)
             .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
index a959632..2244b73 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
@@ -40,7 +40,6 @@
 import android.os.BatteryStats;
 import android.os.Handler;
 import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.MonotonicClock;
@@ -63,11 +62,6 @@
 
 public class CameraPowerStatsTest {
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_CAMERA, 100.0)
             .initMeasuredEnergyStatsLocked();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
index dcddf06..3bc97bd 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
@@ -33,7 +33,6 @@
 
 import android.os.BatteryConsumer;
 import android.os.PersistableBundle;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.IntArray;
 import android.util.LongArray;
 
@@ -60,11 +59,6 @@
 @SmallTest
 public class CpuPowerStatsProcessorTest {
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
             .setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
index a421675..c18bc3e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
@@ -39,7 +39,6 @@
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.Handler;
 import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
@@ -64,11 +63,6 @@
 
 public class CustomEnergyConsumerPowerStatsTest {
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
 
     public static final int ENERGY_CONSUMER_ID1 = 77;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
index b4f2113..9071389 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
@@ -41,7 +41,6 @@
 import android.os.BatteryStats;
 import android.os.Handler;
 import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.MonotonicClock;
@@ -64,11 +63,6 @@
 
 public class GnssPowerStatsTest {
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_GPS_ON, 100.0)
             .setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, new double[]{1000, 100})
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
index 3dc4017..95bf931 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
@@ -73,18 +73,13 @@
 import java.util.function.Supplier;
 
 public class MobileRadioPowerStatsProcessorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
     private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
     private static final int MOBILE_RADIO_ENERGY_CONSUMER_ID = 1;
     private static final int VOLTAGE_MV = 3500;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
     @Mock
     private Context mContext;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStatePowerAttributorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStatePowerAttributorTest.java
index 704ee62..14dd975 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStatePowerAttributorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStatePowerAttributorTest.java
@@ -27,7 +27,6 @@
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import com.android.internal.os.BatteryStatsHistory;
 import com.android.internal.os.MonotonicClock;
@@ -37,7 +36,6 @@
 import com.android.server.power.stats.PowerStatsStore;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -52,11 +50,6 @@
 
 public class MultiStatePowerAttributorTest {
 
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private PowerStatsStore mPowerStatsStore;
     private Handler mHandler;
     private final MockClock mClock = new MockClock();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
index 2f742d7..742f250 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
@@ -37,7 +37,6 @@
 import android.os.BatteryConsumer;
 import android.os.Handler;
 import android.os.OutcomeReceiver;
-import android.platform.test.ravenwood.RavenwoodRule;
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
 
@@ -59,15 +58,10 @@
 import java.util.function.Supplier;
 
 public class PhoneCallPowerStatsProcessorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
     private static final int VOLTAGE_MV = 3500;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
     @Mock
     private Context mContext;
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 9ef58cc..a5a29f5 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
@@ -33,7 +33,6 @@
 import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.os.UidBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -67,11 +66,6 @@
     private static final double TOLERANCE = 0.01;
 
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
             .setCpuScalingPolicy(0, new int[]{0}, new int[]{100})
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
index 31456a15..1ca62b4 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
@@ -36,7 +36,6 @@
 import android.os.BatteryStats;
 import android.os.Handler;
 import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerProfile;
@@ -59,11 +58,6 @@
 public class ScreenPowerStatsProcessorTest {
 
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setNumDisplays(2)
             .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 0, 180.0)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
index c2f01d1..3c8a580 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
@@ -58,11 +58,6 @@
 
 public class SensorPowerStatsProcessorTest {
     @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .initMeasuredEnergyStatsLocked();
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
index e36056a..d4e6810 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
@@ -74,18 +74,13 @@
 import java.util.function.Supplier;
 
 public class WifiPowerStatsProcessorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
     private static final double PRECISION = 0.00001;
     private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
     private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
     private static final int WIFI_ENERGY_CONSUMER_ID = 1;
     private static final int VOLTAGE_MV = 3500;
 
-    @Rule(order = 1)
+    @Rule(order = 0)
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0)
             .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX, 480.0)
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
index 879aa48..2fd316e 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
@@ -409,7 +409,7 @@
         final String TAG = "startTestService";
         final CountDownLatch latch = new CountDownLatch(1);
         LocalIntrusionDetectionEventTransport transport =
-                new LocalIntrusionDetectionEventTransport();
+                new LocalIntrusionDetectionEventTransport(mContext);
 
         ServiceConnection serviceConnection = new ServiceConnection() {
             // Called when connection with the service is established.
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
index f0012da..b0b7815 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/LocalIntrusionDetectionEventTransport.java
@@ -18,8 +18,13 @@
 
 package com.android.coretests.apps.testapp;
 
+import android.app.admin.SecurityLog;
+import android.app.admin.SecurityLog.SecurityEvent;
+import android.content.Context;
+import android.content.Intent;
 import android.security.intrusiondetection.IntrusionDetectionEvent;
 import android.security.intrusiondetection.IntrusionDetectionEventTransport;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -36,6 +41,44 @@
 public class LocalIntrusionDetectionEventTransport extends IntrusionDetectionEventTransport {
     private List<IntrusionDetectionEvent> mEvents = new ArrayList<>();
 
+    private static final String ACTION_SECURITY_EVENT_RECEIVED =
+            "com.android.coretests.apps.testapp.ACTION_SECURITY_EVENT_RECEIVED";
+    private static final String TAG = "LocalIntrusionDetectionEventTransport";
+    private static final String TEST_SECURITY_EVENT_TAG = "test_security_event_tag";
+    private static Context sContext;
+
+    public LocalIntrusionDetectionEventTransport(Context context) {
+        sContext = context;
+    }
+
+    // Broadcast an intent to the CTS test service to indicate that the security
+    // event was received.
+    private static void broadcastSecurityEventReceived() {
+        try {
+            Intent intent = new Intent(ACTION_SECURITY_EVENT_RECEIVED);
+            sContext.sendBroadcast(intent);
+            Log.i(TAG, "LIZ_TESTING: sent broadcast");
+        } catch (Exception e) {
+            Log.e(TAG, "Exception sending broadcast", e);
+        }
+    }
+
+    private static void checkIfSecurityEventReceivedFromCts(List<IntrusionDetectionEvent> events) {
+        // Loop through the events and check if any of them are the security event
+        // that uses the TEST_SECURITY_EVENT_TAG tag, which is set by the CTS test.
+        for (IntrusionDetectionEvent event : events) {
+            if (event.getType() == IntrusionDetectionEvent.SECURITY_EVENT) {
+                SecurityEvent securityEvent = event.getSecurityEvent();
+                Object[] eventData = (Object[]) securityEvent.getData();
+                if (securityEvent.getTag() == SecurityLog.TAG_KEY_GENERATED
+                        && eventData[1].equals(TEST_SECURITY_EVENT_TAG)) {
+                    broadcastSecurityEventReceived();
+                    return;
+                }
+            }
+        }
+    }
+
     @Override
     public boolean initialize() {
         return true;
@@ -43,6 +86,11 @@
 
     @Override
     public boolean addData(List<IntrusionDetectionEvent> events) {
+        // Our CTS tests will generate a security event. In order to
+        // verify the event is received with the appropriate data, we will
+        // check the events locally and set a property value that can be
+        // read by the test.
+        checkIfSecurityEventReceivedFromCts(events);
         mEvents.addAll(events);
         return true;
     }
@@ -55,4 +103,4 @@
     public List<IntrusionDetectionEvent> getEvents() {
         return mEvents;
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
index e4bf987..9183a75 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
@@ -17,19 +17,20 @@
 package com.android.coretests.apps.testapp;
 
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
-import android.os.Process;
-
-import com.android.internal.infra.AndroidFuture;
-
 
 public class TestLoggingService extends Service {
     private static final String TAG = "TestLoggingService";
     private LocalIntrusionDetectionEventTransport mLocalIntrusionDetectionEventTransport;
 
-    public TestLoggingService() {
-        mLocalIntrusionDetectionEventTransport = new LocalIntrusionDetectionEventTransport();
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        Context context = getApplicationContext();
+        mLocalIntrusionDetectionEventTransport = new LocalIntrusionDetectionEventTransport(context);
     }
 
     // Binder given to clients.
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 5240f58..cc0d5e4 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -32,8 +32,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index ce3751a..d254e96 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -23,10 +23,10 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyListOf;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
@@ -35,7 +35,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.Manifest.permission;
@@ -243,7 +242,7 @@
     @Test
     public void testRequestScores_providerNotConnected() throws Exception {
         assertFalse(mNetworkScoreService.requestScores(new NetworkKey[0]));
-        verifyZeroInteractions(mRecommendationProvider);
+        verifyNoMoreInteractions(mRecommendationProvider);
     }
 
     @Test
@@ -328,8 +327,8 @@
         // updateScores should update both caches
         mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
 
-        verify(mNetworkScoreCache).updateScores(anyListOf(ScoredNetwork.class));
-        verify(mNetworkScoreCache2).updateScores(anyListOf(ScoredNetwork.class));
+        verify(mNetworkScoreCache).updateScores(anyList());
+        verify(mNetworkScoreCache2).updateScores(anyList());
 
         mNetworkScoreService.unregisterNetworkScoreCache(
                 NetworkKey.TYPE_WIFI, mNetworkScoreCache2);
@@ -337,7 +336,7 @@
         // updateScores should only update the first cache since the 2nd has been unregistered
         mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
 
-        verify(mNetworkScoreCache, times(2)).updateScores(anyListOf(ScoredNetwork.class));
+        verify(mNetworkScoreCache, times(2)).updateScores(anyList());
 
         mNetworkScoreService.unregisterNetworkScoreCache(
                 NetworkKey.TYPE_WIFI, mNetworkScoreCache);
@@ -604,7 +603,7 @@
         consumer.accept(mNetworkScoreCache, null /*cookie*/);
 
         verify(mNetworkScoreCache).updateScores(scoredNetworkList);
-        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+        verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
     }
 
     @Test
@@ -618,7 +617,7 @@
         consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
 
         verify(mNetworkScoreCache).updateScores(scoredNetworkList);
-        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+        verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
     }
 
     @Test
@@ -632,7 +631,7 @@
         consumer.accept(mNetworkScoreCache, -1 /*cookie*/);
 
         verify(mNetworkScoreCache).updateScores(scoredNetworkList);
-        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+        verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
     }
 
     @Test
@@ -646,7 +645,7 @@
         consumer.accept(mNetworkScoreCache, "not an int" /*cookie*/);
 
         verify(mNetworkScoreCache).updateScores(scoredNetworkList);
-        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+        verifyNoMoreInteractions(mCurrentNetworkFilter, mScanResultsFilter);
     }
 
     @Test
@@ -658,7 +657,7 @@
 
         consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
 
-        verifyZeroInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
+        verifyNoMoreInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
     }
 
     @Test
@@ -676,7 +675,7 @@
         consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK);
 
         verify(mNetworkScoreCache).updateScores(filteredList);
-        verifyZeroInteractions(mScanResultsFilter);
+        verifyNoMoreInteractions(mScanResultsFilter);
     }
 
     @Test
@@ -694,7 +693,7 @@
         consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS);
 
         verify(mNetworkScoreCache).updateScores(filteredList);
-        verifyZeroInteractions(mCurrentNetworkFilter);
+        verifyNoMoreInteractions(mCurrentNetworkFilter);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
index 52428e8..1621c5d4 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
@@ -23,9 +23,9 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
diff --git a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
index c18faef..16df97b 100644
--- a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 42b84bd..c7c8c58 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -64,7 +64,6 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityService;
@@ -838,7 +837,7 @@
         // ...without secure layers included
         assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isFalse();
         // No error sent to callback
-        verifyZeroInteractions(mMockCallback);
+        verifyNoMoreInteractions(mMockCallback);
     }
 
     @Test
@@ -856,7 +855,7 @@
         // ...with secure layers included
         assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
         // No error sent to callback
-        verifyZeroInteractions(mMockCallback);
+        verifyNoMoreInteractions(mMockCallback);
     }
 
     @Test
@@ -889,7 +888,7 @@
         // ...with secure layers included
         assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
         // No error sent to callback
-        verifyZeroInteractions(mMockCallback);
+        verifyNoMoreInteractions(mMockCallback);
     }
 
     private void takeScreenshotOfWindow(int windowFlags) throws Exception {
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 9cfa51a..2ccd336 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -50,6 +50,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
@@ -66,6 +67,7 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
@@ -77,10 +79,12 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.input.KeyGestureEvent;
 import android.net.Uri;
@@ -114,6 +118,7 @@
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.TestUtils;
 import com.android.internal.R;
@@ -136,6 +141,8 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
+import com.google.common.truth.Correspondence;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -188,6 +195,8 @@
             DESCRIPTION,
             TEST_PENDING_INTENT);
 
+    private static final int FAKE_SYSTEMUI_UID = 1000;
+
     private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1;
     private static final String TARGET_MAGNIFICATION = MAGNIFICATION_CONTROLLER_NAME;
     private static final ComponentName TARGET_ALWAYS_ON_A11Y_SERVICE =
@@ -207,11 +216,12 @@
     @Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
     @Mock private WindowManagerInternal.AccessibilityControllerInternal mMockA11yController;
     @Mock private PackageManager mMockPackageManager;
+    @Mock
+    private PackageManagerInternal mMockPackageManagerInternal;
     @Mock private WindowManagerInternal mMockWindowManagerService;
     @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
     @Mock private SystemActionPerformer mMockSystemActionPerformer;
     @Mock private AccessibilityWindowManager mMockA11yWindowManager;
-    @Mock private AccessibilityDisplayListener mMockA11yDisplayListener;
     @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
     @Mock private UserManagerInternal mMockUserManagerInternal;
     @Mock private IBinder mMockBinder;
@@ -234,6 +244,7 @@
     private TestableLooper mTestableLooper;
     private Handler mHandler;
     private FakePermissionEnforcer mFakePermissionEnforcer;
+    private TestDisplayManagerWrapper mTestDisplayManagerWrapper;
 
     @Before
     public void setUp() throws Exception {
@@ -246,6 +257,7 @@
         LocalServices.removeServiceForTest(UserManagerInternal.class);
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
         LocalServices.removeServiceForTest(PermissionEnforcer.class);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(
                 WindowManagerInternal.class, mMockWindowManagerService);
         LocalServices.addService(
@@ -256,6 +268,12 @@
         mInputFilter = mock(FakeInputFilter.class);
         mTestableContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
 
+        when(mMockPackageManagerInternal.getSystemUiServiceComponent()).thenReturn(
+                new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService"));
+        when(mMockPackageManagerInternal.getPackageUid(eq("com.android.systemui"), anyLong(),
+                anyInt())).thenReturn(FAKE_SYSTEMUI_UID);
+        LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
+
         when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
                 mMockMagnificationConnectionManager);
         when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
@@ -273,15 +291,9 @@
                 eq(UserHandle.USER_CURRENT)))
                 .thenReturn(mTestableContext.getUserId());
 
-        final ArrayList<Display> displays = new ArrayList<>();
-        final Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(),
-                Display.DEFAULT_DISPLAY, new DisplayInfo(),
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        final Display testDisplay = new Display(DisplayManagerGlobal.getInstance(), TEST_DISPLAY,
-                new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        displays.add(defaultDisplay);
-        displays.add(testDisplay);
-        when(mMockA11yDisplayListener.getValidDisplayList()).thenReturn(displays);
+        mTestDisplayManagerWrapper = new TestDisplayManagerWrapper(mTestableContext);
+        mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(Display.TYPE_INTERNAL,
+                Display.TYPE_EXTERNAL);
 
         mA11yms = new AccessibilityManagerService(
                 mTestableContext,
@@ -290,7 +302,7 @@
                 mMockSecurityPolicy,
                 mMockSystemActionPerformer,
                 mMockA11yWindowManager,
-                mMockA11yDisplayListener,
+                mTestDisplayManagerWrapper,
                 mMockMagnificationController,
                 mInputFilter,
                 mProxyManager,
@@ -2309,6 +2321,73 @@
                 mA11yms.getCurrentUserIdLocked())).isEmpty();
     }
 
+    @Test
+    public void displayListReturnsDisplays() {
+        mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(
+                        Display.TYPE_INTERNAL,
+                        Display.TYPE_EXTERNAL,
+                        Display.TYPE_WIFI,
+                        Display.TYPE_OVERLAY,
+                        Display.TYPE_VIRTUAL
+        );
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // In #setUp() we already have TYPE_INTERNAL and TYPE_EXTERNAL. Call the rest.
+            for (int i = 2; i < mTestDisplayManagerWrapper.mDisplays.size(); i++) {
+                mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(
+                        mTestDisplayManagerWrapper.mDisplays.get(i).getDisplayId());
+            }
+        });
+
+        List<Display> displays = mA11yms.getValidDisplayList();
+        assertThat(displays).hasSize(5);
+        assertThat(displays)
+                .comparingElementsUsing(
+                        Correspondence.transforming(Display::getType, "has a type of"))
+                .containsExactly(Display.TYPE_INTERNAL,
+                        Display.TYPE_EXTERNAL,
+                        Display.TYPE_WIFI,
+                        Display.TYPE_OVERLAY,
+                        Display.TYPE_VIRTUAL);
+    }
+
+    @Test
+    public void displayListReturnsDisplays_excludesVirtualPrivate() {
+        // Add a private virtual display whose uid is different from systemui.
+        final List<Display> displays = createFakeDisplayList(Display.TYPE_INTERNAL,
+                Display.TYPE_EXTERNAL);
+        displays.add(createFakeVirtualPrivateDisplay(/* displayId= */ 2, FAKE_SYSTEMUI_UID + 100));
+        mTestDisplayManagerWrapper.mDisplays = displays;
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(2);
+        });
+
+        List<Display> validDisplays = mA11yms.getValidDisplayList();
+        assertThat(validDisplays).hasSize(2);
+        assertThat(validDisplays)
+                .comparingElementsUsing(
+                        Correspondence.transforming(Display::getType, "has a type of"))
+                .doesNotContain(Display.TYPE_VIRTUAL);
+    }
+
+    @Test
+    public void displayListReturnsDisplays_includesVirtualSystemUIPrivate() {
+        // Add a private virtual display whose uid is systemui.
+        final List<Display> displays = createFakeDisplayList(Display.TYPE_INTERNAL,
+                Display.TYPE_EXTERNAL);
+        displays.add(createFakeVirtualPrivateDisplay(/* displayId= */ 2, FAKE_SYSTEMUI_UID));
+        mTestDisplayManagerWrapper.mDisplays = displays;
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mTestDisplayManagerWrapper.mRegisteredListener.onDisplayAdded(2);
+        });
+
+        List<Display> validDisplays = mA11yms.getValidDisplayList();
+        assertThat(validDisplays).hasSize(3);
+        assertThat(validDisplays)
+                .comparingElementsUsing(
+                        Correspondence.transforming(Display::getType, "has a type of"))
+                .contains(Display.TYPE_VIRTUAL);
+    }
+
     private Set<String> readStringsFromSetting(String setting) {
         final Set<String> result = new ArraySet<>();
         mA11yms.readColonDelimitedSettingToSet(
@@ -2374,14 +2453,6 @@
         return lockState;
     }
 
-    private void assertStartActivityWithExpectedComponentName(Context mockContext,
-            String componentName) {
-        verify(mockContext).startActivityAsUser(mIntentArgumentCaptor.capture(),
-                any(Bundle.class), any(UserHandle.class));
-        assertThat(mIntentArgumentCaptor.getValue().getStringExtra(
-                Intent.EXTRA_COMPONENT_NAME)).isEqualTo(componentName);
-    }
-
     private void assertStartActivityWithExpectedShortcutType(Context mockContext,
             @UserShortcutType int shortcutType) {
         verify(mockContext).startActivityAsUser(mIntentArgumentCaptor.capture(),
@@ -2430,6 +2501,27 @@
                 });
     }
 
+    private static List<Display> createFakeDisplayList(int... types) {
+        final ArrayList<Display> displays = new ArrayList<>();
+        for (int i = 0; i < types.length; i++) {
+            final DisplayInfo info = new DisplayInfo();
+            info.type = types[i];
+            final Display display = new Display(DisplayManagerGlobal.getInstance(),
+                    i, info, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+            displays.add(display);
+        }
+        return displays;
+    }
+
+    private static Display createFakeVirtualPrivateDisplay(int displayId, int uid) {
+        final DisplayInfo info = new DisplayInfo();
+        info.type = Display.TYPE_VIRTUAL;
+        info.flags |= Display.FLAG_PRIVATE;
+        info.ownerUid = uid;
+        return new Display(DisplayManagerGlobal.getInstance(),
+                displayId, info, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+    }
+
     public static class FakeInputFilter extends AccessibilityInputFilter {
         FakeInputFilter(Context context,
                 AccessibilityManagerService service) {
@@ -2484,10 +2576,6 @@
             return mMockContext;
         }
 
-        public void addMockUserContext(int userId, Context context) {
-            mMockUserContexts.put(userId, context);
-        }
-
         @Override
         @NonNull
         public Context createContextAsUser(UserHandle user, int flags) {
@@ -2518,4 +2606,35 @@
         Set<String> setting = readStringsFromSetting(ShortcutUtils.convertToKey(shortcutType));
         assertThat(setting).containsExactlyElementsIn(value);
     }
+
+    private static class TestDisplayManagerWrapper extends
+            AccessibilityDisplayListener.DisplayManagerWrapper {
+        List<Display> mDisplays;
+        DisplayManager.DisplayListener mRegisteredListener;
+
+        TestDisplayManagerWrapper(Context context) {
+            super(context);
+        }
+
+        @Override
+        public Display[] getDisplays() {
+            return mDisplays.toArray(new Display[0]);
+        }
+
+        @Override
+        public Display getDisplay(int displayId) {
+            for (final Display display : mDisplays) {
+                if (display.getDisplayId() == displayId) {
+                    return display;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+                @Nullable Handler handler) {
+            mRegisteredListener = listener;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
index c16f3d3f..1792201 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
@@ -29,9 +29,9 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -129,7 +129,7 @@
                 mMockServiceCallback, mMockReplacerConnection, INTERACTION_ID, INTERROGATING_PID,
                 INTERROGATING_TID);
         verify(mMockReplacerConnection).findAccessibilityNodeInfoByAccessibilityId(
-                eq(AccessibilityNodeInfo.ROOT_NODE_ID), (Region) anyObject(),
+                eq(AccessibilityNodeInfo.ROOT_NODE_ID), (Region) any(),
                 mInteractionIdCaptor.capture(), eq(mActionReplacingCallback), eq(0),
                 eq(INTERROGATING_PID), eq(INTERROGATING_TID), nullable(MagnificationSpec.class),
                 nullable(float[].class), eq(null));
@@ -246,9 +246,9 @@
             throws RemoteException {
         doThrow(RemoteException.class).when(mMockReplacerConnection)
                 .findAccessibilityNodeInfoByAccessibilityId(eq(AccessibilityNodeInfo.ROOT_NODE_ID),
-                        (Region) anyObject(), anyInt(), (ActionReplacingCallback) anyObject(),
+                        (Region) any(), anyInt(), (ActionReplacingCallback) any(),
                         eq(0),  eq(INTERROGATING_PID), eq(INTERROGATING_TID),
-                        (MagnificationSpec) anyObject(), nullable(float[].class), eq(null));
+                        (MagnificationSpec) any(), nullable(float[].class), eq(null));
         ActionReplacingCallback actionReplacingCallback = new ActionReplacingCallback(
                 mMockServiceCallback, mMockReplacerConnection, INTERACTION_ID, INTERROGATING_PID,
                 INTERROGATING_TID);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
index 96ae102e5..d0dc2cb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.FingerprintGestureController;
@@ -86,7 +86,7 @@
                 mMockFingerprintGestureCallback);
         mFingerprintGestureController.onGestureDetectionActiveChanged(true);
         mFingerprintGestureController.onGestureDetectionActiveChanged(false);
-        verifyZeroInteractions(mMockFingerprintGestureCallback);
+        verifyNoMoreInteractions(mMockFingerprintGestureCallback);
     }
 
     @Test
@@ -118,7 +118,7 @@
         mFingerprintGestureController.onGestureDetectionActiveChanged(true);
         mFingerprintGestureController.onGestureDetectionActiveChanged(false);
         assertFalse(messageCapturingHandler.hasMessages());
-        verifyZeroInteractions(mMockFingerprintGestureCallback);
+        verifyNoMoreInteractions(mMockFingerprintGestureCallback);
 
         messageCapturingHandler.removeAllMessages();
     }
@@ -135,7 +135,7 @@
         mFingerprintGestureController.unregisterFingerprintGestureCallback(
                 mMockFingerprintGestureCallback);
         mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
-        verifyZeroInteractions(mMockFingerprintGestureCallback);
+        verifyNoMoreInteractions(mMockFingerprintGestureCallback);
     }
 
     @Test
@@ -159,7 +159,7 @@
                 mMockFingerprintGestureCallback);
         mFingerprintGestureController.onGesture(FINGERPRINT_GESTURE_SWIPE_DOWN);
         assertFalse(messageCapturingHandler.hasMessages());
-        verifyZeroInteractions(mMockFingerprintGestureCallback);
+        verifyNoMoreInteractions(mMockFingerprintGestureCallback);
 
         messageCapturingHandler.removeAllMessages();
     }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
index 30d00ad..f5a708d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
@@ -18,8 +18,8 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
index efea214..3565244 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
@@ -36,6 +36,8 @@
 import android.media.AudioDeviceInfo;
 import android.media.AudioDevicePort;
 import android.media.AudioManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 
@@ -67,6 +69,8 @@
 public class HearingDevicePhoneCallNotificationControllerTest {
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
+    @Rule
+    public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
 
@@ -118,6 +122,7 @@
                 AudioManager.DEVICE_OUT_BLE_HEADSET);
         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
                 new AudioDeviceInfo[]{hapDeviceInfo});
+        when(mAudioManager.getCommunicationDevice()).thenReturn(hapDeviceInfo);
         when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
 
         mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -132,6 +137,7 @@
                 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
                 new AudioDeviceInfo[]{a2dpDeviceInfo});
+        when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
         when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(a2dpDeviceInfo));
 
         mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -146,6 +152,7 @@
                 AudioManager.DEVICE_OUT_BLE_HEADSET);
         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
                 new AudioDeviceInfo[]{hapDeviceInfo});
+        when(mAudioManager.getCommunicationDevice()).thenReturn(hapDeviceInfo);
         when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
 
         mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
@@ -155,6 +162,51 @@
                 eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH));
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+    public void onCallStateChanged_nonHearingDevice_offHookThenIdle_callAddAndRemoveListener() {
+        final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
+                ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
+        AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+                AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+        when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+                new AudioDeviceInfo[]{a2dpDeviceInfo});
+        when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+        mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+        mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE);
+
+        verify(mAudioManager).addOnCommunicationDeviceChangedListener(any(Executor.class),
+                listenerCaptor.capture());
+        verify(mAudioManager).removeOnCommunicationDeviceChangedListener(
+                eq(listenerCaptor.getValue()));
+    }
+
+
+    @Test
+    @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+    public void onCallStateChanged_hearingDeviceFromCommunicationDeviceChanged_showNotification() {
+        final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
+                ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
+        AudioDeviceInfo hapDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+                AudioManager.DEVICE_OUT_BLE_HEADSET);
+        AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+                AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+        when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+                new AudioDeviceInfo[]{a2dpDeviceInfo});
+        when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+        mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+        verify(mAudioManager).addOnCommunicationDeviceChangedListener(any(Executor.class),
+                listenerCaptor.capture());
+        when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+                new AudioDeviceInfo[]{hapDeviceInfo});
+        listenerCaptor.getValue().onCommunicationDeviceChanged(hapDeviceInfo);
+
+        verify(mNotificationManager).notify(
+                eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH), any());
+    }
+
     private AudioDeviceInfo createAudioDeviceInfo(String address, int type) {
         AudioDevicePort audioDevicePort = mock(AudioDevicePort.class);
         doReturn(type).when(audioDevicePort).type();
@@ -171,7 +223,7 @@
             HearingDevicePhoneCallNotificationController.CallStateListener {
 
         TestCallStateListener(@NonNull Context context) {
-            super(context);
+            super(context, context.getMainExecutor());
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index ebb73e8..186f742 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -20,14 +20,14 @@
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertTrue;
 
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 
@@ -96,12 +96,12 @@
                 mLock, powerManager, mMessageCapturingHandler);
 
         mKeyEventFilter1 = mock(KeyEventFilter.class);
-        when(mKeyEventFilter1.onKeyEvent((KeyEvent) anyObject(),
+        when(mKeyEventFilter1.onKeyEvent((KeyEvent) any(),
                 mFilter1SequenceCaptor.capture().intValue()))
                 .thenReturn(true);
 
         mKeyEventFilter2 = mock(KeyEventFilter.class);
-        when(mKeyEventFilter2.onKeyEvent((KeyEvent) anyObject(),
+        when(mKeyEventFilter2.onKeyEvent((KeyEvent) any(),
                 mFilter2SequenceCaptor.capture().intValue()))
                 .thenReturn(true);
     }
@@ -122,7 +122,7 @@
     @Test
     public void testNotifyKeyEvent_boundServiceDoesntProcessEvents_shouldReturnFalse() {
         KeyEventFilter keyEventFilter = mock(KeyEventFilter.class);
-        when(keyEventFilter.onKeyEvent((KeyEvent) anyObject(), anyInt())).thenReturn(false);
+        when(keyEventFilter.onKeyEvent((KeyEvent) any(), anyInt())).thenReturn(false);
         assertFalse(mKeyEventDispatcher
                 .notifyKeyEventLocked(mKeyEvent, 0, Arrays.asList(keyEventFilter)));
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
@@ -159,7 +159,7 @@
                 mFilter1SequenceCaptor.getValue());
 
         assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
     }
 
@@ -189,7 +189,7 @@
                 mFilter2SequenceCaptor.getValue());
 
         assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
     }
 
@@ -261,7 +261,7 @@
         mKeyEventDispatcher.setOnKeyEventResult(mKeyEventFilter2, false,
                 mFilter2SequenceCaptor.getValue());
         assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
         assertFalse(isTimeoutPending(mMessageCapturingHandler));
     }
 
@@ -278,7 +278,7 @@
         mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
 
         assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
     }
 
     @Test
@@ -293,7 +293,7 @@
         mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
 
         assertTrue(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
     }
 
     @Test
@@ -327,7 +327,7 @@
                 mFilter1SequenceCaptor.getValue());
 
         assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
     }
 
     @Test
@@ -344,7 +344,7 @@
         mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0));
 
         assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT));
-        verifyZeroInteractions(mMockPowerManagerService);
+        verifyNoMoreInteractions(mMockPowerManagerService);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index c62cae5..e351512 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -21,8 +21,8 @@
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -94,7 +94,7 @@
         when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
                 argThat(matchesKeyEvent(event)), eq(0))).thenReturn(-1L);
         mInterceptor.onKeyEvent(event, 0);
-        verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
+        verify(mMockAms, times(0)).notifyKeyEvent(any(), anyInt());
         assertFalse(mHandler.hasMessages());
     }
 
@@ -106,7 +106,7 @@
         mInterceptor.onKeyEvent(event, 0);
 
         assertTrue(mHandler.hasMessages());
-        verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
+        verify(mMockAms, times(0)).notifyKeyEvent(any(), anyInt());
 
         when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
                 argThat(matchesKeyEvent(event)), eq(0))).thenReturn(0L);
@@ -123,13 +123,13 @@
         mInterceptor.onKeyEvent(event, 0);
 
         assertTrue(mHandler.hasMessages());
-        verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
+        verify(mMockAms, times(0)).notifyKeyEvent(any(), anyInt());
 
         when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
                 argThat(matchesKeyEvent(event)), eq(0))).thenReturn(-1L);
         mHandler.sendAllMessages();
 
-        verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
+        verify(mMockAms, times(0)).notifyKeyEvent(any(), anyInt());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index c75cfe6..367f2d1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -29,15 +29,14 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 
 import android.accessibilityservice.GestureDescription.GestureStep;
@@ -223,7 +222,7 @@
         verifyNoMoreInteractions(next);
         reset(next);
 
-        verifyZeroInteractions(mServiceInterface);
+        verifyNoMoreInteractions(mServiceInterface);
 
         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
         verify(next).onMotionEvent(argThat(allOf(mIsLineEnd, hasRightDownTime)),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index ea83825..99c922c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -18,12 +18,14 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK;
 import static com.android.server.testutils.MockitoUtilsKt.eq;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -43,6 +45,7 @@
 
 import com.android.internal.accessibility.util.AccessibilityUtils;
 import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.accessibility.BaseEventStreamTransformation;
 
 import org.junit.After;
 import org.junit.Before;
@@ -70,6 +73,20 @@
     @Mock private WindowManager mMockWindowManager;
     private AutoclickController mController;
 
+    private static class MotionEventCaptor extends BaseEventStreamTransformation {
+        public MotionEvent downEvent;
+        public int eventCount = 0;
+        @Override
+        public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    downEvent = event;
+                    eventCount++;
+                    break;
+            }
+        }
+    }
+
     @Before
     public void setUp() {
         mTestableLooper = TestableLooper.get(this);
@@ -533,6 +550,29 @@
 
     @Test
     @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+    public void triggerRightClickWithRevertToLeftClickEnabled_resetClickType() {
+        // Move mouse to initialize autoclick panel.
+        injectFakeMouseActionHoverMoveEvent();
+
+        AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+        mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+        mController.clickPanelController.handleAutoclickTypeChange(AUTOCLICK_TYPE_RIGHT_CLICK);
+
+        // Set ACCESSIBILITY_AUTOCLICK_REVERT_TO_LEFT_CLICK to true.
+        Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_AUTOCLICK_REVERT_TO_LEFT_CLICK,
+                AccessibilityUtils.State.ON,
+                mTestableContext.getUserId());
+        mController.onChangeForTesting(/* selfChange= */ true,
+                Settings.Secure.getUriFor(
+                        Settings.Secure.ACCESSIBILITY_AUTOCLICK_REVERT_TO_LEFT_CLICK));
+        when(mockAutoclickTypePanel.isPaused()).thenReturn(false);
+        mController.mClickScheduler.run();
+        assertThat(mController.mClickScheduler.getRevertToLeftClickForTesting()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
     public void pauseButton_flagOn_clickNotTriggeredWhenPaused() {
         injectFakeMouseActionHoverMoveEvent();
 
@@ -660,6 +700,206 @@
         assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
     }
 
+    @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+    public void onMotionEvent_flagOn_lazyInitAutoclickScrollPanel() {
+        assertThat(mController.mAutoclickScrollPanel).isNull();
+
+        injectFakeMouseActionHoverMoveEvent();
+
+        assertThat(mController.mAutoclickScrollPanel).isNotNull();
+    }
+
+    @Test
+    @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+    public void onMotionEvent_flagOff_notInitAutoclickScrollPanel() {
+        assertThat(mController.mAutoclickScrollPanel).isNull();
+
+        injectFakeMouseActionHoverMoveEvent();
+
+        assertThat(mController.mAutoclickScrollPanel).isNull();
+    }
+
+    @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+    public void onDestroy_flagOn_hideAutoclickScrollPanel() {
+        injectFakeMouseActionHoverMoveEvent();
+        AutoclickScrollPanel mockAutoclickScrollPanel = mock(AutoclickScrollPanel.class);
+        mController.mAutoclickScrollPanel = mockAutoclickScrollPanel;
+
+        mController.onDestroy();
+
+        verify(mockAutoclickScrollPanel).hide();
+    }
+
+    @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+    public void changeFromScrollToOtherClickType_hidesScrollPanel() {
+        injectFakeMouseActionHoverMoveEvent();
+
+        // Set active click type to SCROLL.
+        mController.clickPanelController.handleAutoclickTypeChange(
+                AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL);
+
+        // Show the scroll panel.
+        mController.mAutoclickScrollPanel.show();
+        assertThat(mController.mAutoclickScrollPanel.isVisible()).isTrue();
+
+        // Change click type to LEFT_CLICK.
+        mController.clickPanelController.handleAutoclickTypeChange(
+                AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK);
+
+        // Verify scroll panel is hidden.
+        assertThat(mController.mAutoclickScrollPanel.isVisible()).isFalse();
+    }
+
+    @Test
+    public void sendClick_clickType_leftClick() {
+        MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+        mController.setNext(motionEventCaptor);
+
+        injectFakeMouseActionHoverMoveEvent();
+        // Set delay to zero so click is scheduled to run immediately.
+        mController.mClickScheduler.updateDelay(0);
+
+        // Send hover move event.
+        MotionEvent hoverMove = MotionEvent.obtain(
+                /* downTime= */ 0,
+                /* eventTime= */ 100,
+                /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+                /* x= */ 30f,
+                /* y= */ 0f,
+                /* metaState= */ 0);
+        hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+        mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+        mTestableLooper.processAllMessages();
+
+        // Verify left click sent.
+        assertThat(motionEventCaptor.downEvent).isNotNull();
+        assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+                MotionEvent.BUTTON_PRIMARY);
+    }
+
+    @Test
+    public void sendClick_clickType_rightClick() {
+        MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+        mController.setNext(motionEventCaptor);
+
+        injectFakeMouseActionHoverMoveEvent();
+        // Set delay to zero so click is scheduled to run immediately.
+        mController.mClickScheduler.updateDelay(0);
+
+        // Set click type to right click.
+        mController.clickPanelController.handleAutoclickTypeChange(
+                AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK);
+        AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+        mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+        // Send hover move event.
+        MotionEvent hoverMove = MotionEvent.obtain(
+                /* downTime= */ 0,
+                /* eventTime= */ 100,
+                /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+                /* x= */ 30f,
+                /* y= */ 0f,
+                /* metaState= */ 0);
+        hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+        mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+        mTestableLooper.processAllMessages();
+
+        // Verify right click sent.
+        assertThat(motionEventCaptor.downEvent).isNotNull();
+        assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+                MotionEvent.BUTTON_SECONDARY);
+    }
+
+    @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+    public void sendClick_clickType_scroll_showsScrollPanelOnlyOnce() {
+        MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+        mController.setNext(motionEventCaptor);
+
+        injectFakeMouseActionHoverMoveEvent();
+        // Set delay to zero so click is scheduled to run immediately.
+        mController.mClickScheduler.updateDelay(0);
+
+        // Set click type to scroll.
+        mController.clickPanelController.handleAutoclickTypeChange(
+                AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL);
+
+        // Mock the scroll panel to verify interactions.
+        AutoclickScrollPanel mockScrollPanel = mock(AutoclickScrollPanel.class);
+        mController.mAutoclickScrollPanel = mockScrollPanel;
+
+        // First hover move event.
+        MotionEvent hoverMove1 = MotionEvent.obtain(
+                /* downTime= */ 0,
+                /* eventTime= */ 100,
+                /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+                /* x= */ 30f,
+                /* y= */ 0f,
+                /* metaState= */ 0);
+        hoverMove1.setSource(InputDevice.SOURCE_MOUSE);
+        mController.onMotionEvent(hoverMove1, hoverMove1, /* policyFlags= */ 0);
+        mTestableLooper.processAllMessages();
+
+        // Verify scroll panel is shown once.
+        verify(mockScrollPanel, times(1)).show();
+        assertThat(motionEventCaptor.downEvent).isNull();
+
+        // Second significant hover move event to trigger another autoclick.
+        MotionEvent hoverMove2 = MotionEvent.obtain(
+                /* downTime= */ 0,
+                /* eventTime= */ 200,
+                /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+                /* x= */ 100f,
+                /* y= */ 100f,
+                /* metaState= */ 0);
+        hoverMove2.setSource(InputDevice.SOURCE_MOUSE);
+        mController.onMotionEvent(hoverMove2, hoverMove2, /* policyFlags= */ 0);
+        mTestableLooper.processAllMessages();
+
+        // Verify scroll panel is still only shown once (not called again).
+        verify(mockScrollPanel, times(1)).show();
+        assertThat(motionEventCaptor.downEvent).isNull();
+    }
+
+    @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+    public void hoverOnAutoclickPanel_rightClickType_forceTriggerLeftClick() {
+        MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+        mController.setNext(motionEventCaptor);
+
+        injectFakeMouseActionHoverMoveEvent();
+        // Set delay to zero so click is scheduled to run immediately.
+        mController.mClickScheduler.updateDelay(0);
+
+        // Set click type to right click.
+        mController.clickPanelController.handleAutoclickTypeChange(
+                AutoclickTypePanel.AUTOCLICK_TYPE_RIGHT_CLICK);
+        // Set mouse to hover panel.
+        AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+        when(mockAutoclickTypePanel.isHovered()).thenReturn(true);
+        mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+        // Send hover move event.
+        MotionEvent hoverMove = MotionEvent.obtain(
+                /* downTime= */ 0,
+                /* eventTime= */ 100,
+                /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+                /* x= */ 30f,
+                /* y= */ 0f,
+                /* metaState= */ 0);
+        hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+        mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+        mTestableLooper.processAllMessages();
+
+        // Verify left click is sent due to the mouse hovering the panel.
+        assertThat(motionEventCaptor.downEvent).isNotNull();
+        assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+                MotionEvent.BUTTON_PRIMARY);
+    }
+
     private void injectFakeMouseActionHoverMoveEvent() {
         MotionEvent event = getFakeMotionHoverMoveEvent();
         event.setSource(InputDevice.SOURCE_MOUSE);
@@ -683,6 +923,41 @@
         mController.onKeyEvent(keyEvent, /* policyFlags= */ 0);
     }
 
+    @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+    public void sendClick_clickType_doubleclick_triggerClickTwice() {
+        MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
+        mController.setNext(motionEventCaptor);
+
+        injectFakeMouseActionHoverMoveEvent();
+        // Set delay to zero so click is scheduled to run immediately.
+        mController.mClickScheduler.updateDelay(0);
+
+        // Set click type to double click.
+        mController.clickPanelController.handleAutoclickTypeChange(
+                AutoclickTypePanel.AUTOCLICK_TYPE_DOUBLE_CLICK);
+        AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+        mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+        // Send hover move event.
+        MotionEvent hoverMove = MotionEvent.obtain(
+                /* downTime= */ 0,
+                /* eventTime= */ 100,
+                /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+                /* x= */ 30f,
+                /* y= */ 0f,
+                /* metaState= */ 0);
+        hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+        mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+        mTestableLooper.processAllMessages();
+
+        // Verify left click sent.
+        assertThat(motionEventCaptor.downEvent).isNotNull();
+        assertThat(motionEventCaptor.downEvent.getButtonState()).isEqualTo(
+                MotionEvent.BUTTON_PRIMARY);
+        assertThat(motionEventCaptor.eventCount).isEqualTo(2);
+    }
+
     private MotionEvent getFakeMotionHoverMoveEvent() {
         return MotionEvent.obtain(
                 /* downTime= */ 0,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPanelTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPanelTest.java
new file mode 100644
index 0000000..4c71f7e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickScrollPanelTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility.autoclick;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.never;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageButton;
+
+import com.android.internal.R;
+
+import org.junit.Before;
+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;
+
+/** Test cases for {@link AutoclickScrollPanel}. */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class AutoclickScrollPanelTest {
+    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Rule
+    public TestableContext mTestableContext =
+            new TestableContext(getInstrumentation().getContext());
+
+    @Mock private WindowManager mMockWindowManager;
+    @Mock private AutoclickScrollPanel.ScrollPanelControllerInterface mMockScrollPanelController;
+
+    private AutoclickScrollPanel mScrollPanel;
+
+    // Scroll panel buttons.
+    private ImageButton mUpButton;
+    private ImageButton mDownButton;
+    private ImageButton mLeftButton;
+    private ImageButton mRightButton;
+    private ImageButton mExitButton;
+
+    @Before
+    public void setUp() {
+        mTestableContext.addMockSystemService(Context.WINDOW_SERVICE, mMockWindowManager);
+        mScrollPanel = new AutoclickScrollPanel(mTestableContext, mMockWindowManager,
+                mMockScrollPanelController);
+
+        View contentView = mScrollPanel.getContentViewForTesting();
+
+        // Initialize buttons.
+        mUpButton = contentView.findViewById(R.id.scroll_up);
+        mDownButton = contentView.findViewById(R.id.scroll_down);
+        mLeftButton = contentView.findViewById(R.id.scroll_left);
+        mRightButton = contentView.findViewById(R.id.scroll_right);
+        mExitButton = contentView.findViewById(R.id.scroll_exit);
+    }
+
+    @Test
+    public void show_addsViewToWindowManager() {
+        mScrollPanel.show();
+
+        // Verify view is added to window manager.
+        verify(mMockWindowManager).addView(any(), any(WindowManager.LayoutParams.class));
+
+        // Verify isVisible reflects correct state.
+        assertThat(mScrollPanel.isVisible()).isTrue();
+    }
+
+    @Test
+    public void show_alreadyVisible_doesNotAddAgain() {
+        // Show twice.
+        mScrollPanel.show();
+        mScrollPanel.show();
+
+        // Verify addView was only called once.
+        verify(mMockWindowManager, times(1)).addView(any(), any());
+    }
+
+    @Test
+    public void hide_removesViewFromWindowManager() {
+        // First show the panel.
+        mScrollPanel.show();
+        // Then hide it.
+        mScrollPanel.hide();
+        // Verify view is removed from window manager.
+        verify(mMockWindowManager).removeView(any());
+        // Verify scroll panel is hidden.
+        assertThat(mScrollPanel.isVisible()).isFalse();
+    }
+
+    @Test
+    public void initialState_correctButtonVisibility() {
+        // Verify all expected buttons exist in the view.
+        assertThat(mUpButton.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mDownButton.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mLeftButton.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mRightButton.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mExitButton.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void directionButtons_hoverEvents_callsHoverButtonChange() {
+        // Test hover enter on direction button.
+        triggerHoverEvent(mUpButton, MotionEvent.ACTION_HOVER_ENTER);
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_UP), eq(/* hovered= */ true));
+
+        // Test hover move.
+        reset(mMockScrollPanelController);
+        triggerHoverEvent(mUpButton, MotionEvent.ACTION_HOVER_MOVE);
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_UP), eq(/* hovered= */ true));
+
+        // Test hover exit.
+        reset(mMockScrollPanelController);
+        triggerHoverEvent(mUpButton, MotionEvent.ACTION_HOVER_EXIT);
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_UP), eq(/* hovered= */ false));
+    }
+
+    @Test
+    public void exitButton_hoverEvents_callsHoverButtonChange() {
+        // Test hover enter on exit button.
+        triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_ENTER);
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_EXIT), eq(/* hovered= */ true));
+
+        // Test hover exit - should call the hover change method with false.
+        reset(mMockScrollPanelController);
+        triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_EXIT);
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_EXIT), eq(/* hovered= */ false));
+
+        // Test exit button hover move - should be ignored.
+        reset(mMockScrollPanelController);
+        triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_MOVE);
+        verify(mMockScrollPanelController, never()).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_EXIT), anyBoolean());
+    }
+
+    @Test
+    public void hoverOnButtonSequence_handledCorrectly() {
+        // Test a realistic sequence of events.
+        // Case 1. Hover enter on up button, then hover move with in up button twice.
+        reset(mMockScrollPanelController);
+        triggerHoverEvent(mUpButton, MotionEvent.ACTION_HOVER_ENTER);
+        triggerHoverEvent(mUpButton, MotionEvent.ACTION_HOVER_MOVE);
+        triggerHoverEvent(mUpButton, MotionEvent.ACTION_HOVER_MOVE);
+        verify(mMockScrollPanelController, times(3)).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_UP), eq(true));
+
+        // Case 2. Move from left button to exit button.
+        reset(mMockScrollPanelController);
+        triggerHoverEvent(mLeftButton, MotionEvent.ACTION_HOVER_ENTER);
+        triggerHoverEvent(mLeftButton, MotionEvent.ACTION_HOVER_MOVE);
+        triggerHoverEvent(mLeftButton, MotionEvent.ACTION_HOVER_EXIT);
+        triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_MOVE);
+        triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_ENTER);
+        triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_EXIT);
+
+        // Verify left button events - 2 'true' calls (enter+move) and 1 'false' call (exit).
+        verify(mMockScrollPanelController, times(2)).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_LEFT), eq(/* hovered= */ true));
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_LEFT), eq(/* hovered= */ false));
+        // Verify exit button events - hover_move is ignored so 1 'true' call (enter) and 1
+        // 'false' call (exit).
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_EXIT), eq(/* hovered= */ true));
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_EXIT), eq(/* hovered= */ false));
+
+        // Case 3. Quick transitions between buttons: left → right → down → exit
+        reset(mMockScrollPanelController);
+        triggerHoverEvent(mLeftButton, MotionEvent.ACTION_HOVER_EXIT);
+        triggerHoverEvent(mRightButton, MotionEvent.ACTION_HOVER_ENTER);
+        triggerHoverEvent(mRightButton, MotionEvent.ACTION_HOVER_EXIT);
+        triggerHoverEvent(mDownButton, MotionEvent.ACTION_HOVER_ENTER);
+        triggerHoverEvent(mDownButton, MotionEvent.ACTION_HOVER_EXIT);
+        triggerHoverEvent(mExitButton, MotionEvent.ACTION_HOVER_ENTER);
+
+        // Verify all hover enter/exit events were properly handled
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_LEFT), eq(/* hovered= */ false));
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_RIGHT), eq(/* hovered= */ true));
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_RIGHT), eq(/* hovered= */ false));
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_DOWN), eq(/* hovered= */ true));
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_DOWN), eq(/* hovered= */ false));
+        verify(mMockScrollPanelController).onHoverButtonChange(
+                eq(AutoclickScrollPanel.DIRECTION_EXIT), eq(/* hovered= */ true));
+    }
+
+    // Helper method to simulate a hover event on a view.
+    private void triggerHoverEvent(View view, int action) {
+        MotionEvent event = MotionEvent.obtain(
+                /* downTime= */ 0,
+                /* eventTime= */ 0,
+                /* action= */ action,
+                /* x= */ 0,
+                /* y= */ 0,
+                /* metaState= */ 0);
+
+        // Dispatch the event to the view's OnHoverListener.
+        view.dispatchGenericMotionEvent(event);
+        event.recycle();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
index dd089fc..8bbbca0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
@@ -198,6 +198,72 @@
     }
 
     @Test
+    public void clickLeftClickButton_resumeAutoClick() {
+        // Pause autoclick.
+        mPauseButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isTrue();
+
+        // Click the button and verify autoclick resumes.
+        mLeftClickButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isFalse();
+    }
+
+    @Test
+    public void clickRightClickButton_resumeAutoClick() {
+        // Pause autoclick.
+        mPauseButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isTrue();
+
+        // Click the button and verify autoclick resumes.
+        mRightClickButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isFalse();
+    }
+
+    @Test
+    public void clickDoubleClickButton_resumeAutoClick() {
+        // Pause autoclick.
+        mPauseButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isTrue();
+
+        // Click the button and verify autoclick resumes.
+        mDoubleClickButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isFalse();
+    }
+
+    @Test
+    public void clickDragButton_resumeAutoClick() {
+        // Pause autoclick.
+        mPauseButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isTrue();
+
+        // Click the button and verify autoclick resumes.
+        mDragButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isFalse();
+    }
+
+    @Test
+    public void clickScrollButton_resumeAutoClick() {
+        // Pause autoclick.
+        mPauseButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isTrue();
+
+        // Click the button and verify autoclick resumes.
+        mScrollButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isFalse();
+    }
+
+    @Test
+    public void clickPositionButton_resumeAutoClick() {
+        // Pause autoclick.
+        mPauseButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isTrue();
+
+        // Click the button and verify autoclick resumes.
+        mPositionButton.callOnClick();
+        assertThat(mAutoclickTypePanel.isPaused()).isFalse();
+    }
+
+    @Test
     public void moveToNextCorner_positionButton_rotatesThroughAllPositions() {
         // Define all positions in sequence
         int[][] expectedPositions = {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 1af59da..5922b12 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -37,6 +37,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -46,6 +47,7 @@
 import android.graphics.PointF;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.DexmakerShareClassLoaderRule;
@@ -504,6 +506,36 @@
         assertThat(sentRawEvent.getDisplayId()).isEqualTo(rawDisplayId);
     }
 
+    @Test
+    @DisableFlags(Flags.FLAG_POINTER_UP_MOTION_EVENT_IN_TOUCH_EXPLORATION)
+    public void handleMotionEventStateTouchExploring_pointerUp_doesNotSendToManager() {
+        mTouchExplorer.getState().setServiceDetectsGestures(true);
+        mTouchExplorer.getState().clear();
+
+        mLastEvent = pointerDownEvent();
+        mTouchExplorer.getState().startTouchExploring();
+        MotionEvent event = fromTouchscreen(pointerUpEvent());
+
+        mTouchExplorer.onMotionEvent(event, event, /*policyFlags=*/0);
+
+        verify(mMockAms, never()).sendMotionEventToListeningServices(event);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_POINTER_UP_MOTION_EVENT_IN_TOUCH_EXPLORATION)
+    public void handleMotionEventStateTouchExploring_pointerUp_sendsToManager() {
+        mTouchExplorer.getState().setServiceDetectsGestures(true);
+        mTouchExplorer.getState().clear();
+
+        mLastEvent = pointerDownEvent();
+        mTouchExplorer.getState().startTouchExploring();
+        MotionEvent event = fromTouchscreen(pointerUpEvent());
+
+        mTouchExplorer.onMotionEvent(event, event, /*policyFlags=*/0);
+
+        verify(mMockAms).sendMotionEventToListeningServices(event);
+    }
+
     /**
      * Used to play back event data of a gesture by parsing the log into MotionEvents and sending
      * them to TouchExplorer.
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchStateTest.java
new file mode 100644
index 0000000..3e7d9fd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchStateTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.accessibility.gestures;
+
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_INTERACTION_END;
+
+import static com.android.server.accessibility.gestures.TouchState.STATE_CLEAR;
+import static com.android.server.accessibility.gestures.TouchState.STATE_TOUCH_EXPLORING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.Display;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.Flags;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@RunWith(AndroidJUnit4.class)
+public class TouchStateTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private TouchState mTouchState;
+    @Mock private AccessibilityManagerService mMockAms;
+
+    @Before
+    public void setup() {
+        mTouchState = new TouchState(Display.DEFAULT_DISPLAY, mMockAms);
+    }
+
+    @EnableFlags(Flags.FLAG_POINTER_UP_MOTION_EVENT_IN_TOUCH_EXPLORATION)
+    @Test
+    public void injectedEvent_interactionEnd_pointerDown_startsTouchExploring() {
+        mTouchState.mReceivedPointerTracker.mReceivedPointersDown = 1;
+        mTouchState.onInjectedAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+        assertThat(mTouchState.getState()).isEqualTo(STATE_TOUCH_EXPLORING);
+    }
+
+    @EnableFlags(Flags.FLAG_POINTER_UP_MOTION_EVENT_IN_TOUCH_EXPLORATION)
+    @Test
+    public void injectedEvent_interactionEnd_pointerUp_clears() {
+        mTouchState.mReceivedPointerTracker.mReceivedPointersDown = 0;
+        mTouchState.onInjectedAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+        assertThat(mTouchState.getState()).isEqualTo(STATE_CLEAR);
+    }
+
+    @DisableFlags(Flags.FLAG_POINTER_UP_MOTION_EVENT_IN_TOUCH_EXPLORATION)
+    @Test
+    public void injectedEvent_interactionEnd_clears() {
+        mTouchState.onInjectedAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+        assertThat(mTouchState.getState()).isEqualTo(STATE_CLEAR);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 4ea5fcf..31cdd6c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -43,8 +43,8 @@
 import static org.mockito.AdditionalMatchers.gt;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 0c92abc..9a24104 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -19,11 +19,11 @@
 import static android.database.sqlite.SQLiteDatabase.deleteDatabase;
 
 import static org.mockito.ArgumentMatchers.contains;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
index 61ac74c..514c078 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 1627f68..6d656609 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -58,10 +58,10 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+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.doAnswer;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doNothing;
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java
index 01fee7f..918159f 100644
--- a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java
@@ -97,7 +97,8 @@
         mDiscreteRegistry.recordDiscreteAccess(opEvent2);
         List<DiscreteOp> discreteOps = mDiscreteRegistry.getAllDiscreteOps();
 
-        assertThat(discreteOps.size()).isEqualTo(1);
+        assertWithMessage("Expected list size is 1, but the list is: " + discreteOps)
+                .that(discreteOps.size()).isEqualTo(1);
         assertThat(discreteOps).contains(opEvent);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
index ae973be..56f802b 100644
--- a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
@@ -89,8 +89,7 @@
         int attributionChainId = AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
 
         mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
-                uidState, accessTime, duration, attributionFlags, attributionChainId,
-                DiscreteOpsXmlRegistry.ACCESS_TYPE_FINISH_OP);
+                uidState, accessTime, duration, attributionFlags, attributionChainId);
 
         // Verify in-memory object is correct
         fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
@@ -121,8 +120,7 @@
         int attributionChainId = 10;
 
         mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
-                uidState, accessTime, duration, attributionFlags, attributionChainId,
-                DiscreteOpsXmlRegistry.ACCESS_TYPE_START_OP);
+                uidState, accessTime, duration, attributionFlags, attributionChainId);
 
         fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
                 duration, uidState, opFlags, attributionFlags, attributionChainId);
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
index 8eea1c7..6c66f149 100644
--- a/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
@@ -70,7 +70,7 @@
                     opEvent.getDeviceId(), opEvent.getOpCode(), opEvent.getAttributionTag(),
                     opEvent.getOpFlags(), opEvent.getUidState(), opEvent.getAccessTime(),
                     opEvent.getDuration(), opEvent.getAttributionFlags(),
-                    (int) opEvent.getChainId(), DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP);
+                    (int) opEvent.getChainId());
         }
         xmlRegistry.writeAndClearOldAccessHistory();
         assertThat(xmlRegistry.readLargestChainIdFromDiskLocked()).isEqualTo(RECORD_COUNT);
@@ -104,7 +104,7 @@
                     opEvent.getDeviceId(), opEvent.getOpCode(), opEvent.getAttributionTag(),
                     opEvent.getOpFlags(), opEvent.getUidState(), opEvent.getAccessTime(),
                     opEvent.getDuration(), opEvent.getAttributionFlags(),
-                    (int) opEvent.getChainId(), DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP);
+                    (int) opEvent.getChainId());
         }
         // flush records from cache to the database.
         sqlRegistry.shutdown();
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 3475c8f..20a95e9 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -34,7 +34,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
 import android.attention.AttentionManagerInternal.ProximityUpdateCallbackInternal;
@@ -196,7 +195,7 @@
     @Test
     public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
         mSpyAttentionManager.onStopProximityUpdates(mMockProximityUpdateCallbackInternal);
-        verifyZeroInteractions(mMockProximityUpdateCallbackInternal);
+        verifyNoMoreInteractions(mMockProximityUpdateCallbackInternal);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
index d5638e9..d89cf7b 100644
--- a/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/MusicFxHelperTest.java
@@ -16,7 +16,7 @@
 package com.android.server.audio;
 
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -107,13 +107,13 @@
             List<ResolveInfo> list, int bind, int broadcast, String packageName, int audioSession,
             int uid) {
         doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
-        doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(anyObject(), anyInt());
+        doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(any(), anyInt());
         if (list != null && list.size() != 0) {
             try {
                 doReturn(uid).when(mMockPackageManager)
-                        .getPackageUidAsUser(eq(packageName), anyObject(), anyInt());
+                        .getPackageUidAsUser(eq(packageName), any(), anyInt());
                 doReturn(mMusicFxUid).when(mMockPackageManager)
-                        .getPackageUidAsUser(eq(mMusicFxPkgName), anyObject(), anyInt());
+                        .getPackageUidAsUser(eq(mMusicFxPkgName), any(), anyInt());
             } catch (PackageManager.NameNotFoundException e) {
                 Log.e(TAG, "NameNotFoundException: " + e);
             }
@@ -123,8 +123,8 @@
                 packageName, audioSession);
         mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
         verify(mMockContext, times(bind))
-                .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
-        verify(mMockContext, times(broadcast)).sendBroadcastAsUser(anyObject(), anyObject());
+                .bindServiceAsUser(any(), any(), anyInt(), any());
+        verify(mMockContext, times(broadcast)).sendBroadcastAsUser(any(), any());
     }
 
     /**
@@ -136,13 +136,13 @@
                 List<ResolveInfo> list, int unBind, int broadcast, String packageName,
                 int audioSession, int uid) {
         doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
-        doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(anyObject(), anyInt());
+        doReturn(list).when(mMockPackageManager).queryBroadcastReceivers(any(), anyInt());
         if (list != null && list.size() != 0) {
             try {
                 doReturn(uid).when(mMockPackageManager)
-                        .getPackageUidAsUser(eq(packageName), anyObject(), anyInt());
+                        .getPackageUidAsUser(eq(packageName), any(), anyInt());
                 doReturn(mMusicFxUid).when(mMockPackageManager)
-                        .getPackageUidAsUser(eq(mMusicFxPkgName), anyObject(), anyInt());
+                        .getPackageUidAsUser(eq(mMusicFxPkgName), any(), anyInt());
             } catch (PackageManager.NameNotFoundException e) {
                 Log.e(TAG, "NameNotFoundException: " + e);
             }
@@ -151,8 +151,8 @@
         Intent intent = newIntent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION,
                                 packageName, audioSession);
         mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
-        verify(mMockContext, times(unBind)).unbindService(anyObject());
-        verify(mMockContext, times(broadcast)).sendBroadcastAsUser(anyObject(), anyObject());
+        verify(mMockContext, times(unBind)).unbindService(any());
+        verify(mMockContext, times(broadcast)).sendBroadcastAsUser(any(), any());
     }
 
     /**
@@ -160,8 +160,8 @@
      */
     private void sendMessage(int msgId, int uid, int unBinds, int broadcasts) {
         mMusicFxHelper.handleMessage(Message.obtain(null, msgId, uid /* arg1 */, 0 /* arg2 */));
-        verify(mMockContext, times(broadcasts)).sendBroadcastAsUser(anyObject(), anyObject());
-        verify(mMockContext, times(unBinds)).unbindService(anyObject());
+        verify(mMockContext, times(broadcasts)).sendBroadcastAsUser(any(), any());
+        verify(mMockContext, times(unBinds)).unbindService(any());
     }
 
     /**
@@ -209,15 +209,15 @@
         intent.setPackage(mTestPkg1);
         mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
         verify(mMockContext, times(0))
-                .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
-        verify(mMockContext, times(0)).sendBroadcastAsUser(anyObject(), anyObject());
+                .bindServiceAsUser(any(), any(), anyInt(), any());
+        verify(mMockContext, times(0)).sendBroadcastAsUser(any(), any());
 
         intent = newIntent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION, null, 1);
         intent.setPackage(mTestPkg2);
         mMusicFxHelper.handleAudioEffectBroadcast(mMockContext, intent);
         verify(mMockContext, times(0))
-                .bindServiceAsUser(anyObject(), anyObject(), anyInt(), anyObject());
-        verify(mMockContext, times(0)).sendBroadcastAsUser(anyObject(), anyObject());
+                .bindServiceAsUser(any(), any(), anyInt(), any());
+        verify(mMockContext, times(0)).sendBroadcastAsUser(any(), any());
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index d8a6162..a5d6a19 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -42,7 +42,6 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -451,7 +450,7 @@
         assertEquals(startFingerprintNow ? BiometricSensor.STATE_AUTHENTICATING
                         : BiometricSensor.STATE_COOKIE_RETURNED,
                 session.mPreAuthInfo.eligibleSensors.get(fingerprintSensorId).getSensorState());
-        verify(mBiometricContext).updateContext((OperationContextExt) anyObject(),
+        verify(mBiometricContext).updateContext((OperationContextExt) any(),
                 eq(session.isCrypto()));
 
         // start fingerprint sensor if it was delayed
@@ -554,7 +553,7 @@
 
         session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRMED, null);
         verify(mBiometricFrameworkStatsLogger, times(1)).authenticate(
-                (OperationContextExt) anyObject(),
+                (OperationContextExt) any(),
                 eq(BiometricsProtoEnums.MODALITY_FACE),
                 eq(BiometricsProtoEnums.ACTION_UNKNOWN),
                 eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
@@ -582,10 +581,10 @@
 
         session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, null);
         verify(mBiometricFrameworkStatsLogger, never()).authenticate(
-                anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
+                any(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
                 anyBoolean(), anyInt(), eq(-1f));
         verify(mBiometricFrameworkStatsLogger, never()).error(
-                anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
+                any(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(),
                 anyInt(), anyInt());
     }
 
@@ -605,7 +604,7 @@
 
         session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null);
         verify(mBiometricFrameworkStatsLogger, times(1)).error(
-                (OperationContextExt) anyObject(),
+                (OperationContextExt) any(),
                 eq(BiometricsProtoEnums.MODALITY_FACE),
                 eq(BiometricsProtoEnums.ACTION_AUTHENTICATE),
                 eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
@@ -632,7 +631,7 @@
 
         session.onDialogDismissed(DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS, null);
         verify(mBiometricFrameworkStatsLogger, times(1)).error(
-                (OperationContextExt) anyObject(),
+                (OperationContextExt) any(),
                 eq(BiometricsProtoEnums.MODALITY_FACE),
                 eq(BiometricsProtoEnums.ACTION_AUTHENTICATE),
                 eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT),
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index 7a77033..f4e8717 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.same;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.hardware.biometrics.BiometricFaceConstants;
@@ -172,7 +172,7 @@
         client.onInteractionDetected();
         client.stopHalOperation();
 
-        verifyZeroInteractions(mVibrator);
+        verifyNoMoreInteractions(mVibrator);
     }
 
     private FaceDetectClient createClient() throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
index 67fc564..2e07cd8a 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
@@ -22,18 +22,25 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtual.sensor.IVirtualSensorCallback;
 import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorAdditionalInfo;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.content.AttributionSource;
 import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -49,6 +56,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -65,6 +73,9 @@
 
     private static final int VIRTUAL_SENSOR_TYPE = Sensor.TYPE_ACCELEROMETER;
 
+    private static final float[] ADDITIONAL_INFO_VALUES_1 = new float[] {1.2f, 3.4f};
+    private static final float[] ADDITIONAL_INFO_VALUES_2 = new float[] {5.6f, 7.8f};
+
     @Mock
     private SensorManagerInternal mSensorManagerInternalMock;
     @Mock
@@ -155,6 +166,53 @@
     }
 
     @Test
+    public void sendSensorAdditionalInfo_invalidToken_throwsException() throws Exception {
+        SensorController sensorController = doCreateSensorSuccessfully();
+
+        final VirtualSensorAdditionalInfo info =
+                new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY)
+                        .addValues(ADDITIONAL_INFO_VALUES_1)
+                        .addValues(ADDITIONAL_INFO_VALUES_2)
+                        .build();
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> sensorController.sendSensorAdditionalInfo(
+                        new Binder("invalidSensorToken"), info));
+    }
+
+    @Test
+    public void sendSensorAdditionalInfo_success() throws Exception {
+        SensorController sensorController = doCreateSensorSuccessfully();
+
+        clearInvocations(mSensorManagerInternalMock);
+        when(mSensorManagerInternalMock.sendSensorAdditionalInfo(
+                anyInt(), anyInt(), anyInt(), anyLong(), any()))
+                .thenReturn(true);
+        IBinder token = Iterables.getOnlyElement(sensorController.getSensorDescriptors().keySet());
+
+        final VirtualSensorAdditionalInfo info =
+                new VirtualSensorAdditionalInfo.Builder(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY)
+                        .addValues(ADDITIONAL_INFO_VALUES_1)
+                        .addValues(ADDITIONAL_INFO_VALUES_2)
+                        .build();
+        sensorController.sendSensorAdditionalInfo(token, info);
+
+        InOrder inOrder = inOrder(mSensorManagerInternalMock);
+        inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+                eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_BEGIN),
+                /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull());
+        inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+                eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY),
+                /*serial=*/ eq(0), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_1));
+        inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+                eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_UNTRACKED_DELAY),
+                /*serial=*/ eq(1), /* timestamp= */ anyLong(), eq(ADDITIONAL_INFO_VALUES_2));
+        inOrder.verify(mSensorManagerInternalMock).sendSensorAdditionalInfo(
+                eq(SENSOR_HANDLE), eq(SensorAdditionalInfo.TYPE_FRAME_END),
+                /*serial=*/ eq(0), /* timestamp= */ anyLong(), /*values=*/ isNull());
+    }
+
+    @Test
     public void close_unregistersSensors() throws Exception {
         SensorController sensorController = doCreateSensorSuccessfully();
 
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index b445226..4fa75b9 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -23,7 +23,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -132,8 +132,8 @@
         assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockContentProtectionAllowlistManager);
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -147,7 +147,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager, never()).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -157,8 +157,8 @@
         assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockContentProtectionAllowlistManager);
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -172,7 +172,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager, never()).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -187,7 +187,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager, never()).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -203,7 +203,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -216,8 +216,8 @@
         assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockContentProtectionAllowlistManager);
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -230,8 +230,8 @@
         assertThat(mContentProtectionAllowlistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockContentProtectionAllowlistManager);
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionAllowlistManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -248,7 +248,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -265,7 +265,7 @@
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionAllowlistManager).start(anyLong());
         verify(mMockContentProtectionAllowlistManager).stop();
-        verifyZeroInteractions(mMockContentProtectionConsentManager);
+        verifyNoMoreInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -513,7 +513,7 @@
 
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
     }
 
     @Test
@@ -528,7 +528,7 @@
 
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(1);
         assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
index 195ab68..9d37b99 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
@@ -24,7 +24,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.os.Handler;
@@ -98,10 +98,10 @@
     @Test
     public void constructor() {
         assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
-        verifyZeroInteractions(mMockPackageMonitor);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockPackageMonitor);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -110,10 +110,10 @@
         mTestLooper.dispatchAll();
 
         assertThat(mHandler.hasMessagesOrCallbacks()).isTrue();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
-        verifyZeroInteractions(mMockPackageMonitor);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockPackageMonitor);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -126,8 +126,8 @@
         verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
         verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
         verify(mMockPackageMonitor, never()).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -142,8 +142,8 @@
         verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
         verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
         verify(mMockPackageMonitor, never()).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -153,11 +153,11 @@
         mContentProtectionAllowlistManager.stop();
 
         assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
         verify(mMockPackageMonitor, never()).register(any(), any(), any());
         verify(mMockPackageMonitor).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -169,11 +169,11 @@
         mContentProtectionAllowlistManager.stop();
 
         assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
         verify(mMockPackageMonitor, never()).register(any(), any(), any());
         verify(mMockPackageMonitor).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -188,8 +188,8 @@
         verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
         verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
         verify(mMockPackageMonitor).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -205,8 +205,8 @@
         assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
         verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
         verify(mMockPackageMonitor).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -223,8 +223,8 @@
         assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
         verify(mMockPackageMonitor, times(2)).register(any(), eq(UserHandle.ALL), eq(mHandler));
         verify(mMockPackageMonitor).unregister();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -232,10 +232,10 @@
         boolean actual = mContentProtectionAllowlistManager.isAllowed(FIRST_PACKAGE_NAME);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
-        verifyZeroInteractions(mMockPackageMonitor);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockPackageMonitor);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -248,9 +248,9 @@
         boolean actual = manager.isAllowed(SECOND_PACKAGE_NAME);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
-        verifyZeroInteractions(mMockPackageMonitor);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockPackageMonitor);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
     }
 
     @Test
@@ -263,9 +263,9 @@
         boolean actual = manager.isAllowed(FIRST_PACKAGE_NAME);
 
         assertThat(actual).isTrue();
-        verifyZeroInteractions(mMockContentCaptureManagerService);
-        verifyZeroInteractions(mMockPackageMonitor);
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockContentCaptureManagerService);
+        verifyNoMoreInteractions(mMockPackageMonitor);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
     }
 
     @Test
@@ -276,8 +276,8 @@
         manager.mPackageMonitor.onSomePackagesChanged();
 
         verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -291,7 +291,7 @@
 
         verify(mMockRemoteContentProtectionService)
                 .onUpdateAllowlistRequest(mMockAllowlistCallback);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -309,7 +309,7 @@
         // Does not rethrow
         verify(mMockRemoteContentProtectionService)
                 .onUpdateAllowlistRequest(mMockAllowlistCallback);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -321,8 +321,8 @@
         manager.mPackageMonitor.onSomePackagesChanged();
 
         verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
-        verifyZeroInteractions(mMockRemoteContentProtectionService);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockRemoteContentProtectionService);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -338,7 +338,7 @@
         verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
         verify(mMockRemoteContentProtectionService)
                 .onUpdateAllowlistRequest(mMockAllowlistCallback);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
@@ -355,7 +355,7 @@
         verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
         verify(mMockRemoteContentProtectionService, times(2))
                 .onUpdateAllowlistRequest(mMockAllowlistCallback);
-        verifyZeroInteractions(mMockAllowlistCallback);
+        verifyNoMoreInteractions(mMockAllowlistCallback);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
index b012aaa..cd36a18 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
@@ -27,7 +27,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.admin.DevicePolicyCache;
@@ -112,8 +112,8 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -125,8 +125,8 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -138,8 +138,8 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -152,7 +152,7 @@
 
         assertThat(actual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -166,7 +166,7 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -179,7 +179,7 @@
 
         assertThat(actual).isFalse();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -192,7 +192,7 @@
 
         assertThat(actual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -289,8 +289,8 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -302,8 +302,8 @@
         boolean actual = manager.isConsentGranted(TEST_USER_ID);
 
         assertThat(actual).isFalse();
-        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyManagerInternal);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -316,7 +316,7 @@
 
         assertThat(actual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -339,7 +339,7 @@
         assertThat(thirdActual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
 
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -362,7 +362,7 @@
         assertThat(thirdActual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
 
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -385,7 +385,7 @@
         assertThat(thirdActual).isTrue();
         verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
 
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     @Test
@@ -408,7 +408,7 @@
         assertThat(thirdActual).isTrue();
         verify(mMockDevicePolicyManagerInternal, times(3)).isUserOrganizationManaged(TEST_USER_ID);
 
-        verifyZeroInteractions(mMockDevicePolicyCache);
+        verifyNoMoreInteractions(mMockDevicePolicyCache);
     }
 
     private void putGlobalSettings(String key, int value) {
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
index 6a7e286..563a679 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java
@@ -20,7 +20,7 @@
 
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -87,7 +87,7 @@
     @Test
     public void doesNotAutoConnect() {
         assertThat(mConnectCallCount).isEqualTo(0);
-        verifyZeroInteractions(mMockContentProtectionService);
+        verifyNoMoreInteractions(mMockContentProtectionService);
     }
 
     @Test
@@ -124,7 +124,7 @@
         mRemoteContentProtectionService.onServiceConnectionStatusChanged(
                 mMockContentProtectionService, /* isConnected= */ true);
 
-        verifyZeroInteractions(mMockContentProtectionService);
+        verifyNoMoreInteractions(mMockContentProtectionService);
         assertThat(mConnectCallCount).isEqualTo(0);
     }
 
@@ -133,7 +133,7 @@
         mRemoteContentProtectionService.onServiceConnectionStatusChanged(
                 mMockContentProtectionService, /* isConnected= */ false);
 
-        verifyZeroInteractions(mMockContentProtectionService);
+        verifyNoMoreInteractions(mMockContentProtectionService);
         assertThat(mConnectCallCount).isEqualTo(0);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
index 0f3f27a..9e98af3 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/ProviderRegistryGetSessionTest.java
@@ -21,7 +21,7 @@
 import static org.mockito.ArgumentMatchers.anySet;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
@@ -207,7 +207,7 @@
                 ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY,
                 "unsupportedKey", providerPendingIntentResponse);
 
-        verifyZeroInteractions(mGetRequestSession);
+        verifyNoMoreInteractions(mGetRequestSession);
     }
 
     @Test
@@ -216,7 +216,7 @@
                 ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY,
                 ProviderRegistryGetSession.CREDENTIAL_ENTRY_KEY, null);
 
-        verifyZeroInteractions(mGetRequestSession);
+        verifyNoMoreInteractions(mGetRequestSession);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 5582e13..d5fe0bf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -23,9 +23,9 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 30aa8ce..c50c623 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -88,7 +88,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 import static org.testng.Assert.assertThrows;
@@ -143,6 +142,7 @@
 import android.provider.Settings;
 import android.security.KeyChain;
 import android.security.keystore.AttestationUtils;
+import android.telephony.SubscriptionInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 import android.test.MoreAsserts;
@@ -5304,7 +5304,7 @@
         // both the user restriction and the policy were set by the PO.
         verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed(
                 MANAGED_PROFILE_USER_ID);
-        verifyZeroInteractions(getServices().recoverySystem);
+        verifyNoMoreInteractions(getServices().recoverySystem);
     }
 
     @Test
@@ -5338,7 +5338,7 @@
         // not wiped.
         verify(getServices().userManagerInternal, never())
                 .removeUserEvenWhenDisallowed(anyInt());
-        verifyZeroInteractions(getServices().recoverySystem);
+        verifyNoMoreInteractions(getServices().recoverySystem);
     }
 
     @Test
@@ -5379,7 +5379,7 @@
         dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
 
         // DISALLOW_FACTORY_RESET was set by the system, not the DO, so the device is not wiped.
-        verifyZeroInteractions(getServices().recoverySystem);
+        verifyNoMoreInteractions(getServices().recoverySystem);
         verify(getServices().userManagerInternal, never())
                 .removeUserEvenWhenDisallowed(anyInt());
     }
@@ -7534,7 +7534,7 @@
         verify(getServices().notificationManager, never())
                 .notify(anyInt(), any(Notification.class));
         // Apps shouldn't be suspended.
-        verifyZeroInteractions(getServices().ipackageManager);
+        verifyNoMoreInteractions(getServices().ipackageManager);
         clearInvocations(getServices().alarmManager);
 
         setUserUnlocked(CALLER_USER_HANDLE, false);
@@ -7547,7 +7547,7 @@
         verify(getServices().notificationManager, never())
                 .notify(anyInt(), any(Notification.class));
         // Apps shouldn't be suspended.
-        verifyZeroInteractions(getServices().ipackageManager);
+        verifyNoMoreInteractions(getServices().ipackageManager);
         clearInvocations(getServices().alarmManager);
 
         // Pretend the alarm went off.
@@ -7560,7 +7560,7 @@
         verify(getServices().notificationManager, times(1))
                 .notifyAsUser(any(), anyInt(), any(), any());
         // Apps shouldn't be suspended yet.
-        verifyZeroInteractions(getServices().ipackageManager);
+        verifyNoMoreInteractions(getServices().ipackageManager);
         clearInvocations(getServices().alarmManager);
         clearInvocations(getServices().notificationManager);
 
@@ -7569,7 +7569,7 @@
         sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
 
         // Verify the alarm was not set.
-        verifyZeroInteractions(getServices().alarmManager);
+        verifyNoMoreInteractions(getServices().alarmManager);
         // Now the user should see a notification about suspended apps.
         verify(getServices().notificationManager, times(1))
                 .notifyAsUser(any(), anyInt(), any(), any());
@@ -8715,6 +8715,47 @@
         }
     }
 
+    @RequiresFlagsEnabled(Flags.FLAG_REMOVE_MANAGED_ESIM_ON_WORK_PROFILE_DELETION)
+    @Test
+    public void testManagedProfileDeleted_managedEmbeddedSubscriptionDeleted() throws Exception {
+        // Setup PO mode.
+        setupProfileOwner();
+        // Mock SubscriptionManager to return a subscription managed by the profile owner package.
+        int managedSubscriptionId = 42;
+        SubscriptionInfo managedSubscription = new SubscriptionInfo.Builder().setCardId(1).setId(
+                managedSubscriptionId).setGroupOwner(admin1.getPackageName()).build();
+        when(getServices().subscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
+                List.of(managedSubscription));
+
+        // Send a ACTION_MANAGED_PROFILE_REMOVED broadcast to emulate a managed profile being
+        // removed.
+        sendBroadcastWithUser(dpms, Intent.ACTION_MANAGED_PROFILE_REMOVED, CALLER_USER_HANDLE);
+
+        // Verify that EuiccManager was called to delete the subscription.
+        verify(getServices().euiccManager).deleteSubscription(eq(managedSubscriptionId), any());
+    }
+
+    @RequiresFlagsDisabled(Flags.FLAG_REMOVE_MANAGED_ESIM_ON_WORK_PROFILE_DELETION)
+    @Test
+    public void testManagedProfileDeleted_flagDisabled_managedEmbeddedSubscriptionDeleted()
+            throws Exception {
+        // Set up PO mode.
+        setupProfileOwner();
+        // Mock SubscriptionManager to return a subscription managed by the profile owner package.
+        int managedSubscriptionId = 42;
+        SubscriptionInfo managedSubscription = new SubscriptionInfo.Builder().setCardId(1).setId(
+                managedSubscriptionId).setGroupOwner(admin1.getPackageName()).build();
+        when(getServices().subscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
+                List.of(managedSubscription));
+
+        // Send a ACTION_MANAGED_PROFILE_REMOVED broadcast to emulate a managed profile being
+        // removed.
+        sendBroadcastWithUser(dpms, Intent.ACTION_MANAGED_PROFILE_REMOVED, CALLER_USER_HANDLE);
+
+        // Verify that EuiccManager was not called to delete the subscription.
+        verifyNoMoreInteractions(getServices().euiccManager);
+    }
+
     private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
         final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage,
                 userVpnUid, List.of(new AppOpsManager.OpEntry(
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 00b0c55..479af73 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -253,6 +253,8 @@
                 return mMockSystemServices.subscriptionManager;
             case Context.USB_SERVICE:
                 return mMockSystemServices.usbManager;
+            case Context.EUICC_SERVICE:
+                return mMockSystemServices.euiccManager;
         }
         throw new UnsupportedOperationException();
     }
@@ -487,6 +489,14 @@
     }
 
     @Override
+    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+            IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
+        mMockSystemServices.registerReceiver(receiver, filter, scheduler);
+        return spiedContext.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+                scheduler, flags);
+    }
+
+    @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
         mMockSystemServices.unregisterReceiver(receiver);
         spiedContext.unregisterReceiver(receiver);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 03aaeb7e..b52eb0a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -21,7 +21,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.when;
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 3e4448c1..d01fa91 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -68,6 +68,7 @@
 import android.security.KeyChain;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.euicc.EuiccManager;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.util.ArrayMap;
@@ -151,6 +152,7 @@
     public final File dataDir;
     public final PolicyPathProvider pathProvider;
     public final SupervisionManagerInternal supervisionManagerInternal;
+    public final EuiccManager euiccManager;
 
     private final Map<String, PackageState> mTestPackageStates = new ArrayMap<>();
 
@@ -206,6 +208,7 @@
         roleManagerForMock = mock(RoleManagerForMock.class);
         subscriptionManager = mock(SubscriptionManager.class);
         supervisionManagerInternal = mock(SupervisionManagerInternal.class);
+        euiccManager = mock(EuiccManager.class);
 
         // Package manager is huge, so we use a partial mock instead.
         packageManager = spy(realContext.getPackageManager());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
index 0a696ef..b01895a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
@@ -27,7 +27,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index b0ffebb..aacef3f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -341,18 +341,9 @@
         HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
                 ADDR_TV,
                 ADDR_AUDIO_SYSTEM);
-        // <Report ARC Initiated> should only be sent after SAD querying is done
-        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
-
-        // Finish querying SADs
-        for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
-            assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
-            mNativeWrapper.clearResultMessages();
-            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
-            mTestLooper.dispatchAll();
-        }
-
         assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
+        // But we need to check SADs started to be queried at this time
+        assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
         mNativeWrapper.clearResultMessages();
     }
 
@@ -752,17 +743,6 @@
         HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
                 ADDR_TV,
                 ADDR_AUDIO_SYSTEM);
-        // <Report ARC Initiated> should only be sent after SAD querying is done
-        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
-
-        // Finish querying SADs
-        for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
-            assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
-            mNativeWrapper.clearResultMessages();
-            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
-            mTestLooper.dispatchAll();
-        }
-
         assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
     }
 
@@ -1067,16 +1047,6 @@
         HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
             ADDR_TV,
             ADDR_AUDIO_SYSTEM);
-        // <Report ARC Initiated> should only be sent after SAD querying is done
-        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
-        // Finish querying SADs
-        for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
-            assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
-            mNativeWrapper.clearResultMessages();
-            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
-            mTestLooper.dispatchAll();
-        }
-
         assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
         mNativeWrapper.clearResultMessages();
 
@@ -1268,16 +1238,6 @@
 
         mNativeWrapper.onCecMessage(initiateArc);
         mTestLooper.dispatchAll();
-
-        // Finish querying SADs
-        for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
-            assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
-            mNativeWrapper.clearResultMessages();
-            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
-            mTestLooper.dispatchAll();
-        }
-
-        // ARC should be established after RequestSadAction is finished
         assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
 
         mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
@@ -1421,17 +1381,6 @@
         HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
                 ADDR_TV,
                 ADDR_AUDIO_SYSTEM);
-        // <Report ARC Initiated> should only be sent after SAD querying is done
-        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
-
-        // Finish querying SADs
-        for (int i = 0; i <= RETRY_COUNTER_MAX; ++i) {
-            assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
-            mNativeWrapper.clearResultMessages();
-            mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
-            mTestLooper.dispatchAll();
-        }
-
         assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
     }
 
@@ -2295,6 +2244,38 @@
                 .hasSize(1);
     }
 
+    @Test
+    public void onOneTouchPlay_wakeUp_exist_device() {
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
+
+        // Go to standby to trigger RequestActiveSourceAction for playback_1
+        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+        mTestLooper.dispatchAll();
+
+        // Skip the LauncherX API timeout.
+        mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_TV_ASSERT_ACTIVE_SOURCE_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mNativeWrapper.clearResultMessages();
+
+        // turn off TV and wake up with one touch play
+        mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
+        // FakePowerManagerWrapper#wakeUp() doesn't broadcast Intent.ACTION_SCREEN_ON
+        // manually trigger onWakeUp to mock OTP
+        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestActiveSource);
+    }
+
 
     @Test
     public void handleReportAudioStatus_SamOnAvrStandby_startSystemAudioActionFromTv() {
diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
index e20f1e7..a39f071 100644
--- a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
@@ -31,7 +31,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.app.ActivityManagerInternal;
 import android.content.Context;
@@ -226,7 +226,7 @@
 
         assertTrue(!mSystemAppUpdateTracker.getUpdatedApps().contains(DEFAULT_PACKAGE_NAME_2));
         // getApplicationLocales should be never be invoked if not a system app.
-        verifyZeroInteractions(mMockActivityTaskManager);
+        verifyNoMoreInteractions(mMockActivityTaskManager);
         // Broadcast should be never sent if not a system app.
         verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
         // It shouldn't write to the file if not a system app.
@@ -244,7 +244,7 @@
                 Binder.getCallingUid());
 
         // getApplicationLocales should be never be invoked if not installer is not present.
-        verifyZeroInteractions(mMockActivityTaskManager);
+        verifyNoMoreInteractions(mMockActivityTaskManager);
         // Broadcast should be never sent if installer is not present.
         verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
         // It shouldn't write to file if no installer present.
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
index 565a9b6..43b1ec3 100644
--- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -67,12 +68,15 @@
     private static final int SESSION_ID_RANGE = ContextHubEndpointManager.SERVICE_SESSION_RANGE;
     private static final int MIN_SESSION_ID = 0;
     private static final int MAX_SESSION_ID = MIN_SESSION_ID + SESSION_ID_RANGE - 1;
+    private static final int SESSION_ID_FOR_OPEN_REQUEST = MAX_SESSION_ID + 1;
+    private static final int INVALID_SESSION_ID_FOR_OPEN_REQUEST = MIN_SESSION_ID + 1;
 
     private static final String ENDPOINT_NAME = "Example test endpoint";
     private static final int ENDPOINT_ID = 1;
     private static final String ENDPOINT_PACKAGE_NAME = "com.android.server.location.contexthub";
 
     private static final String TARGET_ENDPOINT_NAME = "Example target endpoint";
+    private static final String ENDPOINT_SERVICE_DESCRIPTOR = "serviceDescriptor";
     private static final int TARGET_ENDPOINT_ID = 1;
 
     private static final int SAMPLE_MESSAGE_TYPE = 1234;
@@ -80,6 +84,10 @@
             new HubMessage.Builder(SAMPLE_MESSAGE_TYPE, new byte[] {1, 2, 3, 4, 5})
                     .setResponseRequired(true)
                     .build();
+    private static final HubMessage SAMPLE_UNRELIABLE_MESSAGE =
+            new HubMessage.Builder(SAMPLE_MESSAGE_TYPE, new byte[] {1, 2, 3, 4, 5})
+                    .setResponseRequired(false)
+                    .build();
 
     private ContextHubClientManager mClientManager;
     private ContextHubEndpointManager mEndpointManager;
@@ -221,6 +229,105 @@
     }
 
     @Test
+    public void testEndpointSessionOpenRequest() throws RemoteException {
+        assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+        IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+        HubEndpointInfo targetInfo =
+                new HubEndpointInfo(
+                        TARGET_ENDPOINT_NAME,
+                        TARGET_ENDPOINT_ID,
+                        ENDPOINT_PACKAGE_NAME,
+                        Collections.emptyList());
+        mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+        mEndpointManager.onEndpointSessionOpenRequest(
+                SESSION_ID_FOR_OPEN_REQUEST,
+                endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+                targetInfo.getIdentifier(),
+                ENDPOINT_SERVICE_DESCRIPTOR);
+
+        verify(mMockCallback)
+                .onSessionOpenRequest(
+                        SESSION_ID_FOR_OPEN_REQUEST, targetInfo, ENDPOINT_SERVICE_DESCRIPTOR);
+
+        // Accept
+        endpoint.openSessionRequestComplete(SESSION_ID_FOR_OPEN_REQUEST);
+        verify(mMockEndpointCommunications)
+                .endpointSessionOpenComplete(SESSION_ID_FOR_OPEN_REQUEST);
+
+        unregisterExampleEndpoint(endpoint);
+    }
+
+    @Test
+    public void testEndpointSessionOpenRequestWithInvalidSessionId() throws RemoteException {
+        assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+        IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+        HubEndpointInfo targetInfo =
+                new HubEndpointInfo(
+                        TARGET_ENDPOINT_NAME,
+                        TARGET_ENDPOINT_ID,
+                        ENDPOINT_PACKAGE_NAME,
+                        Collections.emptyList());
+        mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+        mEndpointManager.onEndpointSessionOpenRequest(
+                INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+                endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+                targetInfo.getIdentifier(),
+                ENDPOINT_SERVICE_DESCRIPTOR);
+        verify(mMockEndpointCommunications)
+                .closeEndpointSession(
+                        INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+                        Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
+        verify(mMockCallback, never())
+                .onSessionOpenRequest(
+                        INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+                        targetInfo,
+                        ENDPOINT_SERVICE_DESCRIPTOR);
+
+        unregisterExampleEndpoint(endpoint);
+    }
+
+    @Test
+    public void testEndpointSessionOpenRequest_duplicatedSessionId_noopWhenSessionIsActive()
+            throws RemoteException {
+        assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+        IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+        HubEndpointInfo targetInfo =
+                new HubEndpointInfo(
+                        TARGET_ENDPOINT_NAME,
+                        TARGET_ENDPOINT_ID,
+                        ENDPOINT_PACKAGE_NAME,
+                        Collections.emptyList());
+        mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+        mEndpointManager.onEndpointSessionOpenRequest(
+                SESSION_ID_FOR_OPEN_REQUEST,
+                endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+                targetInfo.getIdentifier(),
+                ENDPOINT_SERVICE_DESCRIPTOR);
+        endpoint.openSessionRequestComplete(SESSION_ID_FOR_OPEN_REQUEST);
+        // Now session with id SESSION_ID_FOR_OPEN_REQUEST is active
+
+        // Duplicated session open request
+        mEndpointManager.onEndpointSessionOpenRequest(
+                SESSION_ID_FOR_OPEN_REQUEST,
+                endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+                targetInfo.getIdentifier(),
+                ENDPOINT_SERVICE_DESCRIPTOR);
+
+        // Client API is only invoked once
+        verify(mMockCallback, times(1))
+                .onSessionOpenRequest(
+                        SESSION_ID_FOR_OPEN_REQUEST, targetInfo, ENDPOINT_SERVICE_DESCRIPTOR);
+        // HAL still receives two open complete notifications
+        verify(mMockEndpointCommunications, times(2))
+                .endpointSessionOpenComplete(SESSION_ID_FOR_OPEN_REQUEST);
+
+        unregisterExampleEndpoint(endpoint);
+    }
+
+    @Test
     public void testMessageTransaction() throws RemoteException {
         IContextHubEndpoint endpoint = registerExampleEndpoint();
         testMessageTransactionInternal(endpoint, /* deliverMessageStatus= */ true);
@@ -260,6 +367,24 @@
         unregisterExampleEndpoint(endpoint);
     }
 
+    @Test
+    public void testUnreliableMessage() throws RemoteException {
+        IContextHubEndpoint endpoint = registerExampleEndpoint();
+        int sessionId = openTestSession(endpoint);
+
+        mEndpointManager.onMessageReceived(sessionId, SAMPLE_UNRELIABLE_MESSAGE);
+        ArgumentCaptor<HubMessage> messageCaptor = ArgumentCaptor.forClass(HubMessage.class);
+        verify(mMockCallback).onMessageReceived(eq(sessionId), messageCaptor.capture());
+        assertThat(messageCaptor.getValue()).isEqualTo(SAMPLE_UNRELIABLE_MESSAGE);
+
+        // Confirm we can send another message
+        mEndpointManager.onMessageReceived(sessionId, SAMPLE_UNRELIABLE_MESSAGE);
+        verify(mMockCallback, times(2)).onMessageReceived(eq(sessionId), messageCaptor.capture());
+        assertThat(messageCaptor.getValue()).isEqualTo(SAMPLE_UNRELIABLE_MESSAGE);
+
+        unregisterExampleEndpoint(endpoint);
+    }
+
     /** A helper method to create a session and validates reliable message sending. */
     private void testMessageTransactionInternal(
             IContextHubEndpoint endpoint, boolean deliverMessageStatus) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 2868e55..125791a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -18,7 +18,6 @@
 
 import static android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION;
 import static android.security.Flags.FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL;
-import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS;
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
@@ -558,7 +557,6 @@
     @Test
     public void testVerifyCredential_notifyLockSettingsStateListeners_whenGoodPassword()
             throws Exception {
-        mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS);
         final LockscreenCredential password = newPassword("password");
         setCredential(PRIMARY_USER_ID, password);
         final LockSettingsStateListener listener = mock(LockSettingsStateListener.class);
@@ -574,7 +572,6 @@
     @Test
     public void testVerifyCredential_notifyLockSettingsStateListeners_whenBadPassword()
             throws Exception {
-        mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS);
         final LockscreenCredential password = newPassword("password");
         setCredential(PRIMARY_USER_ID, password);
         final LockscreenCredential badPassword = newPassword("badPassword");
@@ -590,7 +587,6 @@
 
     @Test
     public void testLockSettingsStateListener_registeredThenUnregistered() throws Exception {
-        mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS);
         final LockscreenCredential password = newPassword("password");
         setCredential(PRIMARY_USER_ID, password);
         final LockscreenCredential badPassword = newPassword("badPassword");
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 2da2f50..e836780 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.locksettings;
 
+import static android.content.pm.UserInfo.FLAG_FOR_TESTING;
 import static android.content.pm.UserInfo.FLAG_FULL;
 import static android.content.pm.UserInfo.FLAG_MAIN;
 import static android.content.pm.UserInfo.FLAG_PRIMARY;
@@ -44,6 +45,8 @@
 
 import android.app.PropertyInvalidatedCache;
 import android.app.admin.PasswordMetrics;
+import android.content.ComponentName;
+import android.content.pm.UserInfo;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
@@ -357,6 +360,45 @@
     }
 
     @Test
+    public void testEscrowDataRetainedWhenManagedUserVerifiesCredential() throws RemoteException {
+        when(mDeviceStateCache.isUserOrganizationManaged(anyInt())).thenReturn(true);
+
+        LockscreenCredential password = newPassword("password");
+        initSpAndSetCredential(PRIMARY_USER_ID, password);
+
+        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */);
+
+        assertTrue("Escrow data was destroyed", mSpManager.hasEscrowData(PRIMARY_USER_ID));
+    }
+
+    @Test
+    public void testEscrowDataRetainedWhenUnmanagedTestUserVerifiesCredential()
+            throws RemoteException {
+        when(mDeviceStateCache.isUserOrganizationManaged(anyInt())).thenReturn(false);
+        UserInfo userInfo = mUserManagerInternal.getUserInfo(PRIMARY_USER_ID);
+        userInfo.flags |= FLAG_FOR_TESTING;
+
+        LockscreenCredential password = newPassword("password");
+        initSpAndSetCredential(PRIMARY_USER_ID, password);
+
+        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */);
+
+        assertTrue("Escrow data was destroyed", mSpManager.hasEscrowData(PRIMARY_USER_ID));
+    }
+
+    @Test
+    public void testEscrowDataDeletedWhenUnmanagedUserVerifiesCredential() throws RemoteException {
+        when(mDeviceStateCache.isUserOrganizationManaged(anyInt())).thenReturn(false);
+
+        LockscreenCredential password = newPassword("password");
+        initSpAndSetCredential(PRIMARY_USER_ID, password);
+
+        mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */);
+
+        assertFalse("Escrow data wasn't destroyed", mSpManager.hasAnyEscrowData(PRIMARY_USER_ID));
+    }
+
+    @Test
     public void testTokenBasedClearPassword() throws RemoteException {
         LockscreenCredential password = newPassword("password");
         LockscreenCredential pattern = newPattern("123654");
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index a58a9cd..4a05ea6 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -48,7 +48,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
@@ -488,7 +488,7 @@
 
         projection.stop(StopReason.STOP_UNKNOWN);
 
-        verifyZeroInteractions(mMediaProjectionMetricsLogger);
+        verifyNoMoreInteractions(mMediaProjectionMetricsLogger);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
index 570256b..a46fc22 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
@@ -35,7 +35,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index da14e45..03ba3b6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -25,11 +25,11 @@
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
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 bbc2cb2..242ebc5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -59,10 +59,10 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.waitOnMainThread;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+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.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index f5690b7..46c532b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -23,9 +23,9 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.parceled;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
 
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index 19c26f0b..88395a4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -21,10 +21,10 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isNull;
-import static org.mockito.Matchers.notNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
index ee1bf38..7ea33ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
@@ -18,8 +18,8 @@
 
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index f994660..b842d3a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -389,6 +389,44 @@
     }
 
     @Test
+    public void testSupervisingProfile() throws Exception {
+        assumeTrue("Device doesn't support supervising profiles ",
+                mUserManager.isUserTypeEnabled(UserManager.USER_TYPE_PROFILE_SUPERVISING));
+
+        final UserTypeDetails userTypeDetails =
+                UserTypeFactory.getUserTypes().get(UserManager.USER_TYPE_PROFILE_SUPERVISING);
+        assertWithMessage("No supervising user type on device").that(userTypeDetails).isNotNull();
+
+
+        // Create supervising profile if it doesn't exist
+        UserInfo supervisingUser = getSupervisingProfile();
+        if (supervisingUser == null) {
+            supervisingUser = createUser("Supervising",
+                    UserManager.USER_TYPE_PROFILE_SUPERVISING, /*flags*/ 0);
+        }
+        assertWithMessage("Couldn't create supervising profile").that(supervisingUser).isNotNull();
+        UserHandle supervisingHandle = supervisingUser.getUserHandle();
+
+        // Test that only one supervising profile can be created
+        final UserInfo secondSupervisingProfile =
+                createUser("Supervising", UserManager.USER_TYPE_PROFILE_SUPERVISING,
+                        /*flags*/ 0);
+        assertThat(secondSupervisingProfile).isNull();
+
+        // Verify that the supervising profile doesn't have a parent
+        assertThat(mUserManager.getProfileParent(supervisingHandle.getIdentifier())).isNull();
+
+        // Make sure that the supervising profile can be started in the background, and that it
+        // is visible
+        final boolean isStarted = mActivityManager.startProfile(supervisingHandle);
+        assertWithMessage("Unable to start supervising profile").that(isStarted).isTrue();
+        final UserManager umSupervising = (UserManager) mContext.createPackageContextAsUser(
+                "android", 0, supervisingHandle).getSystemService(Context.USER_SERVICE);
+        assertWithMessage("Supervising profile not visible").that(
+                umSupervising.isUserVisible()).isTrue();
+    }
+
+    @Test
     public void testGetProfileAccessibilityString_throwsExceptionForNonProfileUser() {
         UserInfo user1 = createUser("Guest 1", UserInfo.FLAG_GUEST);
         assertThat(user1).isNotNull();
@@ -2198,4 +2236,13 @@
         assertEquals(actual.getLevel(), expected.getLevel());
     }
 
+    @Nullable
+    private UserInfo getSupervisingProfile() {
+        for (UserInfo user : mUserManager.getUsers()) {
+            if (user.userType.equals(UserManager.USER_TYPE_PROFILE_SUPERVISING)) {
+                return user;
+            }
+        }
+        return null;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
index d55f967..2ed2704 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java
@@ -23,7 +23,7 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.pm.ApplicationInfo;
@@ -273,7 +273,7 @@
 
         assertThat(mMessagesForUid).isEmpty();
         assertThat(mWriteTriggered).isFalse();
-        verifyZeroInteractions(mPM);
+        verifyNoMoreInteractions(mPM);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
index 3551af8..5e5be12 100644
--- a/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
@@ -17,10 +17,10 @@
 package com.android.server.pm.permission;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+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.when;
 import static org.testng.Assert.assertThrows;
 
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 2a4c3fd..64f8819 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -21,8 +21,8 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
index c7a06b8..339bac4 100644
--- a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
@@ -259,7 +259,7 @@
 
         AdvancedProtectionProvider provider = new AdvancedProtectionProvider() {
             @Override
-            public List<AdvancedProtectionFeature> getFeatures() {
+            public List<AdvancedProtectionFeature> getFeatures(Context context) {
                 return List.of(feature2);
             }
         };
@@ -291,7 +291,7 @@
 
         AdvancedProtectionProvider provider = new AdvancedProtectionProvider() {
             @Override
-            public List<AdvancedProtectionFeature> getFeatures() {
+            public List<AdvancedProtectionFeature> getFeatures(Context context) {
                 return List.of(feature2);
             }
         };
diff --git a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
index b76e0bc..c107bd4 100644
--- a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java
@@ -18,7 +18,6 @@
 
 import static android.adaptiveauth.Flags.FLAG_ENABLE_ADAPTIVE_AUTH;
 import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS;
-import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS;
 import static android.security.authenticationpolicy.AuthenticationPolicyManager.ERROR_UNSUPPORTED;
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
@@ -112,7 +111,6 @@
         MockitoAnnotations.initMocks(this);
 
         mSetFlagsRule.enableFlags(FLAG_ENABLE_ADAPTIVE_AUTH);
-        mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS);
         mSetFlagsRule.enableFlags(FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS);
 
         mContext = spy(ApplicationProvider.getApplicationContext());
diff --git a/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java b/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
index 3cdf109..1b93d4a 100644
--- a/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
@@ -48,10 +48,10 @@
 import java.util.concurrent.TimeUnit;
 
 import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+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.doAnswer;
 import static org.mockito.Mockito.when;
 
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
index b647b99..4a97b46 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
@@ -18,11 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.isNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 9a7abd4..15a60dc 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -62,9 +62,9 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.intThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.intThat;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
diff --git a/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java b/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java
index 4eb2474..770dfe3 100644
--- a/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java
@@ -20,8 +20,8 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertSame;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.same;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.verify;
 
 import android.platform.test.annotations.Presubmit;
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index bf99b6a..3dcd1d7 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -43,7 +43,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentMatcher;
-import org.mockito.Matchers;
+import org.mockito.ArgumentMatchers;
 import org.mockito.Mockito;
 
 import java.util.concurrent.CountDownLatch;
@@ -340,7 +340,7 @@
         runWebViewBootPreparationOnMainSync();
 
         Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
-                Matchers.anyObject());
+                ArgumentMatchers.any());
 
         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
         assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
@@ -378,7 +378,7 @@
         runWebViewBootPreparationOnMainSync();
 
         Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
-                Matchers.anyObject());
+                ArgumentMatchers.any());
 
         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
         assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
@@ -822,7 +822,7 @@
         checkPreparationPhasesForPackage(firstPackage, 1);
 
         Mockito.verify(mTestSystemImpl, Mockito.never()).killPackageDependents(
-                Mockito.anyObject());
+                Mockito.any());
     }
 
     @Test
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 4c0361d..cdc28a1 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -25,10 +25,10 @@
 import static junit.framework.Assert.fail;
 
 import static org.junit.Assert.assertNotEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyList;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
diff --git a/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
index c8afb78..630a7e4 100644
--- a/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
+++ b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
@@ -23,7 +23,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.AlarmManager;
@@ -126,7 +126,7 @@
         locationListener.onLocationChanged(location);
 
         verify(mMockLocationManager).removeUpdates(locationListener);
-        verifyZeroInteractions(mMockTimeDetectorInternal);
+        verifyNoMoreInteractions(mMockTimeDetectorInternal);
         verify(mMockAlarmManager).set(
                 eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
                 anyLong(),
@@ -150,7 +150,7 @@
 
         // Verify the service returned to location listening.
         verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any());
-        verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
+        verifyNoMoreInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
     }
 
     // Tests what happens when a call is made to startGnssListeningInternal() when service is
@@ -172,7 +172,7 @@
         // listening again.
         verify(mMockAlarmManager).cancel(alarmListenerCaptor.getValue());
         verify(mMockLocationManager).requestLocationUpdates(any(), any(), any(), any());
-        verifyZeroInteractions(mMockTimeDetectorInternal);
+        verifyNoMoreInteractions(mMockTimeDetectorInternal);
     }
 
     private void advanceServiceToSleepingState(
@@ -190,7 +190,7 @@
                 any(), any(), any(), locationListenerCaptor.capture());
         LocationListener locationListener = locationListenerCaptor.getValue();
         Location location = new Location(LocationManager.GPS_PROVIDER);
-        verifyZeroInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
+        verifyNoMoreInteractions(mMockAlarmManager, mMockTimeDetectorInternal);
 
         locationListener.onLocationChanged(location);
 
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 66d7611..d34d74d 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -11,13 +11,8 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-android_test {
-    name: "FrameworksUiServicesTests",
-
-    // Include test java files
-    srcs: [
-        "src/**/*.java",
-    ],
+java_defaults {
+    name: "FrameworksUiServicesTests-defaults",
 
     static_libs: [
         "compatibility-device-util-axt-minus-dexmaker",
@@ -95,12 +90,72 @@
     javacflags: ["-parameters"],
 }
 
-test_module_config {
-    name: "FrameworksUiServicesTests_notification",
-    base: "FrameworksUiServicesTests",
-    test_suites: [
-        "automotive-tests",
-        "device-tests",
+// Utility files used by multiple tests
+filegroup {
+    name: "shared-srcs",
+    srcs: [
+        "src/android/app/ExampleActivity.java",
+        "src/android/app/NotificationSystemUtil.java",
+        "src/com/android/frameworks/tests/uiservices/DummyProvider.java",
+        "src/com/android/internal/logging/InstanceIdSequenceFake.java",
+        "src/com/android/server/UiServiceTestCase.java",
+        "src/com/android/server/notification/ZenChangeOrigin.java",
+        "src/com/android/server/notification/ZenModeEventLoggerFake.java",
     ],
-    exclude_annotations: ["androidx.test.filters.LargeTest"],
+    visibility: ["//visibility:private"],
+}
+
+filegroup {
+    name: "notification-srcs",
+    srcs: [
+        "src/**/Notification*.java",
+        "src/com/android/server/notification/*.java",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+filegroup {
+    name: "notification-zen-srcs",
+    srcs: [
+        "src/android/app/NotificationManagerZenTest.java",
+        "src/com/android/server/notification/Zen*Test.java",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+android_test {
+    name: "FrameworksUiServicesTests",
+
+    // Include test java files but not the notification & zen ones which are separated
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    exclude_srcs: [
+        ":notification-srcs",
+        ":notification-zen-srcs",
+    ],
+
+    defaults: ["FrameworksUiServicesTests-defaults"],
+}
+
+android_test {
+    name: "FrameworksUiServicesNotificationTests",
+    srcs: [
+        ":notification-srcs",
+        ":shared-srcs",
+    ],
+    exclude_srcs: [":notification-zen-srcs"],
+    defaults: ["FrameworksUiServicesTests-defaults"],
+    test_config: "notification-tests.xml",
+}
+
+android_test {
+    name: "FrameworksUiServicesZenTests",
+    srcs: [
+        ":notification-zen-srcs",
+        ":shared-srcs",
+    ],
+    defaults: ["FrameworksUiServicesTests-defaults"],
+    test_config: "notification-zen-tests.xml",
 }
diff --git a/services/tests/uiservicestests/AndroidTest.xml b/services/tests/uiservicestests/AndroidTest.xml
index 11e8f09..93c8c72 100644
--- a/services/tests/uiservicestests/AndroidTest.xml
+++ b/services/tests/uiservicestests/AndroidTest.xml
@@ -15,6 +15,7 @@
 -->
 <configuration description="Runs Frameworks UI Services Tests.">
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="FrameworksUiServicesTests.apk" />
     </target_preparer>
 
diff --git a/services/tests/uiservicestests/notification-tests.xml b/services/tests/uiservicestests/notification-tests.xml
new file mode 100644
index 0000000..acfd844
--- /dev/null
+++ b/services/tests/uiservicestests/notification-tests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Frameworks UI Services Tests (notifications subset).">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="FrameworksUiServicesNotificationTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="FrameworksUiServicesNotificationTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.tests.uiservices" />
+        <option name="runner" value="android.testing.TestableInstrumentation" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/uiservicestests/notification-zen-tests.xml b/services/tests/uiservicestests/notification-zen-tests.xml
new file mode 100644
index 0000000..01d8aab
--- /dev/null
+++ b/services/tests/uiservicestests/notification-zen-tests.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Frameworks UI Services Tests (zen mode subset).">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="FrameworksUiServicesZenTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="FrameworksUiServicesZenTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.tests.uiservices" />
+        <option name="runner" value="android.testing.TestableInstrumentation" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 9930c9f..7b1ce44 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -62,7 +62,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
@@ -992,7 +991,7 @@
                 () -> mService.requestProjection(mBinder, PROJECTION_TYPE_NONE, PACKAGE_NAME));
         verify(mContext, never()).enforceCallingPermission(
                 eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
-        verifyZeroInteractions(mBinder);
+        verifyNoMoreInteractions(mBinder);
         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
     }
 
@@ -1008,7 +1007,7 @@
                 () -> mService.requestProjection(mBinder, multipleProjectionTypes, PACKAGE_NAME));
         verify(mContext, never()).enforceCallingPermission(
                 eq(Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION), any());
-        verifyZeroInteractions(mBinder);
+        verifyNoMoreInteractions(mBinder);
         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
index 6b989cb..b332331 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -38,7 +38,6 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.Condition;
 
-import com.android.internal.R;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Before;
@@ -47,8 +46,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.List;
-
 public class ConditionProvidersTest extends UiServiceTestCase {
 
     private ConditionProviders mProviders;
@@ -172,15 +169,4 @@
 
         assertTrue(mProviders.getApproved(userId, true).isEmpty());
     }
-
-    @Test
-    public void getDefaultDndAccessPackages_returnsPackages() {
-        mContext.getOrCreateTestableResources().addOverride(
-                R.string.config_defaultDndAccessPackages,
-                "com.example.a:com.example.b::::com.example.c");
-
-        List<String> packages = ConditionProviders.getDefaultDndAccessPackages(mContext);
-
-        assertThat(packages).containsExactly("com.example.a", "com.example.b", "com.example.c");
-    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 5ce9a3e..8023bdd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -36,7 +36,6 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.KeyguardManager;
@@ -184,7 +183,7 @@
         mApplier.apply(noEffects, ORIGIN_USER_IN_SYSTEMUI);
 
         verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(false));
-        verifyZeroInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
+        verifyNoMoreInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
     }
 
     @Test
@@ -252,8 +251,8 @@
         // Wallpaper dimming was undone, Grayscale was applied, nothing else was touched.
         verify(mWallpaperManager).setWallpaperDimAmount(eq(0.0f));
         verify(mColorDisplayManager).setSaturationLevel(eq(0));
-        verifyZeroInteractions(mPowerManager);
-        verifyZeroInteractions(mUiModeManager);
+        verifyNoMoreInteractions(mPowerManager);
+        verifyNoMoreInteractions(mUiModeManager);
     }
 
     @Test
@@ -269,7 +268,7 @@
                 ORIGIN_APP);
 
         // Effect was not yet applied, but a broadcast receiver was registered.
-        verifyZeroInteractions(mUiModeManager);
+        verifyNoMoreInteractions(mUiModeManager);
         verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(),
                 intentFilterCaptor.capture(), anyInt());
         assertThat(intentFilterCaptor.getValue().getAction(0)).isEqualTo(Intent.ACTION_SCREEN_OFF);
@@ -337,7 +336,7 @@
                 origin.value());
 
         // Effect was not applied, will be on next screen-off.
-        verifyZeroInteractions(mUiModeManager);
+        verifyNoMoreInteractions(mUiModeManager);
         verify(mContext).registerReceiver(any(),
                 argThat(filter -> Intent.ACTION_SCREEN_OFF.equals(filter.getAction(0))),
                 anyInt());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 4eac1d1..1114365 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -50,9 +50,9 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -60,7 +60,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.annotation.SuppressLint;
@@ -102,7 +102,9 @@
 import platform.test.runner.parameterized.Parameters;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 @SmallTest
 @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the class.
@@ -311,7 +313,7 @@
                 getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
                 false);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -325,7 +327,7 @@
         }
         mGroupHelper.onNotificationPosted(
             getNotificationRecord(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -338,7 +340,7 @@
         }
         mGroupHelper.onNotificationPosted(
             getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.of(7)), false);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -351,7 +353,7 @@
         mGroupHelper.onNotificationPosted(
             getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a", false),
             false);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1742,7 +1744,7 @@
             notificationList.add(r);
             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1757,7 +1759,7 @@
             notificationList.add(r);
             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1773,7 +1775,7 @@
             notificationList.add(r);
             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1789,7 +1791,7 @@
             notificationList.add(r);
             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1809,7 +1811,7 @@
                 String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, "testGrp", true);
         notificationList.add(r);
         mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1828,7 +1830,7 @@
                 String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.of(7), "testGrp", true);
         notificationList.add(r);
         mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1851,7 +1853,7 @@
             String.valueOf(AUTOGROUP_AT_COUNT + 1), UserHandle.SYSTEM, "testGrp", false);
         notificationList.add(child);
         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -1875,7 +1877,7 @@
         notificationList.add(child);
         summaryByGroup.put(summary.getGroupKey(), summary);
         mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -2207,7 +2209,7 @@
             childrenToRemove.add(child);
         }
         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
 
         // Remove all child notifications from the valid group => summary without children
         Mockito.reset(mCallback);
@@ -2271,7 +2273,7 @@
             }
         }
         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
 
         // Remove some child notifications from the valid group, transform into a singleton group
         Mockito.reset(mCallback);
@@ -2327,7 +2329,7 @@
             notificationList.add(child);
         }
         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
 
         // Remove all child notifications from the valid group => summary without children
         Mockito.reset(mCallback);
@@ -2341,7 +2343,7 @@
         mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList,
                 summaryByGroup);
         // Check that nothing was force grouped
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -3835,7 +3837,7 @@
             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
         }
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
 
@@ -3859,7 +3861,7 @@
             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
         }
         // FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS is disabled => don't force group
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     @Test
@@ -4443,4 +4445,97 @@
         verify(mCallback).sendAppProvidedSummaryDeleteIntent(eq(pkg),
                 eq(deleteIntentofFirstSummary));
     }
+
+    @Test
+    @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
+    public void testGroupSummaryAdded_hadUngroupedNotif_doesNotAutogroup() {
+        // Scenario:
+        //  * child notification posted before summary; added to ungrouped notifications
+        //  * summary posted, so now the child has a group summary and is no longer "ungrouped
+        //  * another ungrouped notification is posted
+        // Confirm that the first notification (that now has a summary) is not autogrouped.
+
+        // Bookkeeping items
+        List<NotificationRecord> notifList = new ArrayList<>();
+        Map<String, NotificationRecord> summaryByGroupKey = new HashMap<>();
+
+        // Setup: post AUTOGROUP_AT_COUNT - 2 notifications so that the next notification would not
+        // trigger autogrouping, but the one after that would
+        for (int i = 0; i < AUTOGROUP_AT_COUNT - 2; i++) {
+            NotificationRecord child = getNotificationRecord(mPkg, i, "", mUser, "group" + i, false,
+                    IMPORTANCE_DEFAULT);
+            notifList.add(child);
+            mGroupHelper.onNotificationPostedWithDelay(child, notifList, summaryByGroupKey);
+        }
+
+        // Group child: posted enough before its associated summary to be put in the "ungrouped"
+        // set of notifications
+        NotificationRecord groupChild = getNotificationRecord(mPkg, AUTOGROUP_AT_COUNT - 2, "",
+                mUser, "specialGroup", false, IMPORTANCE_DEFAULT);
+        notifList.add(groupChild);
+        mGroupHelper.onNotificationPostedWithDelay(groupChild, notifList, summaryByGroupKey);
+
+        // Group summary: posted after child 1
+        NotificationRecord groupSummary = getNotificationRecord(mPkg, AUTOGROUP_AT_COUNT - 1, "",
+                mUser, "specialGroup", true, IMPORTANCE_DEFAULT);
+        notifList.add(groupSummary);
+        summaryByGroupKey.put(groupSummary.getSbn().getGroupKey(), groupSummary);
+        mGroupHelper.onGroupSummaryAdded(groupSummary, notifList);
+        mGroupHelper.onNotificationPostedWithDelay(groupSummary, notifList, summaryByGroupKey);
+
+        // One more notification posted to the group; because its summary already exists, it should
+        // never be counted as an "ungrouped" notification
+        NotificationRecord groupChild2 = getNotificationRecord(mPkg, AUTOGROUP_AT_COUNT, "",
+                mUser, "specialGroup", false, IMPORTANCE_DEFAULT);
+        notifList.add(groupChild2);
+        mGroupHelper.onNotificationPostedWithDelay(groupChild2, notifList, summaryByGroupKey);
+
+        // Now one more ungrouped notification; this would have put the number of "ungrouped"
+        // notifications above the limit if the first groupChild notification were left ungrouped
+        NotificationRecord extra = getNotificationRecord(mPkg, AUTOGROUP_AT_COUNT + 1, "", mUser,
+                "yetAnotherGroup", false, IMPORTANCE_DEFAULT);
+        notifList.add(extra);
+        mGroupHelper.onNotificationPostedWithDelay(extra, notifList, summaryByGroupKey);
+
+        // no autogrouping should have occurred
+        verifyNoMoreInteractions(mCallback);
+    }
+
+    @Test
+    @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
+    public void testGroupSummaryAdded_onlyUnrelatedGroupedNotifs() {
+        // If all of the existing ungrouped notifications have nothing to do with the summary
+        // they should still get grouped as needed.
+        List<NotificationRecord> notifList = new ArrayList<>();
+        Map<String, NotificationRecord> summaryByGroupKey = new HashMap<>();
+
+        // Post 1 fewer than the autogroupable notifications, each associated with a different
+        // group without a summary.
+        for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
+            NotificationRecord child = getNotificationRecord(mPkg, i, "", mUser, "group" + i, false,
+                    IMPORTANCE_DEFAULT);
+            notifList.add(child);
+            mGroupHelper.onNotificationPostedWithDelay(child, notifList, summaryByGroupKey);
+        }
+
+        // At this point we do not yet expect autogrouping.
+        // Add a group summary that is a summary associated with none of the above notifications.
+        // Because this gets considered a "summary without children", all of these notifications
+        // should now be autogrouped.
+        NotificationRecord summary = getNotificationRecord(mPkg, AUTOGROUP_AT_COUNT, "", mUser,
+                "summaryGroup", true, IMPORTANCE_DEFAULT);
+        notifList.add(summary);
+        summaryByGroupKey.put(summary.getSbn().getKey(), summary);
+        mGroupHelper.onGroupSummaryAdded(summary, notifList);
+        mGroupHelper.onNotificationPostedWithDelay(summary, notifList, summaryByGroupKey);
+
+        // all of the above posted notifications should be autogrouped
+        String expectedGroupKey = getExpectedAutogroupKey(
+                getNotificationRecord(mPkg, 0, String.valueOf(0), mUser));
+        verify(mCallback, times(1)).addAutoGroupSummary(
+                anyInt(), eq(mPkg), anyString(), eq(expectedGroupKey),
+                anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
+        verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+                eq(expectedGroupKey), anyBoolean());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 98440ec..9c85b04 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -40,10 +40,10 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 076e3e9..aef3d30 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -36,8 +36,8 @@
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index bc01fc4..8fbd3bd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -48,7 +48,6 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -1114,8 +1113,8 @@
         verifyDelayedVibrate(
             mAttentionHelper.getVibratorHelper().createFallbackVibration(
                 /* insistent= */ false));
-        verify(mRingtonePlayer, never()).playAsync(anyObject(), anyObject(), anyBoolean(),
-            anyObject(), anyFloat());
+        verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(),
+            any(), anyFloat());
         assertTrue(r.isInterruptive());
         assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index 4101192..8de2b9c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -32,9 +32,9 @@
 import static junit.framework.Assert.assertNull;
 
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 5aecac2..9e8023f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -23,9 +23,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+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.spy;
 import static org.mockito.Mockito.verify;
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 dd5c601..902171d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -168,10 +168,10 @@
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.atLeastOnce;
@@ -16844,22 +16844,22 @@
     public void updateAutomaticZenRule_implicitRuleWithoutCPS_disallowedFromApp() throws Exception {
         setUpRealZenTest();
         mService.setCallerIsNormalPackage();
-        assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
+        assertThat(mBinderService.getAutomaticZenRules().getList()).isEmpty();
 
         // Create an implicit zen rule by calling setNotificationPolicy from an app.
         mBinderService.setNotificationPolicy(mPkg, new NotificationManager.Policy(0, 0, 0), false);
-        assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
-        Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
-                mBinderService.getAutomaticZenRules().entrySet());
-        assertThat(rule.getValue().getOwner()).isNull();
-        assertThat(rule.getValue().getConfigurationActivity()).isNull();
+        assertThat(mBinderService.getAutomaticZenRules().getList()).hasSize(1);
+        AutomaticZenRule.AzrWithId rule = getOnlyElement(
+                (List<AutomaticZenRule.AzrWithId>) mBinderService.getAutomaticZenRules().getList());
+        assertThat(rule.mRule.getOwner()).isNull();
+        assertThat(rule.mRule.getConfigurationActivity()).isNull();
 
         // Now try to update said rule (e.g. disable it). Should fail.
         // We also validate the exception message because NPE could be thrown by all sorts of test
         // issues (e.g. misconfigured mocks).
-        rule.getValue().setEnabled(false);
+        rule.mRule.setEnabled(false);
         NullPointerException e = assertThrows(NullPointerException.class,
-                () -> mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false));
+                () -> mBinderService.updateAutomaticZenRule(rule.mId, rule.mRule, false));
         assertThat(e.getMessage()).isEqualTo(
                 "Rule must have a ConditionProviderService and/or configuration activity");
     }
@@ -16869,24 +16869,24 @@
     public void updateAutomaticZenRule_implicitRuleWithoutCPS_allowedFromSystem() throws Exception {
         setUpRealZenTest();
         mService.setCallerIsNormalPackage();
-        assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
+        assertThat(mBinderService.getAutomaticZenRules().getList()).isEmpty();
 
         // Create an implicit zen rule by calling setNotificationPolicy from an app.
         mBinderService.setNotificationPolicy(mPkg, new NotificationManager.Policy(0, 0, 0), false);
-        assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
-        Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
-                mBinderService.getAutomaticZenRules().entrySet());
-        assertThat(rule.getValue().getOwner()).isNull();
-        assertThat(rule.getValue().getConfigurationActivity()).isNull();
+        assertThat(mBinderService.getAutomaticZenRules().getList()).hasSize(1);
+        AutomaticZenRule.AzrWithId rule = getOnlyElement(
+                (List<AutomaticZenRule.AzrWithId>) mBinderService.getAutomaticZenRules().getList());
+        assertThat(rule.mRule.getOwner()).isNull();
+        assertThat(rule.mRule.getConfigurationActivity()).isNull();
 
         // Now update said rule from Settings (e.g. disable it). Should work!
         mService.isSystemUid = true;
-        rule.getValue().setEnabled(false);
-        mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false);
+        rule.mRule.setEnabled(false);
+        mBinderService.updateAutomaticZenRule(rule.mId, rule.mRule, false);
 
-        Map.Entry<String, AutomaticZenRule> updatedRule = getOnlyElement(
-                mBinderService.getAutomaticZenRules().entrySet());
-        assertThat(updatedRule.getValue().isEnabled()).isFalse();
+        AutomaticZenRule.AzrWithId updatedRule = getOnlyElement(
+                (List<AutomaticZenRule.AzrWithId>) mBinderService.getAutomaticZenRules().getList());
+        assertThat(updatedRule.mRule.isEnabled()).isFalse();
     }
 
     @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 a02f628..43228f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -96,7 +96,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
@@ -6372,7 +6372,7 @@
         NotificationChannel same = new NotificationChannel("id", "Bah", IMPORTANCE_DEFAULT);
         mHelper.createNotificationChannel(PKG_P, 0, same, true, false, 0, false);
 
-        verifyZeroInteractions(mHandler);
+        verifyNoMoreInteractions(mHandler);
     }
 
     @Test
@@ -6398,7 +6398,7 @@
 
         mHelper.updateNotificationChannel(PKG_P, 0, same, false, 0, false);
 
-        verifyZeroInteractions(mHandler);
+        verifyNoMoreInteractions(mHandler);
     }
 
     @Test
@@ -6412,7 +6412,7 @@
     public void setShowBadge_same_doesNotRequestSort() {
         mHelper.setShowBadge(PKG_P, 0, true); // true == DEFAULT_SHOW_BADGE
 
-        verifyZeroInteractions(mHandler);
+        verifyNoMoreInteractions(mHandler);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index ec428d5..91e4fd6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -25,8 +25,8 @@
 
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 31436c6..62f7471 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -25,7 +25,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index dcd56e0..7f05dde 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -28,9 +28,9 @@
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java
deleted file mode 100644
index 154a905..0000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenConfigTrimmerTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.notification;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.internal.R;
-import com.android.server.UiServiceTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class ZenConfigTrimmerTest extends UiServiceTestCase {
-
-    private static final String TRUSTED_PACKAGE = "com.trust.me";
-    private static final int ONE_PERCENT = 1_500;
-
-    private ZenConfigTrimmer mTrimmer;
-
-    @Before
-    public void setUp() {
-        mContext.getOrCreateTestableResources().addOverride(
-                R.string.config_defaultDndAccessPackages, TRUSTED_PACKAGE);
-
-        mTrimmer = new ZenConfigTrimmer(mContext);
-    }
-
-    @Test
-    public void trimToMaximumSize_belowMax_untouched() {
-        ZenModeConfig config = new ZenModeConfig();
-        addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "4", "pkg2", 20 * ONE_PERCENT);
-        addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
-
-        mTrimmer.trimToMaximumSize(config);
-
-        assertThat(config.automaticRules.keySet()).containsExactly("1", "2", "3", "4", "5");
-    }
-
-    @Test
-    public void trimToMaximumSize_exceedsMax_removesAllRulesOfLargestPackages() {
-        ZenModeConfig config = new ZenModeConfig();
-        addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "4", "pkg2", 20 * ONE_PERCENT);
-        addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
-        addZenRule(config, "6", "pkg3", 35 * ONE_PERCENT);
-        addZenRule(config, "7", "pkg4", 38 * ONE_PERCENT);
-
-        mTrimmer.trimToMaximumSize(config);
-
-        assertThat(config.automaticRules.keySet()).containsExactly("1", "2", "3", "6");
-        assertThat(config.automaticRules.values().stream().map(r -> r.pkg).distinct())
-                .containsExactly("pkg1", "pkg3");
-    }
-
-    @Test
-    public void trimToMaximumSize_keepsRulesFromTrustedPackages() {
-        ZenModeConfig config = new ZenModeConfig();
-        addZenRule(config, "1", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "2", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "3", "pkg1", 10 * ONE_PERCENT);
-        addZenRule(config, "4", TRUSTED_PACKAGE, 60 * ONE_PERCENT);
-        addZenRule(config, "5", "pkg2", 20 * ONE_PERCENT);
-        addZenRule(config, "6", "pkg3", 35 * ONE_PERCENT);
-
-        mTrimmer.trimToMaximumSize(config);
-
-        assertThat(config.automaticRules.keySet()).containsExactly("4", "5");
-        assertThat(config.automaticRules.values().stream().map(r -> r.pkg).distinct())
-                .containsExactly(TRUSTED_PACKAGE, "pkg2");
-    }
-
-    /**
-     * Create a ZenRule that, when serialized to a Parcel, will take <em>approximately</em>
-     * {@code desiredSize} bytes (within 100 bytes). Try to make the tests not rely on a very tight
-     * fit.
-     */
-    private static void addZenRule(ZenModeConfig config, String id, String pkg, int desiredSize) {
-        ZenRule rule = new ZenRule();
-        rule.id = id;
-        rule.pkg = pkg;
-        config.automaticRules.put(id, rule);
-
-        // Make the ZenRule as large as desired. Not to the exact byte, because otherwise this
-        // test would have to be adjusted whenever we change the parceling of ZenRule in any way.
-        // (Still might need adjustment if we change the serialization _significantly_).
-        int nameLength = desiredSize - id.length() - pkg.length() - 232;
-        rule.name = "A".repeat(nameLength);
-
-        Parcel verification = Parcel.obtain();
-        try {
-            verification.writeParcelable(rule, 0);
-            assertThat(verification.dataSize()).isWithin(100).of(desiredSize);
-        } finally {
-            verification.recycle();
-        }
-    }
-}
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 5377102..bfce647 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -90,7 +90,6 @@
 import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
 import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
 import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
-import static com.android.server.notification.Flags.FLAG_LIMIT_ZEN_CONFIG_SIZE;
 import static com.android.server.notification.Flags.FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING;
 import static com.android.server.notification.ZenModeEventLogger.ACTIVE_RULE_TYPE_MANUAL;
 import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
@@ -238,7 +237,6 @@
 @SmallTest
 @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
 @RunWith(ParameterizedAndroidJunit4.class)
-@EnableFlags(FLAG_LIMIT_ZEN_CONFIG_SIZE) // Should be parameterization, but off path does nothing.
 @TestableLooper.RunWithLooper
 public class ZenModeHelperTest extends UiServiceTestCase {
 
@@ -7485,45 +7483,6 @@
         assertThat(getZenRule(ruleId).lastActivation).isNull();
     }
 
-    @Test
-    @EnableFlags(FLAG_LIMIT_ZEN_CONFIG_SIZE)
-    public void addAutomaticZenRule_trimsConfiguration() {
-        mZenModeHelper.mConfig.automaticRules.clear();
-        AutomaticZenRule smallRule = new AutomaticZenRule.Builder("Reasonable", CONDITION_ID)
-                .setConfigurationActivity(new ComponentName(mPkg, "cls"))
-                .build();
-        AutomaticZenRule systemRule = new AutomaticZenRule.Builder("System", CONDITION_ID)
-                .setOwner(new ComponentName("android", "ScheduleConditionProvider"))
-                .build();
-
-        AutomaticZenRule bigRule = new AutomaticZenRule.Builder("Yuge", CONDITION_ID)
-                .setConfigurationActivity(new ComponentName("evil.package", "cls"))
-                .setTriggerDescription("0123456789".repeat(6000)) // ~60k bytes utf16.
-                .build();
-
-        String systemRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "android",
-                systemRule, ORIGIN_SYSTEM, "add", SYSTEM_UID);
-        String smallRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, smallRule,
-                ORIGIN_APP, "add", CUSTOM_PKG_UID);
-        String bigRuleId1 =  mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
-                bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
-                systemRuleId, smallRuleId, bigRuleId1);
-
-        String bigRuleId2 =  mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
-                bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
-                systemRuleId, smallRuleId, bigRuleId1, bigRuleId2);
-
-        // This should go over the threshold
-        String bigRuleId3 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, "evil.package",
-                bigRule, ORIGIN_APP, "add", CUSTOM_PKG_UID);
-
-        // Rules from evil.package are gone.
-        assertThat(mZenModeHelper.mConfig.automaticRules.keySet()).containsExactly(
-                systemRuleId, smallRuleId);
-    }
-
     private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
             @Nullable ZenPolicy zenPolicy) {
         ZenRule rule = new ZenRule();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 04335df..2baf0c1 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -247,6 +247,7 @@
         assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty();
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_singleVibratorWaveform_runsVibrationAndChangesAmplitudes() {
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
@@ -269,7 +270,10 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    @EnableFlags({
+            Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
+            Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING,
+    })
     public void vibrate_singleWaveformWithAdaptiveHapticsScaling_scalesAmplitudesProperly() {
         // No user settings scale.
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
@@ -296,7 +300,10 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+    @EnableFlags({
+            Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
+            Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING,
+    })
     public void vibrate_withVibrationParamsRequestStalling_timeoutRequestAndApplyNoScaling() {
         // No user settings scale.
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
@@ -357,6 +364,7 @@
         }
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_singleVibratorRepeatingShortAlwaysOnWaveform_turnsVibratorOnForLonger()
             throws Exception {
@@ -380,6 +388,7 @@
                 .containsExactly(expectedOneShot(5000)).inOrder();
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_singleVibratorPatternWithZeroDurationSteps_skipsZeroDurationSteps() {
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
@@ -399,6 +408,7 @@
                 .containsExactlyElementsIn(expectedOneShots(100L, 150L)).inOrder();
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_singleVibratorPatternWithZeroDurationAndAmplitude_skipsZeroDurationSteps() {
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
@@ -537,6 +547,7 @@
         assertThat(fakeVibrator.getEffectSegments(vibration.id)).hasSize(10);
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_singleVibratorRepeatingLongAlwaysOnWaveform_turnsVibratorOnForACycle()
             throws Exception {
@@ -700,6 +711,7 @@
                 .containsExactly(expectedPrebaked(VibrationEffect.EFFECT_THUD)).inOrder();
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_singleVibratorPrebakedAndUnsupportedEffectWithFallback_runsFallback() {
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
@@ -1247,6 +1259,7 @@
                 .containsExactly(expected).inOrder();
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_multipleStereo_runsVibrationOnRightVibrators() {
         mockVibrators(1, 2, 3, 4);
@@ -1479,6 +1492,7 @@
         assertThat(mVibratorProviders.get(1).getAmplitudes()).isEmpty();
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_multipleWaveforms_playsWaveformsInParallel() throws Exception {
         mockVibrators(1, 2, 3);
@@ -1544,8 +1558,8 @@
                         VibrationEffect.createOneShot(
                                 expectedDuration, VibrationEffect.DEFAULT_AMPLITUDE)));
 
-        startThreadAndDispatcher(vibration);
         long startTime = SystemClock.elapsedRealtime();
+        startThreadAndDispatcher(vibration);
 
         vibration.waitForEnd();
         long vibrationEndTime = SystemClock.elapsedRealtime();
@@ -1554,15 +1568,13 @@
         long completionTime = SystemClock.elapsedRealtime();
 
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
-        // Vibration ends after duration, thread completed after ramp down
-        assertThat(vibrationEndTime - startTime).isAtLeast(expectedDuration);
+        // Vibration ends before ramp down, thread completed after ramp down
         assertThat(vibrationEndTime - startTime).isLessThan(expectedDuration + rampDownDuration);
         assertThat(completionTime - startTime).isAtLeast(expectedDuration + rampDownDuration);
     }
 
     @Test
-    public void vibrate_withVibratorCallbackDelayShorterThanTimeout_vibrationFinishedAfterDelay()
-            throws Exception {
+    public void vibrate_withVibratorCallbackDelayShorterThanTimeout_vibrationFinishedAfterDelay() {
         long expectedDuration = 10;
         long callbackDelay = VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT / 2;
 
@@ -1577,10 +1589,8 @@
         long startTime = SystemClock.elapsedRealtime();
         startThreadAndDispatcher(vibration);
 
-        vibration.waitForEnd();
-        long vibrationEndTime = SystemClock.elapsedRealtime();
-
         waitForCompletion(TEST_TIMEOUT_MILLIS);
+        long vibrationEndTime = SystemClock.elapsedRealtime();
 
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
         assertThat(vibrationEndTime - startTime).isAtLeast(expectedDuration + callbackDelay);
@@ -1588,8 +1598,7 @@
 
     @LargeTest
     @Test
-    public void vibrate_withVibratorCallbackDelayLongerThanTimeout_vibrationFinishedAfterTimeout()
-            throws Exception {
+    public void vibrate_withVibratorCallbackDelayLongerThanTimeout_vibrationFinishedAfterTimeout() {
         long expectedDuration = 10;
         long callbackTimeout = VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
         long callbackDelay = callbackTimeout * 2;
@@ -1602,21 +1611,17 @@
                         VibrationEffect.createOneShot(
                                 expectedDuration, VibrationEffect.DEFAULT_AMPLITUDE)));
 
-        startThreadAndDispatcher(vibration);
         long startTime = SystemClock.elapsedRealtime();
-
-        vibration.waitForEnd();
-        long vibrationEndTime = SystemClock.elapsedRealtime();
+        startThreadAndDispatcher(vibration);
 
         waitForCompletion(callbackDelay + TEST_TIMEOUT_MILLIS);
-        long completionTime = SystemClock.elapsedRealtime();
+        long vibrationEndTime = SystemClock.elapsedRealtime();
 
         verify(mControllerCallbacks, never())
                 .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
         // Vibration ends and thread completes after timeout, before the HAL callback
         assertThat(vibrationEndTime - startTime).isAtLeast(expectedDuration + callbackTimeout);
         assertThat(vibrationEndTime - startTime).isLessThan(expectedDuration + callbackDelay);
-        assertThat(completionTime - startTime).isLessThan(expectedDuration + callbackDelay);
     }
 
     @LargeTest
@@ -1637,8 +1642,8 @@
         Arrays.fill(amplitudes, VibrationEffect.DEFAULT_AMPLITUDE);
         VibrationEffect effect = VibrationEffect.createWaveform(timings, amplitudes, -1);
 
-        startThreadAndDispatcher(effect);
         long startTime = SystemClock.elapsedRealtime();
+        startThreadAndDispatcher(effect);
 
         waitForCompletion(totalDuration + TEST_TIMEOUT_MILLIS);
         long delay = Math.abs(SystemClock.elapsedRealtime() - startTime - totalDuration);
@@ -1806,6 +1811,7 @@
         assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_waveformWithRampDown_addsRampDownAfterVibrationCompleted() {
         when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
@@ -1833,6 +1839,7 @@
         }
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_waveformWithRampDown_triggersCallbackWhenOriginalVibrationEnds()
             throws Exception {
@@ -1863,6 +1870,7 @@
         verify(mManagerHooks).onVibrationThreadReleased(vibration.id);
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_waveformCancelledWithRampDown_addsRampDownAfterVibrationCancelled()
             throws Exception {
@@ -2057,6 +2065,7 @@
                 .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder();
     }
 
+    @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
     @Test
     public void vibrate_multipleVibratorsSequentialInSession_runsInOrderWithoutDelaysAndNoOffs() {
         mockVibrators(1, 2, 3);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 79e272b..01698b5 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -33,7 +33,6 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
@@ -133,7 +132,7 @@
         mVibratorControlService.registerVibratorController(controller1);
         mVibratorControlService.unregisterVibratorController(controller2);
 
-        verifyZeroInteractions(mMockVibrationScaler);
+        verifyNoMoreInteractions(mMockVibrationScaler);
         assertThat(controller1.isLinkedToDeath).isTrue();
     }
 
@@ -187,7 +186,7 @@
         verify(mStatsLoggerMock).logVibrationParamResponseIgnored();
         verifyNoMoreInteractions(mStatsLoggerMock);
 
-        verifyZeroInteractions(mMockVibrationScaler);
+        verifyNoMoreInteractions(mMockVibrationScaler);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -242,7 +241,7 @@
                 mFakeVibratorController);
 
         verify(mStatsLoggerMock, never()).logVibrationParamScale(anyFloat());
-        verifyZeroInteractions(mMockVibrationScaler);
+        verifyNoMoreInteractions(mMockVibrationScaler);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -280,7 +279,7 @@
                 mFakeVibratorController);
 
         verify(mStatsLoggerMock, never()).logVibrationParamScale(anyFloat());
-        verifyZeroInteractions(mMockVibrationScaler);
+        verifyNoMoreInteractions(mMockVibrationScaler);
     }
 
     @Test
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
index 9a59ede..011971d 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandlerTest.java
@@ -28,7 +28,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.media.soundtrigger.RecognitionStatus;
 import android.media.soundtrigger_middleware.RecognitionEventSys;
@@ -76,7 +75,7 @@
         assertEquals(event.halEventReceivedMillis, -1);
         assertEquals(event.recognitionEvent.status, RecognitionStatus.ABORTED);
         assertFalse(event.recognitionEvent.recognitionStillActive);
-        verifyZeroInteractions(mGlobalCallback);
+        verifyNoMoreInteractions(mGlobalCallback);
         clearInvocations(callback, mUnderlying);
 
         mNotifier.setActive(false);
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 3ca0197..fcdf88f 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -605,29 +605,6 @@
     }
 
     @Test
-    public void testKeyGestureAccessibilityShortcutChord() {
-        Assert.assertTrue(
-                sendKeyGestureEventStart(
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD));
-        mPhoneWindowManager.moveTimeForward(5000);
-        Assert.assertTrue(
-                sendKeyGestureEventCancel(
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD));
-        mPhoneWindowManager.assertAccessibilityKeychordCalled();
-    }
-
-    @Test
-    public void testKeyGestureAccessibilityShortcutChordCancelled() {
-        Assert.assertTrue(
-                sendKeyGestureEventStart(
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD));
-        Assert.assertTrue(
-                sendKeyGestureEventCancel(
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD));
-        mPhoneWindowManager.assertAccessibilityKeychordNotCalled();
-    }
-
-    @Test
     public void testKeyGestureRingerToggleChord() {
         mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE);
         Assert.assertTrue(
@@ -670,29 +647,6 @@
     }
 
     @Test
-    public void testKeyGestureAccessibilityTvShortcutChord() {
-        Assert.assertTrue(
-                sendKeyGestureEventStart(
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD));
-        mPhoneWindowManager.moveTimeForward(5000);
-        Assert.assertTrue(
-                sendKeyGestureEventCancel(
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD));
-        mPhoneWindowManager.assertAccessibilityKeychordCalled();
-    }
-
-    @Test
-    public void testKeyGestureAccessibilityTvShortcutChordCancelled() {
-        Assert.assertTrue(
-                sendKeyGestureEventStart(
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD));
-        Assert.assertTrue(
-                sendKeyGestureEventCancel(
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD));
-        mPhoneWindowManager.assertAccessibilityKeychordNotCalled();
-    }
-
-    @Test
     public void testKeyGestureTvTriggerBugReport() {
         Assert.assertTrue(
                 sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT));
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index d961a6a..50fc776 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.eq;
@@ -90,7 +90,7 @@
         XmlResourceParser testBookmarks = mResources.getXml(
                 com.android.frameworks.wmtests.R.xml.bookmarks);
 
-        doReturn(mContext).when(mContext).createContextAsUser(anyObject(), anyInt());
+        doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
         when(mContext.getResources()).thenReturn(mResources);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mResources.getXml(R.xml.bookmarks)).thenReturn(testBookmarks);
@@ -106,7 +106,7 @@
 
             doReturn(testActivityInfo).when(mPackageManager).getActivityInfo(
                     eq(new ComponentName("com.test", "com.test.BookmarkTest")), anyInt());
-            doReturn(testResolveInfo).when(mPackageManager).resolveActivity(anyObject(), anyInt());
+            doReturn(testResolveInfo).when(mPackageManager).resolveActivity(any(), anyInt());
             doThrow(new PackageManager.NameNotFoundException("com.test3")).when(mPackageManager)
                     .getActivityInfo(eq(new ComponentName("com.test3", "com.test.BookmarkTest")),
                         anyInt());
@@ -140,10 +140,10 @@
     public void test_shortcutInfoFromIntent_appIntent() {
         Intent mockIntent = mock(Intent.class);
         ActivityInfo mockActivityInfo = mock(ActivityInfo.class);
-        when(mockActivityInfo.loadLabel(anyObject())).thenReturn("label");
+        when(mockActivityInfo.loadLabel(any())).thenReturn("label");
         mockActivityInfo.packageName = "android";
         when(mockActivityInfo.getIconResource()).thenReturn(R.drawable.sym_def_app_icon);
-        when(mockIntent.resolveActivityInfo(anyObject(), anyInt())).thenReturn(mockActivityInfo);
+        when(mockIntent.resolveActivityInfo(any(), anyInt())).thenReturn(mockActivityInfo);
 
         KeyboardShortcutInfo info = mModifierShortcutManager.shortcutInfoFromIntent(
                 'a', mockIntent, true);
@@ -161,7 +161,7 @@
         Intent mockSelector = mock(Intent.class);
         ActivityInfo mockActivityInfo = mock(ActivityInfo.class);
         mockActivityInfo.name = com.android.internal.app.ResolverActivity.class.getName();
-        when(mockIntent.resolveActivityInfo(anyObject(), anyInt())).thenReturn(mockActivityInfo);
+        when(mockIntent.resolveActivityInfo(any(), anyInt())).thenReturn(mockActivityInfo);
         when(mockIntent.getSelector()).thenReturn(mockSelector);
         when(mockSelector.getCategories()).thenReturn(
                 Collections.singleton(Intent.CATEGORY_APP_BROWSER));
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 8ede9ef..c57adfd 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -44,7 +44,7 @@
 import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
 
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 
 import static java.util.Collections.unmodifiableMap;
@@ -111,7 +111,7 @@
         mContext = spy(getInstrumentation().getTargetContext());
         mResources = spy(mContext.getResources());
         mPackageManager = spy(mContext.getPackageManager());
-        doReturn(mContext).when(mContext).createContextAsUser(anyObject(), anyInt());
+        doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
         doReturn(mResources).when(mContext).getResources();
         doReturn(mSettingsProviderRule.mockContentResolver(mContext))
                 .when(mContext).getContentResolver();
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 f884924..e56fd3c 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -750,11 +750,6 @@
         verify(mAccessibilityShortcutController).performAccessibilityShortcut();
     }
 
-    void assertAccessibilityKeychordNotCalled() {
-        mTestLooper.dispatchAll();
-        verify(mAccessibilityShortcutController, never()).performAccessibilityShortcut();
-    }
-
     void assertCloseAllDialogs() {
         verify(mContext).closeSystemDialogs();
     }
diff --git a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
index 9e59bce..4ecf6ab 100644
--- a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
@@ -145,8 +145,8 @@
         // Verify the policy wake up call succeeds because of the call on the delegate, and not
         // because of a PowerManager wake up.
         assertThat(mPolicy.wakeUpFromMotion(
-                mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true)).isTrue();
-        verify(mInputWakeUpDelegate).wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true);
+                mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true, false)).isTrue();
+        verify(mInputWakeUpDelegate).wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true, false);
         verifyNoPowerManagerWakeUp();
 
         setDelegatedMotionWakeUpResult(false);
@@ -154,8 +154,8 @@
         // Verify the policy wake up call succeeds because of the PowerManager wake up, since the
         // delegate would not handle the wake up request.
         assertThat(mPolicy.wakeUpFromMotion(
-                mDefaultDisplay.getDisplayId(), 300, SOURCE_ROTARY_ENCODER, false)).isTrue();
-        verify(mInputWakeUpDelegate).wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false);
+                mDefaultDisplay.getDisplayId(), 300, SOURCE_ROTARY_ENCODER, false, false)).isTrue();
+        verify(mInputWakeUpDelegate).wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false, false);
         verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
     }
 
@@ -214,8 +214,9 @@
 
         // Check that the wake up does not happen because the theater mode policy check fails.
         assertThat(mPolicy.wakeUpFromMotion(
-                mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true)).isFalse();
-        verify(mInputWakeUpDelegate, never()).wakeUpFromMotion(anyLong(), anyInt(), anyBoolean());
+                mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true, false)).isFalse();
+        verify(mInputWakeUpDelegate, never())
+                .wakeUpFromMotion(anyLong(), anyInt(), anyBoolean(), anyBoolean());
     }
 
     @Test
@@ -227,7 +228,8 @@
         setBooleanRes(config_allowTheaterModeWakeFromMotion, false);
         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
 
-        mPolicy.wakeUpFromMotion(mDefaultDisplay.getDisplayId(), 200L, SOURCE_TOUCHSCREEN, true);
+        mPolicy.wakeUpFromMotion(
+                mDefaultDisplay.getDisplayId(), 200L, SOURCE_TOUCHSCREEN, true, false);
 
         verify(mPowerManager).wakeUp(200L, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
     }
@@ -237,7 +239,7 @@
     public void testWakeUpFromMotion() {
         runPowerManagerUpChecks(
                 () -> mPolicy.wakeUpFromMotion(mDefaultDisplay.getDisplayId(),
-                        mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true),
+                        mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true, false),
                 config_allowTheaterModeWakeFromMotion,
                 WAKE_REASON_WAKE_MOTION,
                 "android.policy:MOTION");
@@ -251,7 +253,8 @@
         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
 
         boolean displayWokeUp = mPolicy.wakeUpFromMotion(
-                displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true);
+                displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true,
+                /* deviceGoingToSleep= */ false);
 
         // Verify that display is woken up
         assertThat(displayWokeUp).isTrue();
@@ -267,7 +270,8 @@
         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
 
         boolean displayWokeUp = mPolicy.wakeUpFromMotion(
-                displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true);
+                displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true,
+                /* deviceGoingToSleep= */ false);
 
         // Verify that power is woken up and display isn't woken up individually
         assertThat(displayWokeUp).isTrue();
@@ -442,7 +446,7 @@
     }
 
     private void setDelegatedMotionWakeUpResult(boolean result) {
-        when(mInputWakeUpDelegate.wakeUpFromMotion(anyLong(), anyInt(), anyBoolean()))
+        when(mInputWakeUpDelegate.wakeUpFromMotion(anyLong(), anyInt(), anyBoolean(), anyBoolean()))
                 .thenReturn(result);
     }
 
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 8992c2e..7f242de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2021,8 +2021,6 @@
         display.setFixedRotationLaunchingAppUnchecked(activity);
         displayRotation.updateRotationUnchecked(true /* forceUpdate */);
 
-        assertTrue(displayRotation.isRotatingSeamlessly());
-
         // The launching rotated app should not be cleared when waiting for remote rotation.
         display.continueUpdateOrientationForDiffOrienLaunchingApp();
         assertTrue(display.isFixedRotationLaunchingApp(activity));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
index 948371f..ad706e8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivitySnapshotControllerTests.java
@@ -63,11 +63,23 @@
         super(0.8f /* highResScale */, 0.5f /* lowResScale */);
     }
 
+    private class TestActivitySnapshotController extends ActivitySnapshotController {
+        TestActivitySnapshotController(WindowManagerService service,
+                SnapshotPersistQueue persistQueue) {
+            super(service, persistQueue);
+        }
+        @Override
+        BaseAppSnapshotPersister.PersistInfoProvider createPersistInfoProvider(
+                WindowManagerService service) {
+            return mPersister.mPersistInfoProvider;
+        }
+    }
     @Override
     @Before
     public void setUp() {
         super.setUp();
-        mActivitySnapshotController = new ActivitySnapshotController(mWm, mSnapshotPersistQueue);
+        mActivitySnapshotController = new TestActivitySnapshotController(
+                mWm, mSnapshotPersistQueue);
         spyOn(mActivitySnapshotController);
         doReturn(false).when(mActivitySnapshotController).shouldDisableSnapshots();
         mActivitySnapshotController.resetTmpFields();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index e3e9cc4..08b0077 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -797,7 +797,7 @@
         // Create adjacent tasks and put one activity under it
         final Task parent = new TaskBuilder(mSupervisor).build();
         final Task adjacentParent = new TaskBuilder(mSupervisor).build();
-        parent.setAdjacentTaskFragment(adjacentParent);
+        parent.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(parent, adjacentParent));
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setParentTask(parent)
                 .setCreateTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index a9be47d..862ce51 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -75,13 +76,15 @@
  * Tests for the {@link ActivityTaskSupervisor} class.
  *
  * Build/Install/Run:
- *  atest WmTests:ActivityTaskSupervisorTests
+ * atest WmTests:ActivityTaskSupervisorTests
  */
 @MediumTest
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class ActivityTaskSupervisorTests extends WindowTestsBase {
     private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
+    private static final int DEFAULT_CALLING_PID = -1;
+    private static final int DEFAULT_CALLING_UID = -1;
 
     /**
      * Ensures that an activity is removed from the stopping activities list once it is resumed.
@@ -110,7 +113,7 @@
                 .setCreateTask(true).build();
         final ConditionVariable condition = new ConditionVariable();
         final WaitResult taskToFrontWait = new WaitResult();
-        final ComponentName[] launchedComponent = { null };
+        final ComponentName[] launchedComponent = {null};
         // Create a new thread so the waiting method in test can be notified.
         new Thread(() -> {
             synchronized (mAtm.mGlobalLock) {
@@ -408,7 +411,8 @@
         doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(),
                 anyInt(), any());
 
-        mSupervisor.startActivityFromRecents(-1, -1, activity.getRootTaskId(), safeOptions);
+        mSupervisor.startActivityFromRecents(DEFAULT_CALLING_PID, DEFAULT_CALLING_UID,
+                activity.getRootTaskId(), safeOptions);
 
         assertThat(activity.mLaunchCookie).isEqualTo(launchCookie);
         verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions));
@@ -426,12 +430,62 @@
         doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(),
                 anyInt(), any());
 
-        mSupervisor.startActivityFromRecents(-1, -1, activity.getRootTaskId(), safeOptions);
+        mSupervisor.startActivityFromRecents(DEFAULT_CALLING_PID, DEFAULT_CALLING_UID,
+                activity.getRootTaskId(), safeOptions);
 
         assertThat(activity.mLaunchCookie).isNull();
         verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions));
     }
 
+    /** Verifies that launch from recents doesn't set the launch cookie on the activity. */
+    @Test
+    public void testStartActivityFromRecents_inMultiWindowRootTask_homeNotMoved() {
+        final Task multiWindowRootTask = new TaskBuilder(mSupervisor).setWindowingMode(
+                WINDOWING_MODE_MULTI_WINDOW).setOnTop(true).build();
+
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setParentTask(
+                multiWindowRootTask).setCreateTask(true).build();
+
+        SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(
+                ActivityOptions.makeBasic().toBundle(),
+                Binder.getCallingPid(), Binder.getCallingUid());
+
+        doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(),
+                anyInt(), any());
+
+        mSupervisor.startActivityFromRecents(DEFAULT_CALLING_PID, DEFAULT_CALLING_UID,
+                activity.getRootTaskId(), safeOptions);
+
+        verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions));
+        verify(mRootWindowContainer.getDefaultTaskDisplayArea(), never()).moveHomeRootTaskToFront(
+                any());
+        verify(multiWindowRootTask.getDisplayArea(), never()).moveHomeRootTaskToFront(any());
+    }
+
+    /** Verifies that launch from recents doesn't set the launch cookie on the activity. */
+    @Test
+    public void testStartActivityFromRecents_inFullScreenRootTask_homeMovedToFront() {
+        final Task fullscreenRootTask = new TaskBuilder(mSupervisor).setWindowingMode(
+                WINDOWING_MODE_FULLSCREEN).setOnTop(true).build();
+
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setParentTask(
+                fullscreenRootTask).setCreateTask(true).build();
+
+        SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(
+                ActivityOptions.makeBasic().toBundle(),
+                Binder.getCallingPid(), Binder.getCallingUid());
+
+        doNothing().when(mSupervisor.mService).moveTaskToFrontLocked(eq(null), eq(null), anyInt(),
+                anyInt(), any());
+
+        mSupervisor.startActivityFromRecents(DEFAULT_CALLING_PID, DEFAULT_CALLING_UID,
+                activity.getRootTaskId(), safeOptions);
+
+        verify(mAtm).moveTaskToFrontLocked(any(), eq(null), anyInt(), anyInt(), eq(safeOptions));
+        verify(mRootWindowContainer.getDefaultTaskDisplayArea()).moveHomeRootTaskToFront(any());
+        verify(fullscreenRootTask.getDisplayArea()).moveHomeRootTaskToFront(any());
+    }
+
     @Test
     public void testOpaque_leafTask_occludingActivity_isOpaque() {
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
@@ -488,14 +542,13 @@
                 WINDOWING_MODE_MULTI_WINDOW, /* opaque */ true, /* filling */ false);
         final TaskFragment tf2 = createChildTaskFragment(/* parent */ rootTask,
                 WINDOWING_MODE_MULTI_WINDOW, /* opaque */ true, /* filling */ false);
-        tf1.setAdjacentTaskFragment(tf2);
+        tf1.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(tf1, tf2));
 
         assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(rootTask)).isTrue();
     }
 
     @Test
-    @EnableFlags({Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
-            Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS})
+    @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
     public void testOpaque_rootTask_nonFillingOpaqueAdjacentChildren_multipleAdjacent_isOpaque() {
         final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
         final TaskFragment tf1 = createChildTaskFragment(/* parent */ rootTask,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 8fe0855..cb98b9a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -243,6 +243,11 @@
                 .getAspectRatioOverrides()).getUserMinAspectRatio();
     }
 
+    void setShouldRefreshActivityForCameraCompat(boolean enabled) {
+        doReturn(enabled).when(mActivityStack.top().mAppCompatController.getCameraOverrides())
+                .shouldRefreshActivityForCameraCompat();
+    }
+
     void setIgnoreOrientationRequest(boolean enabled) {
         mDisplayContent.setIgnoreOrientationRequest(enabled);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
index 05f6ed6..7ef85262d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
@@ -64,6 +64,19 @@
                 .isCameraCompatTreatmentEnabledAtBuildTime();
     }
 
+    void setCameraCompatAspectRatio(float aspectRatio) {
+        doReturn(aspectRatio).when(mAppCompatConfiguration).getCameraCompatAspectRatio();
+    }
+
+    void enableCameraCompatRefresh(boolean enabled) {
+        doReturn(enabled).when(mAppCompatConfiguration).isCameraCompatRefreshEnabled();
+    }
+
+    void enableCameraCompatRefreshCycleThroughStop(boolean enabled) {
+        doReturn(enabled).when(mAppCompatConfiguration)
+                .isCameraCompatRefreshCycleThroughStopEnabled();
+    }
+
     void enableUserAppAspectRatioFullscreen(boolean enabled) {
         doReturn(enabled).when(mAppCompatConfiguration).isUserAppAspectRatioFullscreenEnabled();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatSafeRegionPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatSafeRegionPolicyTests.java
new file mode 100644
index 0000000..bdd57bc
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatSafeRegionPolicyTests.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import com.android.window.flags.Flags;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatSafeRegionPolicy}.
+ * Build/Install/Run:
+ * atest WmTests:AppCompatSafeRegionPolicyTests
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+@EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+public class AppCompatSafeRegionPolicyTests extends WindowTestsBase {
+
+    @Test
+    public void testHasNeedsSafeRegion_returnTrue() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.setNeedsSafeRegionBounds(/*needsSafeRegionBounds*/ true);
+
+            robot.checkNeedsSafeRegionBounds(/* expected */ true);
+        });
+    }
+
+    @Test
+    public void testDoesNotHaveNeedsSafeRegion_returnFalse() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.setNeedsSafeRegionBounds(/*needsSafeRegionBounds*/ false);
+
+            robot.checkNeedsSafeRegionBounds(/* expected */ false);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<AppCompatSafeRegionPolicyRobotTest> consumer) {
+        final AppCompatSafeRegionPolicyRobotTest robot =
+                new AppCompatSafeRegionPolicyRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class AppCompatSafeRegionPolicyRobotTest extends AppCompatRobotBase {
+
+        AppCompatSafeRegionPolicyRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        @Override
+        void onPostActivityCreation(@NonNull ActivityRecord activity) {
+            super.onPostActivityCreation(activity);
+            spyOn(activity.mAppCompatController.getSafeRegionPolicy());
+        }
+
+        AppCompatSafeRegionPolicy getAppCompatSafeRegionPolicy() {
+            return activity().top().mAppCompatController.getSafeRegionPolicy();
+        }
+
+        void setNeedsSafeRegionBounds(boolean needsSafeRegionBounds) {
+            doReturn(needsSafeRegionBounds).when(
+                    getAppCompatSafeRegionPolicy()).getNeedsSafeRegionBounds();
+        }
+
+        void checkNeedsSafeRegionBounds(boolean expected) {
+            assertEquals(expected, getAppCompatSafeRegionPolicy().getNeedsSafeRegionBounds());
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
index bfd533a..b2cfbbd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
@@ -98,6 +98,24 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void getLetterboxReasonString_isLetterboxedForSafeRegionOnly() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.checkTopActivityInSizeCompatMode(/* inScm */ false);
+            });
+            robot.setIsLetterboxedForFixedOrientationAndAspectRatio(
+                    /* forFixedOrientationAndAspectRatio */ false);
+            robot.setIsLetterboxedForDisplayCutout(/* displayCutout */ false);
+            robot.setIsLetterboxedForAspectRatioOnly(/* forAspectRatio */ false);
+            robot.setIsLetterboxedForSafeRegionOnlyAllowed(/* safeRegionOnly */ true);
+
+            robot.checkTopActivityLetterboxReason(/* expected */ "SAFE_REGION");
+        });
+    }
+
+    @Test
     public void getLetterboxReasonString_aspectRatio() {
         runTestScenario((robot) -> {
             robot.applyOnActivity((a) -> {
@@ -124,6 +142,7 @@
                     /* forFixedOrientationAndAspectRatio */ false);
             robot.setIsLetterboxedForDisplayCutout(/* displayCutout */ false);
             robot.setIsLetterboxedForAspectRatioOnly(/* forAspectRatio */ false);
+            robot.setIsLetterboxedForSafeRegionOnlyAllowed(/* safeRegionOnly */ false);
 
             robot.checkTopActivityLetterboxReason(/* expected */ "UNKNOWN_REASON");
         });
@@ -253,6 +272,7 @@
         void onPostActivityCreation(@NonNull ActivityRecord activity) {
             super.onPostActivityCreation(activity);
             spyOn(activity.mAppCompatController.getAspectRatioPolicy());
+            spyOn(activity.mAppCompatController.getSafeRegionPolicy());
         }
 
         @Override
@@ -286,6 +306,11 @@
             when(mWindowState.isLetterboxedForDisplayCutout()).thenReturn(displayCutout);
         }
 
+        void setIsLetterboxedForSafeRegionOnlyAllowed(boolean safeRegionOnly) {
+            when(activity().top().mAppCompatController.getSafeRegionPolicy()
+                    .isLetterboxedForSafeRegionOnlyAllowed()).thenReturn(safeRegionOnly);
+        }
+
         void setFreeformCameraCompatMode(@FreeformCameraCompatMode int mode) {
             doReturn(mode).when(activity().top().mDisplayContent.mAppCompatCameraPolicy
                     .mCameraCompatFreeformPolicy).getCameraCompatMode(activity().top());
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 bdee3c3..dd3e9fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -343,8 +343,7 @@
 
         // Adjacent + no companion => unable to predict
         // TF1 | TF2
-        tf1.setAdjacentTaskFragment(tf2);
-        tf2.setAdjacentTaskFragment(tf1);
+        tf1.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(tf1, tf2));
         predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr,
                 outPrevActivities);
         assertTrue(outPrevActivities.isEmpty());
@@ -393,8 +392,7 @@
         // Adjacent => predict for previous activity.
         // TF2 | TF3
         // TF1
-        tf2.setAdjacentTaskFragment(tf3);
-        tf3.setAdjacentTaskFragment(tf2);
+        tf2.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(tf2, tf3));
         predictable = BackNavigationController.getAnimatablePrevActivities(task, topAr,
                 outPrevActivities);
         assertTrue(outPrevActivities.contains(prevAr));
@@ -657,8 +655,7 @@
         final TaskFragment secondaryTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
         final ActivityRecord primaryActivity = primaryTf.getTopMostActivity();
         final ActivityRecord secondaryActivity = secondaryTf.getTopMostActivity();
-        primaryTf.setAdjacentTaskFragment(secondaryTf);
-        secondaryTf.setAdjacentTaskFragment(primaryTf);
+        primaryTf.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(primaryTf, secondaryTf));
 
         final WindowState primaryWindow = mock(WindowState.class);
         final WindowState secondaryWindow = mock(WindowState.class);
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 f5bec04..6f95981 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -21,13 +21,13 @@
 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.FreeformCameraCompatMode;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
 import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION;
 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.SCREEN_ORIENTATION_FULL_USER;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
@@ -40,18 +40,15 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
 import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
 import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -59,13 +56,11 @@
 import static org.mockito.Mockito.verify;
 
 import android.annotation.NonNull;
-import android.app.CameraCompatTaskInfo;
 import android.app.IApplicationThread;
 import android.app.WindowConfiguration.WindowingMode;
 import android.app.servertransaction.RefreshCallbackItem;
 import android.app.servertransaction.ResumeActivityItem;
 import android.compat.testing.PlatformCompatChangeRule;
-import android.content.ComponentName;
 import android.content.pm.ActivityInfo.ScreenOrientation;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -73,17 +68,16 @@
 import android.graphics.Rect;
 import android.hardware.camera2.CameraManager;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
-import android.view.DisplayInfo;
 import android.view.Surface;
 
 import androidx.test.filters.SmallTest;
 
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
@@ -91,6 +85,7 @@
 import org.mockito.ArgumentCaptor;
 
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Tests for {@link CameraCompatFreeformPolicy}.
@@ -109,30 +104,18 @@
     private static final String TEST_PACKAGE_1 = "com.android.frameworks.wmtests";
     private static final String TEST_PACKAGE_2 = "com.test.package.two";
     private static final String CAMERA_ID_1 = "camera-1";
-    private AppCompatConfiguration mAppCompatConfiguration;
-
-    private CameraManager.AvailabilityCallback mCameraAvailabilityCallback;
-    private CameraCompatFreeformPolicy mCameraCompatFreeformPolicy;
-    private ActivityRecord mActivity;
-
-    // TODO(b/384465100): use a robot structure.
-    @Before
-    public void setUp() throws Exception {
-        setupAppCompatConfiguration();
-        setupCameraManager();
-        setupHandler();
-        doReturn(true).when(() -> DesktopModeHelper.canEnterDesktopMode(any()));
-    }
 
     @Test
     @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testFeatureDisabled_cameraCompatFreeformPolicyNotCreated() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertNull(mCameraCompatFreeformPolicy);
+            robot.checkCameraCompatPolicyNotCreated();
+        });
     }
 
     @Test
@@ -140,31 +123,37 @@
             FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION})
     public void testIsCameraRunningAndWindowingModeEligible_disabledViaOverride_returnsFalse() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertFalse(mCameraCompatFreeformPolicy.isCameraRunningAndWindowingModeEligible(mActivity));
+            robot.checkIsCameraRunningAndWindowingModeEligible(false);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testIsCameraRunningAndWindowingModeEligible_cameraNotRunning_returnsFalse() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        assertFalse(mCameraCompatFreeformPolicy.isCameraRunningAndWindowingModeEligible(mActivity));
+            robot.checkIsCameraRunningAndWindowingModeEligible(false);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testIsCameraRunningAndWindowingModeEligible_notFreeformWindowing_returnsFalse() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertFalse(mCameraCompatFreeformPolicy.isCameraRunningAndWindowingModeEligible(mActivity));
+            robot.checkIsCameraRunningAndWindowingModeEligible(false);
+        });
     }
 
     @Test
@@ -172,64 +161,76 @@
     @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testIsCameraRunningAndWindowingModeEligible_optInFreeformCameraRunning_true() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertTrue(mCameraCompatFreeformPolicy.isCameraRunningAndWindowingModeEligible(mActivity));
+            robot.checkIsCameraRunningAndWindowingModeEligible(true);
+        });
     }
 
     @Test
     @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
             FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
     public void testIsCameraRunningAndWindowingModeEligible_freeformCameraRunning_true() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertTrue(mCameraCompatFreeformPolicy.isCameraRunningAndWindowingModeEligible(mActivity));
+            robot.checkIsCameraRunningAndWindowingModeEligible(true);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
     public void testIsFreeformLetterboxingForCameraAllowed_optInMechanism_notOptedIn_retFalse() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+            robot.checkIsFreeformLetterboxingForCameraAllowed(false);
+        });
     }
 
     @Test
     @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
             FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
     public void testIsFreeformLetterboxingForCameraAllowed_notOptedOut_returnsTrue() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertTrue(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+            robot.checkIsFreeformLetterboxingForCameraAllowed(true);
+        });
     }
 
     @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);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+            robot.checkIsFreeformLetterboxingForCameraAllowed(false);
+        });
     }
 
     @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);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertFalse(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+            robot.checkIsFreeformLetterboxingForCameraAllowed(false);
+        });
     }
 
     @Test
@@ -237,519 +238,603 @@
     @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testIsFreeformLetterboxingForCameraAllowed_optInFreeformCameraRunning_true() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertTrue(mCameraCompatFreeformPolicy.isFreeformLetterboxingForCameraAllowed(mActivity));
+            robot.checkIsFreeformLetterboxingForCameraAllowed(true);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testFullscreen_doesNotActivateCameraCompatMode() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
-        doReturn(false).when(mActivity).inFreeformWindowingMode();
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
+            robot.setInFreeformWindowingMode(false);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertNotInCameraCompatMode();
+            robot.assertNotInCameraCompatMode();
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testOrientationUnspecified_doesNotActivateCameraCompatMode() {
-        configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
 
-        assertNotInCameraCompatMode();
+            robot.assertNotInCameraCompatMode();
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testNoCameraConnection_doesNotActivateCameraCompatMode() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        assertNotInCameraCompatMode();
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+            robot.assertNotInCameraCompatMode();
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testCameraConnected_deviceInPortrait_portraitCameraCompatMode() throws Exception {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        setDisplayRotation(ROTATION_0);
+    public void testCameraConnected_deviceInPortrait_portraitCameraCompatMode() {
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.activity().rotateDisplayForTopActivity(ROTATION_0);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT);
-        assertActivityRefreshRequested(/* refreshRequested */ false);
+            robot.assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT);
+            robot.assertActivityRefreshRequested(/* refreshRequested */ false);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testCameraConnected_deviceInLandscape_portraitCameraCompatMode() throws Exception {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        setDisplayRotation(ROTATION_270);
+    public void testCameraConnected_deviceInLandscape_portraitCameraCompatMode() {
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.activity().rotateDisplayForTopActivity(ROTATION_270);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
-        assertActivityRefreshRequested(/* refreshRequested */ false);
+            robot.assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
+            robot.assertActivityRefreshRequested(/* refreshRequested */ false);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testCameraConnected_deviceInPortrait_landscapeCameraCompatMode() throws Exception {
-        configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
-        setDisplayRotation(ROTATION_0);
+    public void testCameraConnected_deviceInPortrait_landscapeCameraCompatMode() {
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
+            robot.activity().rotateDisplayForTopActivity(ROTATION_0);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT);
-        assertActivityRefreshRequested(/* refreshRequested */ false);
+            robot.assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT);
+            robot.assertActivityRefreshRequested(/* refreshRequested */ false);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testCameraConnected_deviceInLandscape_landscapeCameraCompatMode() throws Exception {
-        configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
-        setDisplayRotation(ROTATION_270);
+    public void testCameraConnected_deviceInLandscape_landscapeCameraCompatMode() {
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
+            robot.activity().rotateDisplayForTopActivity(ROTATION_270);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE);
-        assertActivityRefreshRequested(/* refreshRequested */ false);
+            robot.assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE);
+            robot.assertActivityRefreshRequested(/* refreshRequested */ false);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        setDisplayRotation(ROTATION_270);
+    public void testCameraReconnected_cameraCompatModeAndRefresh() {
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.activity().rotateDisplayForTopActivity(ROTATION_270);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        callOnActivityConfigurationChanging(mActivity, /* letterboxNew= */ true,
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.callOnActivityConfigurationChanging(/* letterboxNew= */ true,
                 /* lastLetterbox= */ false);
-        assertActivityRefreshRequested(/* refreshRequested */ true);
-        onCameraClosed(CAMERA_ID_1);
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        // Activity is letterboxed from the previous configuration change.
-        callOnActivityConfigurationChanging(mActivity, /* letterboxNew= */ true,
-                /* lastLetterbox= */ true);
+            robot.assertActivityRefreshRequested(/* refreshRequested */ true);
+            robot.onCameraClosed(CAMERA_ID_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            // Activity is letterboxed from the previous configuration change.
+            robot.callOnActivityConfigurationChanging(/* letterboxNew= */ true,
+                    /* lastLetterbox= */ true);
 
-        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
-        assertActivityRefreshRequested(/* refreshRequested */ true);
+            robot.assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
+            robot.assertActivityRefreshRequested(/* refreshRequested */ true);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testCameraOpenedForDifferentPackage_notInCameraCompatMode() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_2);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_2);
 
-        assertNotInCameraCompatMode();
+            robot.assertNotInCameraCompatMode();
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
     public void testShouldApplyCameraCompatFreeformTreatment_overrideNotEnabled_returnsFalse() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertFalse(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity(mActivity,
-                /* checkOrientation */ true));
+            robot.checkIsCameraCompatTreatmentActiveForTopActivity(false);
+        });
     }
 
     @Test
     @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
             FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
     public void testShouldApplyCameraCompatFreeformTreatment_notOptedOut_returnsTrue() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertTrue(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity(mActivity,
-                /* checkOrientation */ true));
+            robot.checkIsCameraCompatTreatmentActiveForTopActivity(true);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges(OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT)
     public void testShouldApplyCameraCompatFreeformTreatment_enabledByOverride_returnsTrue() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertTrue(mActivity.info
-                .isChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT));
-        assertTrue(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity(mActivity,
-                /* checkOrientation */ true));
+            robot.checkIsCameraCompatTreatmentActiveForTopActivity(true);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testShouldRefreshActivity_appBoundsChanged_returnsTrue() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        Configuration oldConfiguration = createConfiguration(/* letterbox= */ false);
-        Configuration newConfiguration = createConfiguration(/* letterbox= */ true);
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        assertTrue(mCameraCompatFreeformPolicy.shouldRefreshActivity(mActivity, newConfiguration,
-                oldConfiguration));
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+            robot.checkShouldRefreshActivity(/* expected= */ true,
+                    robot.createConfiguration(/* letterbox= */ true, /* rotation= */ 0),
+                    robot.createConfiguration(/* letterbox= */ false, /* rotation= */ 0));
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testShouldRefreshActivity_displayRotationChanged_returnsTrue() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        Configuration oldConfiguration = createConfiguration(/* letterbox= */ true);
-        Configuration newConfiguration = createConfiguration(/* letterbox= */ true);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        oldConfiguration.windowConfiguration.setDisplayRotation(0);
-        newConfiguration.windowConfiguration.setDisplayRotation(90);
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertTrue(mCameraCompatFreeformPolicy.shouldRefreshActivity(mActivity, newConfiguration,
-                oldConfiguration));
+            robot.checkShouldRefreshActivity(/* expected= */ true,
+                    robot.createConfiguration(/* letterbox= */ true, /* rotation= */ 90),
+                    robot.createConfiguration(/* letterbox= */ true, /* rotation= */ 0));
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testShouldRefreshActivity_appBoundsNorDisplayChanged_returnsFalse() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        Configuration oldConfiguration = createConfiguration(/* letterbox= */ true);
-        Configuration newConfiguration = createConfiguration(/* letterbox= */ true);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
-        oldConfiguration.windowConfiguration.setDisplayRotation(0);
-        newConfiguration.windowConfiguration.setDisplayRotation(0);
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertFalse(mCameraCompatFreeformPolicy.shouldRefreshActivity(mActivity, newConfiguration,
-                oldConfiguration));
+            robot.checkShouldRefreshActivity(/* expected= */ false,
+                    robot.createConfiguration(/* letterbox= */ true, /* rotation= */ 0),
+                    robot.createConfiguration(/* letterbox= */ true, /* rotation= */ 0));
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testOnActivityConfigurationChanging_refreshDisabledViaFlag_noRefresh()
-            throws Exception {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+    public void testOnActivityConfigurationChanging_refreshDisabledViaFlag_noRefresh() {
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.activity().setShouldRefreshActivityForCameraCompat(false);
 
-        doReturn(false).when(mActivity.mAppCompatController.getCameraOverrides())
-                .shouldRefreshActivityForCameraCompat();
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.callOnActivityConfigurationChanging();
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        callOnActivityConfigurationChanging(mActivity);
-
-        assertActivityRefreshRequested(/* refreshRequested */ false);
+            robot.assertActivityRefreshRequested(/* refreshRequested */ false);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testOnActivityConfigurationChanging_cycleThroughStopDisabled() throws Exception {
-        when(mAppCompatConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
-                .thenReturn(false);
+    public void testOnActivityConfigurationChanging_cycleThroughStopDisabled() {
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.conf().enableCameraCompatRefreshCycleThroughStop(false);
 
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.callOnActivityConfigurationChanging();
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        callOnActivityConfigurationChanging(mActivity);
-
-        assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
+            robot.assertActivityRefreshRequested(/* refreshRequested */ true,
+                    /* cycleThroughStop */ false);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp()
-            throws Exception {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        doReturn(true).when(mActivity.mAppCompatController.getCameraOverrides())
-                .shouldRefreshActivityViaPauseForCameraCompat();
+    public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp() {
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.setShouldRefreshActivityViaPause(true);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        callOnActivityConfigurationChanging(mActivity);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.callOnActivityConfigurationChanging();
 
-        assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
+            robot.assertActivityRefreshRequested(/* refreshRequested */ true,
+                    /* cycleThroughStop */ false);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testGetCameraCompatAspectRatio_activityNotInCameraCompat_returnsDefaultAspRatio() {
-        configureActivity(SCREEN_ORIENTATION_FULL_USER);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_FULL_USER);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        callOnActivityConfigurationChanging(mActivity);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.callOnActivityConfigurationChanging();
 
-        assertEquals(MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO,
-                mCameraCompatFreeformPolicy.getCameraCompatAspectRatio(mActivity),
-                /* delta= */ 0.001);
+            robot.checkCameraCompatAspectRatioEquals(MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testGetCameraCompatAspectRatio_activityInCameraCompat_returnsConfigAspectRatio() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        final float configAspectRatio = 1.5f;
-        mWm.mAppCompatConfiguration.setCameraCompatAspectRatio(configAspectRatio);
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+            final float configAspectRatio = 1.5f;
+            robot.conf().setCameraCompatAspectRatio(configAspectRatio);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        callOnActivityConfigurationChanging(mActivity);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.callOnActivityConfigurationChanging();
 
-        assertEquals(configAspectRatio,
-                mCameraCompatFreeformPolicy.getCameraCompatAspectRatio(mActivity),
-                /* delta= */ 0.001);
+            robot.checkCameraCompatAspectRatioEquals(configAspectRatio);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testGetCameraCompatAspectRatio_inCameraCompatPerAppOverride_returnDefAspectRatio() {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        final float configAspectRatio = 1.5f;
-        mWm.mAppCompatConfiguration.setCameraCompatAspectRatio(configAspectRatio);
-        doReturn(true).when(mActivity.mAppCompatController.getCameraOverrides())
-                .isOverrideMinAspectRatioForCameraEnabled();
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.conf().setCameraCompatAspectRatio(1.5f);
+            robot.setOverrideMinAspectRatioEnabled(true);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        callOnActivityConfigurationChanging(mActivity);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.callOnActivityConfigurationChanging();
 
-        assertEquals(MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO,
-                mCameraCompatFreeformPolicy.getCameraCompatAspectRatio(mActivity),
-                /* delta= */ 0.001);
+            robot.checkCameraCompatAspectRatioEquals(MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testOnCameraOpened_portraitActivity_sandboxesDisplayRotationAndUpdatesApp() throws
-            Exception {
-        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        setDisplayRotation(ROTATION_270);
+    public void testOnCameraOpened_portraitActivity_sandboxesDisplayRotationAndUpdatesApp() {
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.activity().rotateDisplayForTopActivity(ROTATION_270);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        // This is a portrait rotation for a device with portrait natural orientation (most common,
-        // currently the only one supported).
-        assertCompatibilityInfoSentWithDisplayRotation(ROTATION_0);
+            // This is a portrait rotation for a device with portrait natural orientation (most
+            // common, currently the only one supported).
+            robot.assertCompatibilityInfoSentWithDisplayRotation(ROTATION_0);
+        });
     }
 
     @Test
     @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
-    public void testOnCameraOpened_landscapeActivity_sandboxesDisplayRotationAndUpdatesApp() throws
-            Exception {
-        configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
-        setDisplayRotation(ROTATION_0);
+    public void testOnCameraOpened_landscapeActivity_sandboxesDisplayRotationAndUpdatesApp() {
+        runTestScenario((robot) -> {
+            robot.configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
+            robot.activity().rotateDisplayForTopActivity(ROTATION_0);
 
-        onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        // This is a landscape rotation for a device with portrait natural orientation (most common,
-        // currently the only one supported).
-        assertCompatibilityInfoSentWithDisplayRotation(ROTATION_90);
+            // This is a landscape rotation for a device with portrait natural orientation (most
+            // common, currently the only one supported).
+            robot.assertCompatibilityInfoSentWithDisplayRotation(ROTATION_90);
+        });
     }
 
-    private void setupAppCompatConfiguration() {
-        mAppCompatConfiguration = mDisplayContent.mWmService.mAppCompatConfiguration;
-        spyOn(mAppCompatConfiguration);
-        when(mAppCompatConfiguration.isCameraCompatTreatmentEnabled()).thenReturn(true);
-        when(mAppCompatConfiguration.isCameraCompatTreatmentEnabledAtBuildTime()).thenReturn(true);
-        when(mAppCompatConfiguration.isCameraCompatRefreshEnabled()).thenReturn(true);
-        when(mAppCompatConfiguration.isCameraCompatSplitScreenAspectRatioEnabled())
-                .thenReturn(false);
-        when(mAppCompatConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
-                .thenReturn(true);
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<CameraCompatFreeformPolicyRobotTests> consumer) {
+        final CameraCompatFreeformPolicyRobotTests robot =
+                new CameraCompatFreeformPolicyRobotTests(mWm, mAtm, mSupervisor, this);
+        consumer.accept(robot);
     }
 
-    private void setupCameraManager() {
-        final CameraManager mockCameraManager = mock(CameraManager.class);
-        doAnswer(invocation -> {
-            mCameraAvailabilityCallback = invocation.getArgument(1);
-            return null;
-        }).when(mockCameraManager).registerAvailabilityCallback(
-                any(Executor.class), any(CameraManager.AvailabilityCallback.class));
+    private static class CameraCompatFreeformPolicyRobotTests extends AppCompatRobotBase {
+        private final WindowTestsBase mWindowTestsBase;
 
-        when(mContext.getSystemService(CameraManager.class)).thenReturn(mockCameraManager);
-    }
+        private CameraManager.AvailabilityCallback mCameraAvailabilityCallback;
 
-    private void setupHandler() {
-        final Handler handler = mDisplayContent.mWmService.mH;
-        spyOn(handler);
+        CameraCompatFreeformPolicyRobotTests(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor,
+                @NonNull WindowTestsBase windowTestsBase) {
+            super(wm, atm, supervisor);
+            mWindowTestsBase = windowTestsBase;
+            setupCameraManager();
+            setupAppCompatConfiguration();
+        }
 
-        when(handler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(
-                invocation -> {
-                    ((Runnable) invocation.getArgument(0)).run();
-                    return null;
-                });
-    }
+        @Override
+        void onPostDisplayContentCreation(@NonNull DisplayContent displayContent) {
+            super.onPostDisplayContentCreation(displayContent);
+            spyOn(displayContent.mAppCompatCameraPolicy);
+            if (displayContent.mAppCompatCameraPolicy.mCameraCompatFreeformPolicy != null) {
+                spyOn(displayContent.mAppCompatCameraPolicy.mCameraCompatFreeformPolicy);
+            }
+        }
 
-    private void configureActivity(@ScreenOrientation int activityOrientation) {
-        configureActivity(activityOrientation, WINDOWING_MODE_FREEFORM);
-    }
+        @Override
+        void onPostActivityCreation(@NonNull ActivityRecord activity) {
+            super.onPostActivityCreation(activity);
+            setupCameraManager();
+            setupHandler();
+            setupMockApplicationThread();
+        }
 
-    private void configureActivity(@ScreenOrientation int activityOrientation,
-            @WindowingMode int windowingMode) {
-        configureActivityAndDisplay(activityOrientation, ORIENTATION_PORTRAIT, windowingMode);
-    }
+        private void setupMockApplicationThread() {
+            IApplicationThread mockApplicationThread = mock(IApplicationThread.class);
+            spyOn(activity().top().app);
+            doReturn(mockApplicationThread).when(activity().top().app).getThread();
+        }
 
-    private void configureActivityAndDisplay(@ScreenOrientation int activityOrientation,
-            @Orientation int naturalOrientation, @WindowingMode int windowingMode) {
-        setupDisplayContent(naturalOrientation);
-        final Task task = setupTask(windowingMode);
-        setupActivity(task, activityOrientation, windowingMode);
-        setupMockApplicationThread();
+        private Configuration createConfiguration(boolean letterbox, int rotation) {
+            final Configuration configuration = createConfiguration(letterbox);
+            configuration.windowConfiguration.setDisplayRotation(rotation);
+            return configuration;
+        }
 
-        mCameraCompatFreeformPolicy = mDisplayContent.mAppCompatCameraPolicy
-                .mCameraCompatFreeformPolicy;
-    }
+        private Configuration createConfiguration(boolean letterbox) {
+            final Configuration configuration = new Configuration();
+            Rect bounds = letterbox ? new Rect(/*left*/ 300, /*top*/ 0, /*right*/ 700, /*bottom*/
+                    600)
+                    : new Rect(/*left*/ 0, /*top*/ 0, /*right*/ 1000, /*bottom*/ 600);
+            configuration.windowConfiguration.setAppBounds(bounds);
+            return configuration;
+        }
 
-    private void setupDisplayContent(@Orientation int naturalOrientation) {
-        // Create a new DisplayContent so that the flag values create the camera freeform policy.
-        mDisplayContent = new TestDisplayContent.Builder(mAtm, mDisplayContent.getSurfaceWidth(),
-                mDisplayContent.getSurfaceHeight()).build();
-        mDisplayContent.setIgnoreOrientationRequest(true);
-        setDisplayRotation(ROTATION_90);
-        doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
-    }
+        private void setupAppCompatConfiguration() {
+            applyOnConf((c) -> {
+                c.enableCameraCompatTreatment(true);
+                c.enableCameraCompatTreatmentAtBuildTime(true);
+                c.enableCameraCompatRefresh(true);
+                c.enableCameraCompatRefreshCycleThroughStop(true);
+                c.enableCameraCompatSplitScreenAspectRatio(false);
+            });
+        }
 
-    private Task setupTask(@WindowingMode int windowingMode) {
-        final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
-        spyOn(tda);
-        doReturn(true).when(tda).supportsNonResizableMultiWindow();
+        private void setupCameraManager() {
+            final CameraManager mockCameraManager = mock(CameraManager.class);
+            doAnswer(invocation -> {
+                mCameraAvailabilityCallback = invocation.getArgument(1);
+                return null;
+            }).when(mockCameraManager).registerAvailabilityCallback(
+                    any(Executor.class), any(CameraManager.AvailabilityCallback.class));
 
-        final Task task = new TaskBuilder(mSupervisor)
-                .setDisplay(mDisplayContent)
-                .setWindowingMode(windowingMode)
-                .build();
-        task.setBounds(0, 0, 1000, 500);
-        return task;
-    }
+            doReturn(mockCameraManager).when(mWindowTestsBase.mWm.mContext).getSystemService(
+                    CameraManager.class);
+        }
 
-    private void setupActivity(@NonNull Task task, @ScreenOrientation int activityOrientation,
-            @WindowingMode int windowingMode) {
-        mActivity = new ActivityBuilder(mAtm)
-                // Set the component to be that of the test class in order to enable compat changes
-                .setComponent(ComponentName.createRelative(mContext,
-                        com.android.server.wm.CameraCompatFreeformPolicyTests.class.getName()))
-                .setScreenOrientation(activityOrientation)
-                .setResizeMode(RESIZE_MODE_RESIZEABLE)
-                .setCreateTask(true)
-                .setOnTop(true)
-                .setTask(task)
-                .build();
-        mActivity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
+        private void setupHandler() {
+            final Handler handler = activity().top().mWmService.mH;
+            spyOn(handler);
 
-        spyOn(mActivity.mAppCompatController.getCameraOverrides());
-        spyOn(mActivity.info);
+            doAnswer(invocation -> {
+                ((Runnable) invocation.getArgument(0)).run();
+                return null;
+            }).when(handler).postDelayed(any(Runnable.class), anyLong());
+        }
 
-        doReturn(mActivity).when(mDisplayContent).topRunningActivity(anyBoolean());
-        doReturn(windowingMode == WINDOWING_MODE_FREEFORM).when(mActivity)
-                .inFreeformWindowingMode();
-    }
+        private void configureActivity(@ScreenOrientation int activityOrientation) {
+            configureActivity(activityOrientation, WINDOWING_MODE_FREEFORM);
+        }
 
-    private void onCameraOpened(@NonNull String cameraId, @NonNull String packageName) {
-        mCameraAvailabilityCallback.onCameraOpened(cameraId, packageName);
-        waitHandlerIdle(mDisplayContent.mWmService.mH);
-    }
+        private void configureActivity(@ScreenOrientation int activityOrientation,
+                @WindowingMode int windowingMode) {
+            configureActivityAndDisplay(activityOrientation, ORIENTATION_PORTRAIT, windowingMode);
+        }
 
-    private void onCameraClosed(@NonNull String cameraId) {
-        mCameraAvailabilityCallback.onCameraClosed(cameraId);
-        waitHandlerIdle(mDisplayContent.mWmService.mH);
-    }
+        private void configureActivityAndDisplay(@ScreenOrientation int activityOrientation,
+                @Orientation int naturalOrientation, @WindowingMode int windowingMode) {
+            applyOnActivity(a -> {
+                dw().allowEnterDesktopMode(true);
+                a.createActivityWithComponentInNewTaskAndDisplay();
+                a.setIgnoreOrientationRequest(true);
+                a.rotateDisplayForTopActivity(ROTATION_90);
+                a.configureTopActivity(/* minAspect */ -1, /* maxAspect */ -1,
+                        activityOrientation, /* isUnresizable */ false);
+                a.top().setWindowingMode(windowingMode);
+                a.displayContent().setWindowingMode(windowingMode);
+                a.setDisplayNaturalOrientation(naturalOrientation);
+                spyOn(a.top().mAppCompatController.getCameraOverrides());
+                spyOn(a.top().info);
+                doReturn(a.displayContent().getDisplayInfo()).when(
+                        a.displayContent().mWmService.mDisplayManagerInternal).getDisplayInfo(
+                        a.displayContent().mDisplayId);
+            });
+        }
 
-    private void assertInCameraCompatMode(@CameraCompatTaskInfo.FreeformCameraCompatMode int mode) {
-        assertEquals(mode, mCameraCompatFreeformPolicy.getCameraCompatMode(mActivity));
-    }
+        private void onCameraOpened(@NonNull String cameraId, @NonNull String packageName) {
+            mCameraAvailabilityCallback.onCameraOpened(cameraId, packageName);
+            waitHandlerIdle();
+        }
 
-    private void assertNotInCameraCompatMode() {
-        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_NONE);
-    }
+        private void onCameraClosed(@NonNull String cameraId) {
+            mCameraAvailabilityCallback.onCameraClosed(cameraId);
+        }
 
-    private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception {
-        assertActivityRefreshRequested(refreshRequested, /* cycleThroughStop*/ true);
-    }
+        private void waitHandlerIdle() {
+            mWindowTestsBase.waitHandlerIdle(activity().displayContent().mWmService.mH);
+        }
 
-    private void assertActivityRefreshRequested(boolean refreshRequested,
-            boolean cycleThroughStop) throws Exception {
-        verify(mActivity.mAppCompatController.getCameraOverrides(),
-                times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true);
+        void setInFreeformWindowingMode(boolean inFreeform) {
+            doReturn(inFreeform).when(activity().top()).inFreeformWindowingMode();
+        }
 
-        final RefreshCallbackItem refreshCallbackItem =
-                new RefreshCallbackItem(mActivity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
-        final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(mActivity.token,
-                /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
+        void setShouldRefreshActivityViaPause(boolean enabled) {
+            doReturn(enabled).when(activity().top().mAppCompatController.getCameraOverrides())
+                    .shouldRefreshActivityViaPauseForCameraCompat();
+        }
 
-        verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
-                .scheduleTransactionItems(mActivity.app.getThread(),
-                        refreshCallbackItem, resumeActivityItem);
-    }
+        void checkShouldRefreshActivity(boolean expected, Configuration newConfig,
+                Configuration oldConfig) {
+            assertEquals(expected, cameraCompatFreeformPolicy().shouldRefreshActivity(
+                    activity().top(),  newConfig, oldConfig));
+        }
 
-    private void callOnActivityConfigurationChanging(ActivityRecord activity) {
-        callOnActivityConfigurationChanging(activity, /* letterboxNew= */ true,
-                /* lastLetterbox= */false);
-    }
+        void checkCameraCompatPolicyNotCreated() {
+            assertNull(cameraCompatFreeformPolicy());
+        }
 
-    private void callOnActivityConfigurationChanging(ActivityRecord activity, boolean letterboxNew,
-            boolean lastLetterbox) {
-        mDisplayContent.mAppCompatCameraPolicy.mActivityRefresher
-                .onActivityConfigurationChanging(activity,
-                /* newConfig */ createConfiguration(letterboxNew),
-                /* lastReportedConfig */ createConfiguration(lastLetterbox));
-    }
+        void checkIsCameraRunningAndWindowingModeEligible(boolean expected) {
+            assertEquals(expected, cameraCompatFreeformPolicy()
+                    .isCameraRunningAndWindowingModeEligible(activity().top()));
+        }
 
-    private Configuration createConfiguration(boolean letterbox) {
-        final Configuration configuration = new Configuration();
-        Rect bounds = letterbox ? new Rect(/*left*/ 300, /*top*/ 0, /*right*/ 700, /*bottom*/ 600)
-                : new Rect(/*left*/ 0, /*top*/ 0, /*right*/ 1000, /*bottom*/ 600);
-        configuration.windowConfiguration.setAppBounds(bounds);
-        return configuration;
-    }
+        void checkIsFreeformLetterboxingForCameraAllowed(boolean expected) {
+            assertEquals(expected, cameraCompatFreeformPolicy()
+                    .isFreeformLetterboxingForCameraAllowed(activity().top()));
+        }
 
-    private void setDisplayRotation(@Surface.Rotation int displayRotation) {
-        doAnswer(invocation -> {
-            DisplayInfo displayInfo = new DisplayInfo();
-            mDisplayContent.getDisplay().getDisplayInfo(displayInfo);
-            displayInfo.rotation = displayRotation;
-            // Set height so that the natural orientation (rotation is 0) is portrait. This is the
-            // case for most standard phones and tablets.
-            // TODO(b/365725400): handle landscape natural orientation.
-            displayInfo.logicalHeight = displayRotation % 180 == 0 ? 800 : 600;
-            displayInfo.logicalWidth = displayRotation % 180 == 0 ? 600 : 800;
-            return displayInfo;
-        }).when(mDisplayContent.mWmService.mDisplayManagerInternal)
-                .getDisplayInfo(anyInt());
-    }
+        void checkCameraCompatAspectRatioEquals(float aspectRatio) {
+            assertEquals(aspectRatio,
+                    cameraCompatFreeformPolicy().getCameraCompatAspectRatio(activity().top()),
+                    /* delta= */ 0.001);
+        }
 
-    private void setupMockApplicationThread() {
-        IApplicationThread mockApplicationThread = mock(IApplicationThread.class);
-        spyOn(mActivity.app);
-        doReturn(mockApplicationThread).when(mActivity.app).getThread();
-    }
+        private void assertInCameraCompatMode(@FreeformCameraCompatMode int mode) {
+            assertEquals(mode, cameraCompatFreeformPolicy().getCameraCompatMode(activity().top()));
+        }
 
-    private void assertCompatibilityInfoSentWithDisplayRotation(@Surface.Rotation int
-            expectedRotation) throws Exception {
-        final ArgumentCaptor<CompatibilityInfo> compatibilityInfoArgumentCaptor =
-                ArgumentCaptor.forClass(CompatibilityInfo.class);
-        verify(mActivity.app.getThread()).updatePackageCompatibilityInfo(eq(mActivity.packageName),
-                compatibilityInfoArgumentCaptor.capture());
+        private void assertNotInCameraCompatMode() {
+            assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_NONE);
+        }
 
-        final CompatibilityInfo compatInfo = compatibilityInfoArgumentCaptor.getValue();
-        assertTrue(compatInfo.isOverrideDisplayRotationRequired());
-        assertEquals(expectedRotation, compatInfo.applicationDisplayRotation);
+        private void assertActivityRefreshRequested(boolean refreshRequested) {
+            assertActivityRefreshRequested(refreshRequested, /* cycleThroughStop*/ true);
+        }
+
+        private void assertActivityRefreshRequested(boolean refreshRequested,
+                boolean cycleThroughStop) {
+            verify(activity().top().mAppCompatController.getCameraOverrides(),
+                    times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true);
+
+            final RefreshCallbackItem refreshCallbackItem =
+                    new RefreshCallbackItem(activity().top().token,
+                            cycleThroughStop ? ON_STOP : ON_PAUSE);
+            final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(
+                    activity().top().token,
+                    /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
+            try {
+                verify(activity().top().mAtmService.getLifecycleManager(),
+                        times(refreshRequested ? 1 : 0))
+                        .scheduleTransactionItems(activity().top().app.getThread(),
+                                refreshCallbackItem, resumeActivityItem);
+            } catch (RemoteException e) {
+                fail(e.getMessage());
+            }
+        }
+
+        private void callOnActivityConfigurationChanging() {
+            callOnActivityConfigurationChanging(/* letterboxNew= */ true,
+                    /* lastLetterbox= */false);
+        }
+
+        private void callOnActivityConfigurationChanging(boolean letterboxNew,
+                boolean lastLetterbox) {
+            activity().displayContent().mAppCompatCameraPolicy.mActivityRefresher
+                    .onActivityConfigurationChanging(activity().top(),
+                            /* newConfig */ createConfiguration(letterboxNew),
+                            /* lastReportedConfig */ createConfiguration(lastLetterbox));
+        }
+
+        void checkIsCameraCompatTreatmentActiveForTopActivity(boolean active) {
+            assertEquals(active,
+                    cameraCompatFreeformPolicy().isTreatmentEnabledForActivity(activity().top(),
+                            /* checkOrientation */ true));
+        }
+
+        void setOverrideMinAspectRatioEnabled(boolean enabled) {
+            doReturn(enabled).when(activity().top().mAppCompatController.getCameraOverrides())
+                    .isOverrideMinAspectRatioForCameraEnabled();
+        }
+
+        void assertCompatibilityInfoSentWithDisplayRotation(@Surface.Rotation int
+                expectedRotation) {
+            final ArgumentCaptor<CompatibilityInfo> compatibilityInfoArgumentCaptor =
+                    ArgumentCaptor.forClass(CompatibilityInfo.class);
+            try {
+                verify(activity().top().app.getThread()).updatePackageCompatibilityInfo(
+                        eq(activity().top().packageName),
+                        compatibilityInfoArgumentCaptor.capture());
+            } catch (RemoteException e) {
+                fail(e.getMessage());
+            }
+
+            final CompatibilityInfo compatInfo = compatibilityInfoArgumentCaptor.getValue();
+            assertTrue(compatInfo.isOverrideDisplayRotationRequired());
+            assertEquals(expectedRotation, compatInfo.applicationDisplayRotation);
+        }
+
+        CameraCompatFreeformPolicy cameraCompatFreeformPolicy() {
+            return activity().displayContent().mAppCompatCameraPolicy.mCameraCompatFreeformPolicy;
+        }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index 76b994d..ad76662 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -31,6 +31,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -51,6 +52,7 @@
 import android.graphics.Rect;
 import android.media.projection.StopReason;
 import android.os.IBinder;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.view.ContentRecordingSession;
 import android.view.Display;
@@ -558,6 +560,22 @@
         assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
     }
 
+    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+    @Test
+    public void testStartRecording_shouldShowSystemDecorations_recordingNotStarted() {
+        defaultInit();
+        mContentRecorder.setContentRecordingSession(mTaskSession);
+
+        spyOn(mVirtualDisplayContent.mDisplay);
+        doReturn(true).when(mVirtualDisplayContent.mDisplay).canHostTasks();
+
+        // WHEN a recording tries to start.
+        mContentRecorder.updateRecording();
+
+        // THEN recording does not start.
+        assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
+    }
+
     @Test
     public void testOnVisibleRequestedChanged_notifiesCallback() {
         defaultInit();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java
index 1e91bed..43755ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java
@@ -181,6 +181,7 @@
         assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue();
     }
 
+    @DisableFlags(Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE)
     @Test
     public void isDeviceEligibleForDesktopMode_configDEModeOffAndIntDispHostsDesktop_returnsFalse() {
         doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index bc37496..00b617e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE;
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE;
@@ -40,6 +41,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightPx;
 import static com.android.server.wm.DesktopModeBoundsCalculator.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
 import static com.android.server.wm.DesktopModeBoundsCalculator.DESKTOP_MODE_LANDSCAPE_APP_PADDING;
 import static com.android.server.wm.DesktopModeBoundsCalculator.centerInScreen;
@@ -157,7 +159,7 @@
     @Test
     @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
             Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
-    public void testReturnsContinueIfVisibleFreeformTaskExists() {
+    public void testReturnsContinueIfFreeformTaskExists() {
         setupDesktopModeLaunchParamsModifier();
         when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
 
@@ -165,7 +167,7 @@
         final Task existingFreeformTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         doReturn(existingFreeformTask.getRootActivity()).when(dc)
-                .getTopMostVisibleFreeformActivity();
+                .getTopMostFreeformActivity();
         final Task launchingTask = new TaskBuilder(mSupervisor).build();
         launchingTask.onDisplayChanged(dc);
 
@@ -269,6 +271,38 @@
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_INHERIT_TASK_BOUNDS_FOR_TRAMPOLINE_TASK_LAUNCHES})
+    public void testInheritTaskBoundsFromExistingInstanceIfClosing() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final String packageName = "com.same.package";
+        // Setup existing task.
+        final DisplayContent dc = spy(createNewDisplay());
+        final Task existingFreeformTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).setPackage(packageName).build();
+        existingFreeformTask.setBounds(
+                /* left */ 0,
+                /* top */ 0,
+                /* right */ 500,
+                /* bottom */ 500);
+        doReturn(existingFreeformTask.getRootActivity()).when(dc)
+                .getTopMostVisibleFreeformActivity();
+        // Set up new instance of already existing task. By default multi instance is not supported
+        // so first instance will close.
+        final Task launchingTask = new TaskBuilder(mSupervisor).setPackage(packageName)
+                .setCreateActivity(true).build();
+        launchingTask.onDisplayChanged(dc);
+        launchingTask.intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+
+        // New instance should inherit task bounds of old instance.
+        assertEquals(RESULT_DONE,
+                new CalculateRequestBuilder().setTask(launchingTask)
+                        .setActivity(launchingTask.getRootActivity()).calculate());
+        assertEquals(existingFreeformTask.getBounds(), mResult.mBounds);
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     @DisableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
     public void testUsesDesiredBoundsIfEmptyLayoutAndActivityOptionsBounds() {
@@ -349,7 +383,7 @@
         spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
         doReturn(true).when(
                         mActivity.mAppCompatController.getAspectRatioOverrides())
-                .isUserFullscreenOverrideEnabled();
+                .hasFullscreenOverride();
 
         final int desiredWidth =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -377,7 +411,7 @@
         spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
         doReturn(true).when(
                         mActivity.mAppCompatController.getAspectRatioOverrides())
-                .isSystemOverrideToFullscreenEnabled();
+                .hasFullscreenOverride();
 
         final int desiredWidth =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -860,9 +894,11 @@
 
     @Test
     @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS})
+            Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS,
+            Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS})
     public void testDefaultLandscapeBounds_landscapeDevice_unResizable_landscapeOrientation() {
         setupDesktopModeLaunchParamsModifier();
+        final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext);
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
                 LANDSCAPE_DISPLAY_BOUNDS);
@@ -870,11 +906,11 @@
         final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
                 task, /* ignoreOrientationRequest */ true);
 
-
-        final int desiredWidth =
-                (int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final float displayAspectRatio = (float) LANDSCAPE_DISPLAY_BOUNDS.width()
+                / LANDSCAPE_DISPLAY_BOUNDS.height();
         final int desiredHeight =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth = (int) ((desiredHeight - captionHeight) * displayAspectRatio);
 
         assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
                 .setActivity(activity).calculate());
@@ -883,7 +919,8 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    @EnableFlags({Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS,
+            Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS})
     public void testUnResizablePortraitBounds_landscapeDevice_unResizable_portraitOrientation() {
         setupDesktopModeLaunchParamsModifier();
 
@@ -892,6 +929,7 @@
         final Task task = createTask(display, /* isResizeable */ false);
         final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
                 task, /* ignoreOrientationRequest */ true);
+        final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext);
 
         spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
         doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
@@ -899,7 +937,7 @@
 
         final int desiredHeight =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
-        final int desiredWidth = (int) (desiredHeight / LETTERBOX_ASPECT_RATIO);
+        final int desiredWidth = (int) ((desiredHeight - captionHeight) / LETTERBOX_ASPECT_RATIO);
 
         assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
                 .setActivity(activity).calculate());
@@ -1037,7 +1075,8 @@
 
     @Test
     @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS})
+            Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS,
+            Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS})
     public void testDefaultPortraitBounds_portraitDevice_unResizable_portraitOrientation() {
         setupDesktopModeLaunchParamsModifier();
 
@@ -1046,12 +1085,14 @@
         final Task task = createTask(display, /* isResizeable */ false);
         final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
                 task, /* ignoreOrientationRequest */ true);
+        final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext);
 
-
-        final int desiredWidth =
-                (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final float displayAspectRatio = (float) PORTRAIT_DISPLAY_BOUNDS.height()
+                / PORTRAIT_DISPLAY_BOUNDS.width();
         final int desiredHeight =
-                (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+                (int) (PORTRAIT_DISPLAY_BOUNDS.height()  * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth =
+                (int) ((desiredHeight - captionHeight) / displayAspectRatio);
 
         assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
                 .setActivity(activity).calculate());
@@ -1060,7 +1101,8 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    @EnableFlags({Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS,
+            Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS})
     public void testUnResizableLandscapeBounds_portraitDevice_unResizable_landscapeOrientation() {
         setupDesktopModeLaunchParamsModifier();
 
@@ -1069,6 +1111,7 @@
         final Task task = createTask(display, /* isResizeable */ false);
         final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
                 task, /* ignoreOrientationRequest */ true);
+        final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext);
 
         spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
         doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
@@ -1076,7 +1119,7 @@
 
         final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width()
                 - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
-        final int desiredHeight = (int) (desiredWidth / LETTERBOX_ASPECT_RATIO);
+        final int desiredHeight = (int) (desiredWidth / LETTERBOX_ASPECT_RATIO) + captionHeight;
 
         assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
                 .setActivity(activity).calculate());
@@ -1127,6 +1170,32 @@
     @Test
     @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
             Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
+    public void testOptionsBoundsSet_flexibleLaunchSizeWithFullscreenOverride_noModifications() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
+        final Task task = new TaskBuilder(mSupervisor).setActivityType(
+                ACTIVITY_TYPE_STANDARD).setDisplay(display).build();
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchBounds(new Rect(
+                        DISPLAY_STABLE_BOUNDS.left,
+                        DISPLAY_STABLE_BOUNDS.top,
+                        /* right = */ 500,
+                        /* bottom = */ 500))
+                .setFlexibleLaunchSize(true);
+        spyOn(mActivity.mAppCompatController.getAspectRatioOverrides());
+        doReturn(true).when(
+                        mActivity.mAppCompatController.getAspectRatioOverrides())
+                .hasFullscreenOverride();
+
+        assertEquals(RESULT_DONE,
+                new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+        assertEquals(options.getLaunchBounds(), mResult.mBounds);
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
     public void testOptionsBoundsSet_flexibleLaunchSize_boundsSizeModified() {
         setupDesktopModeLaunchParamsModifier();
 
@@ -1450,6 +1519,24 @@
         assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
     }
 
+    @Test
+    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+    public void testFreeformWindowingModeAppliedIfSourceTaskExists() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final Task task = new TaskBuilder(mSupervisor).setActivityType(
+                ACTIVITY_TYPE_STANDARD).build();
+        final Task sourceTask = new TaskBuilder(mSupervisor).setActivityType(
+                ACTIVITY_TYPE_STANDARD).setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+        final ActivityRecord sourceActivity = new ActivityBuilder(task.mAtmService)
+                .setTask(sourceTask).build();
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setSource(sourceActivity).calculate());
+        assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
+    }
+
     private Task createTask(DisplayContent display, Boolean isResizeable) {
         final int resizeMode = isResizeable ? RESIZE_MODE_RESIZEABLE
                 : RESIZE_MODE_UNRESIZEABLE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index a30591e..9486bc5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -25,6 +25,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -432,4 +433,32 @@
         verify(mTransaction, never()).setAlpha(dimLayer, 0.5f);
         verify(mTransaction).setAlpha(dimLayer, 0.9f);
     }
+
+    /**
+     * A window requesting to dim to 0 and without blur would cause the dim to be created and
+     * destroyed continuously.
+     * Ensure the dim layer is not created until the window is requesting valid values.
+     */
+    @Test
+    public void testDimNotCreatedIfNoAlphaNoBlur() {
+        mDimmer.adjustAppearance(mChild1, 0.0f, 0);
+        mDimmer.adjustPosition(mChild1, mChild1);
+        assertNull(mDimmer.getDimLayer());
+        mDimmer.updateDims(mTransaction);
+        assertNull(mDimmer.getDimLayer());
+
+        mDimmer.adjustAppearance(mChild1, 0.9f, 0);
+        mDimmer.adjustPosition(mChild1, mChild1);
+        assertNotNull(mDimmer.getDimLayer());
+    }
+
+    /**
+     * If there is a blur, then the dim layer is created even though alpha is 0
+     */
+    @Test
+    public void testDimCreatedIfNoAlphaButHasBlur() {
+        mDimmer.adjustAppearance(mChild1, 0.0f, 10);
+        mDimmer.adjustPosition(mChild1, mChild1);
+        assertNotNull(mDimmer.getDimLayer());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 0af41ea..89aa3b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -56,7 +56,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.pm.ActivityInfo;
@@ -174,7 +174,7 @@
         da1.reduceOnAllTaskDisplayAreas(callback2, 0);
         da1.getItemFromTaskDisplayAreas(callback3);
 
-        verifyZeroInteractions(da2);
+        verifyNoMoreInteractions(da2);
 
         // Traverse the child if the current DA has type ANY
         final DisplayArea<WindowContainer> da3 = new DisplayArea<>(mWm, ANY, "DA3");
@@ -207,7 +207,7 @@
         da5.reduceOnAllTaskDisplayAreas(callback2, 0);
         da5.getItemFromTaskDisplayAreas(callback3);
 
-        verifyZeroInteractions(da6);
+        verifyNoMoreInteractions(da6);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayCompatTests.java
new file mode 100644
index 0000000..1445a69
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayCompatTests.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.window.flags.Flags.FLAG_ENABLE_RESTART_MENU_FOR_CONNECTED_DISPLAYS;
+
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertTrue;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:DisplayCompatTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class DisplayCompatTests extends WindowTestsBase {
+
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    @EnableFlags(FLAG_ENABLE_RESTART_MENU_FOR_CONNECTED_DISPLAYS)
+    @Test
+    public void testFixedMiscConfigurationWhenMovingToDisplay() {
+        // Create an app on the default display, at which point the restart menu isn't enabled.
+        final Task task = createTask(mDefaultDisplay);
+        final ActivityRecord activity = createActivityRecord(task);
+        assertFalse(task.getTaskInfo().appCompatTaskInfo.isRestartMenuEnabledForDisplayMove());
+
+        // Move the app to a secondary display, and the restart menu must get enabled.
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.copyFrom(mDisplayInfo);
+        displayInfo.displayId = DEFAULT_DISPLAY + 1;
+        final DisplayContent secondaryDisplay = createNewDisplay(displayInfo);
+        task.reparent(secondaryDisplay.getDefaultTaskDisplayArea(), true);
+        assertTrue(task.getTaskInfo().appCompatTaskInfo.isRestartMenuEnabledForDisplayMove());
+
+        // Once the app gets restarted, the restart menu must be gone.
+        activity.restartProcessIfVisible();
+        assertFalse(task.getTaskInfo().appCompatTaskInfo.isRestartMenuEnabledForDisplayMove());
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
index 7033d79..9ce4a80 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -43,7 +43,6 @@
 
 import com.android.server.LocalServices;
 import com.android.server.wm.TransitionController.OnStartCollect;
-import com.android.window.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -218,7 +217,6 @@
 
     @Test
     public void testWaitForTransition_displaySwitching_waitsForTransitionToBeStarted() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
         mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true);
         boolean willWait = mDisplayContent.mDisplayUpdater.waitForTransition(mScreenUnblocker);
         assertThat(willWait).isTrue();
@@ -241,7 +239,6 @@
 
     @Test
     public void testWaitForTransition_displayNotSwitching_doesNotWait() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
         mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ false);
 
         boolean willWait = mDisplayContent.mDisplayUpdater.waitForTransition(mScreenUnblocker);
@@ -251,19 +248,7 @@
     }
 
     @Test
-    public void testWaitForTransition_displayIsSwitchingButFlagDisabled_doesNotWait() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
-        mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true);
-
-        boolean willWait = mDisplayContent.mDisplayUpdater.waitForTransition(mScreenUnblocker);
-
-        assertThat(willWait).isFalse();
-        verify(mScreenUnblocker, never()).sendToTarget();
-    }
-
-    @Test
     public void testTwoDisplayUpdateAtTheSameTime_bothDisplaysAreUnblocked() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
         prepareSecondaryDisplay();
 
         final WindowState defaultDisplayWindow = newWindowBuilder("DefaultDisplayWindow",
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4c81f73..ed00a9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -30,7 +30,10 @@
 import static android.os.Build.VERSION_CODES.P;
 import static android.os.Build.VERSION_CODES.Q;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_ALLOWS_CONTENT_MODE_SWITCH;
 import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.view.Display.FLAG_TRUSTED;
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
 import static android.view.Surface.ROTATION_0;
@@ -1706,8 +1709,6 @@
         app.setVisible(true);
         doReturn(false).when(app).inTransition();
         mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
-        mStatusBarWindow.finishSeamlessRotation(t);
-        mNavBarWindow.finishSeamlessRotation(t);
 
         // The fixed rotation should be cleared and the new rotation is applied to display.
         assertFalse(app.hasFixedRotationTransform());
@@ -2925,6 +2926,63 @@
         assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
     }
 
+    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+    @Test
+    public void testSetShouldShowSystemDecorations_shouldShowSystemDecorationsDisplay() {
+        // Set up a non-default display with FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS enabled
+        final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+        displayInfo.displayId = DEFAULT_DISPLAY + 1;
+        displayInfo.flags = FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+        final DisplayContent dc = createNewDisplay(displayInfo);
+
+        dc.onDisplayInfoChangeApplied();
+        assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+    }
+
+    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+    @Test
+    public void testSetShouldShowSystemDecorations_notAllowContentModeSwitchDisplay() {
+        // Set up a non-default display without FLAG_ALLOWS_CONTENT_MODE_SWITCH enabled
+        final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+        displayInfo.displayId = DEFAULT_DISPLAY + 1;
+        displayInfo.flags = FLAG_TRUSTED;
+        final DisplayContent dc = createNewDisplay(displayInfo);
+
+        dc.onDisplayInfoChangeApplied();
+        assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+    }
+
+    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+    @Test
+    public void testSetShouldShowSystemDecorations_untrustedDisplay() {
+        // Set up a non-default display without FLAG_TRUSTED enabled
+        final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+        displayInfo.displayId = DEFAULT_DISPLAY + 1;
+        displayInfo.flags = FLAG_ALLOWS_CONTENT_MODE_SWITCH;
+        final DisplayContent dc = createNewDisplay(displayInfo);
+
+        dc.onDisplayInfoChangeApplied();
+        assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+    }
+
+    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+    @Test
+    public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
+        final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
+        displayInfo.displayId = DEFAULT_DISPLAY + 1;
+        displayInfo.flags = (FLAG_ALLOWS_CONTENT_MODE_SWITCH | FLAG_TRUSTED);
+        final DisplayContent dc = createNewDisplay(displayInfo);
+
+        spyOn(dc.mDisplay);
+        doReturn(false).when(dc.mDisplay).canHostTasks();
+        dc.onDisplayInfoChangeApplied();
+        assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+
+        doReturn(true).when(dc.mDisplay).canHostTasks();
+        dc.onDisplayInfoChangeApplied();
+        assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
+    }
+
     @EnableFlags(FLAG_ENABLE_PERSISTING_DISPLAY_SIZE_FOR_CONNECTED_DISPLAYS)
     @Test
     public void testForcedDensityRatioSetForExternalDisplays_persistDensityScaleFlagEnabled() {
@@ -2993,23 +3051,6 @@
         assertEquals(320, displayContent.mBaseDisplayDensity);
     }
 
-    @EnableFlags(FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
-    @Test
-    public void testSetShouldShowSystemDecorations_nonDefaultNonPrivateDisplay() {
-        final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo);
-        displayInfo.displayId = DEFAULT_DISPLAY + 1;
-        final DisplayContent dc = createNewDisplay(displayInfo);
-
-        spyOn(dc.mDisplay);
-        doReturn(false).when(dc.mDisplay).canHostTasks();
-        dc.onDisplayInfoChangeApplied();
-        assertFalse(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
-
-        doReturn(true).when(dc.mDisplay).canHostTasks();
-        dc.onDisplayInfoChangeApplied();
-        assertTrue(dc.mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(dc));
-    }
-
     private void removeRootTaskTests(Runnable runnable) {
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 81e487a..521d836 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -37,7 +37,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 
 import android.annotation.NonNull;
 import android.app.WindowConfiguration;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index 7d59f48..aa794b2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -19,12 +19,25 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
+import static com.android.server.wm.WindowStateAnimator.NO_SURFACE;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.graphics.PixelFormat;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.view.WindowInsets;
 import android.view.inputmethod.Flags;
 import android.view.inputmethod.ImeTracker;
 
@@ -211,4 +224,77 @@
         mImeProvider.setFrozen(false);
         assertFalse(mImeProvider.getSource().isVisible());
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+    public void testUpdateControlForTarget_remoteInsetsControlTarget() throws RemoteException {
+        final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
+        makeWindowVisibleAndDrawn(ime);
+        mImeProvider.setWindowContainer(ime, null, null);
+        mImeProvider.setServerVisible(true);
+        mImeProvider.setClientVisible(true);
+        final WindowState inputTarget = newWindowBuilder("app", TYPE_APPLICATION).build();
+        final var displayWindowInsetsController = spy(createDisplayWindowInsetsController());
+        mDisplayContent.setRemoteInsetsController(displayWindowInsetsController);
+        final var controlTarget = mDisplayContent.mRemoteInsetsControlTarget;
+
+        inputTarget.setRequestedVisibleTypes(
+                WindowInsets.Type.defaultVisible() | WindowInsets.Type.ime());
+        mDisplayContent.setImeInputTarget(inputTarget);
+        mDisplayContent.setImeControlTarget(controlTarget);
+
+        assertTrue(inputTarget.isRequestedVisible(WindowInsets.Type.ime()));
+        assertFalse(controlTarget.isRequestedVisible(WindowInsets.Type.ime()));
+        mImeProvider.updateControlForTarget(controlTarget, true /* force */, null /* statsToken */);
+        verify(displayWindowInsetsController, times(1)).setImeInputTargetRequestedVisibility(
+                eq(true), any());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+    public void testOnPostLayout_resetServerVisibilityWhenImeIsNotDrawn() {
+        final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
+        final WindowState inputTarget = newWindowBuilder("app", TYPE_APPLICATION).build();
+        makeWindowVisibleAndDrawn(ime);
+        mImeProvider.setWindowContainer(ime, null, null);
+        mImeProvider.setServerVisible(true);
+        mImeProvider.setClientVisible(true);
+        mImeProvider.updateVisibility();
+        mImeProvider.updateControlForTarget(inputTarget, true /* force */, null /* statsToken */);
+
+        // Calling onPostLayout, as the drawn state is initially false.
+        mImeProvider.onPostLayout();
+        assertTrue(mImeProvider.isSurfaceVisible());
+
+        // Reset window's drawn state
+        ime.mWinAnimator.mDrawState = NO_SURFACE;
+        mImeProvider.onPostLayout();
+        assertFalse(mImeProvider.isServerVisible());
+        assertFalse(mImeProvider.isSurfaceVisible());
+
+        // Set it back to drawn
+        ime.mWinAnimator.mDrawState = HAS_DRAWN;
+        mImeProvider.onPostLayout();
+        assertTrue(mImeProvider.isServerVisible());
+        assertTrue(mImeProvider.isSurfaceVisible());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+    public void testUpdateControlForTarget_differentControlTarget() throws RemoteException {
+        final WindowState oldTarget = newWindowBuilder("app", TYPE_APPLICATION).build();
+        final WindowState newTarget = newWindowBuilder("newapp", TYPE_APPLICATION).build();
+
+        oldTarget.setRequestedVisibleTypes(
+                WindowInsets.Type.defaultVisible() | WindowInsets.Type.ime());
+        mDisplayContent.setImeControlTarget(oldTarget);
+        mDisplayContent.setImeInputTarget(newTarget);
+
+        // Having a null windowContainer will early return in updateControlForTarget
+        mImeProvider.setWindowContainer(null, null, null);
+
+        clearInvocations(mDisplayContent);
+        mImeProvider.updateControlForTarget(newTarget, false /* force */, ImeTracker.Token.empty());
+        verify(mDisplayContent, never()).getImeInputTarget();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 71e34ef..3c6a898 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -93,7 +93,7 @@
 
         final Task task1 = createTask(mDisplayContent);
         final Task task2 = createTask(mDisplayContent);
-        task1.setAdjacentTaskFragment(task2);
+        task1.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(task1, task2));
         final WindowState win = createAppWindow(task1, WINDOWING_MODE_MULTI_WINDOW, "app");
         final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index 67a95de..ae005f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -44,6 +44,7 @@
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo.WindowLayout;
 import android.graphics.Rect;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
 import android.util.SparseArray;
@@ -52,6 +53,7 @@
 
 import com.android.server.wm.LaunchParamsController.LaunchParams;
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
+import com.android.window.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -115,6 +117,7 @@
         expected.mPreferredTaskDisplayArea = mock(TaskDisplayArea.class);
         expected.mWindowingMode = WINDOWING_MODE_PINNED;
         expected.mBounds.set(200, 300, 400, 500);
+        expected.mNeedsSafeRegionBounds = true;
 
         mPersister.putLaunchParams(userId, name, expected);
 
@@ -187,6 +190,7 @@
         params.mWindowingMode = WINDOWING_MODE_FREEFORM;
         params.mBounds.set(0, 0, 30, 20);
         params.mPreferredTaskDisplayArea = mock(TaskDisplayArea.class);
+        params.mNeedsSafeRegionBounds = true;
 
         final InstrumentedPositioner positioner2 = new InstrumentedPositioner(RESULT_CONTINUE,
                 params);
@@ -227,6 +231,158 @@
     }
 
     /**
+     * Tests only needs safe region bounds are not propagated if results are skipped.
+     */
+    @Test
+    public void testSkip_needsSafeRegionBoundsNotModified() {
+        final LaunchParams params1 = new LaunchParams();
+        params1.mNeedsSafeRegionBounds = true;
+        final InstrumentedPositioner positioner1 = new InstrumentedPositioner(RESULT_SKIP, params1);
+
+        final LaunchParams params2 = new LaunchParams();
+        params2.mNeedsSafeRegionBounds = false;
+        final InstrumentedPositioner positioner2 =
+                new InstrumentedPositioner(RESULT_CONTINUE, params2);
+
+        mController.registerModifier(positioner1);
+        mController.registerModifier(positioner2);
+
+        final LaunchParams result = new LaunchParams();
+
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+                null /*options*/, null /*request*/, PHASE_BOUNDS, result);
+
+        assertEquals(result, positioner2.getLaunchParams());
+    }
+
+    /**
+     * Tests only needs safe region bounds are propagated even if results are continued.
+     */
+    @Test
+    public void testContinue_needsSafeRegionBoundsCarriedOver() {
+        final LaunchParams params1 = new LaunchParams();
+        final InstrumentedPositioner positioner1 =
+                new InstrumentedPositioner(RESULT_CONTINUE, params1);
+
+        final LaunchParams params2 = new LaunchParams();
+        params2.mNeedsSafeRegionBounds = true;
+        final InstrumentedPositioner positioner2 =
+                new InstrumentedPositioner(RESULT_CONTINUE, params2);
+
+        mController.registerModifier(positioner1);
+        mController.registerModifier(positioner2);
+
+        final LaunchParams result = new LaunchParams();
+
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+                null /*options*/, null /*request*/, PHASE_BOUNDS, result);
+
+        // Safe region is propagated from positioner1
+        assertEquals(result.mNeedsSafeRegionBounds,
+                positioner2.getLaunchParams().mNeedsSafeRegionBounds);
+        assertEquals(result.mWindowingMode, positioner1.getLaunchParams().mWindowingMode);
+        assertEquals(result.mBounds, positioner1.getLaunchParams().mBounds);
+        assertEquals(result.mPreferredTaskDisplayArea,
+                positioner1.getLaunchParams().mPreferredTaskDisplayArea);
+    }
+
+    /**
+     * Tests needs safe region bounds are modified if results from the next continue have been set.
+     */
+    @Test
+    public void testContinue_needsSafeRegionBoundsModifiedFromLaterContinue() {
+        final LaunchParams params1 = new LaunchParams();
+        params1.mNeedsSafeRegionBounds = false;
+        final InstrumentedPositioner positioner1 =
+                new InstrumentedPositioner(RESULT_CONTINUE, params1);
+
+        final LaunchParams params2 = new LaunchParams();
+        params2.mNeedsSafeRegionBounds = true;
+        final InstrumentedPositioner positioner2 =
+                new InstrumentedPositioner(RESULT_CONTINUE, params2);
+
+        mController.registerModifier(positioner1);
+        mController.registerModifier(positioner2);
+
+        final LaunchParams result = new LaunchParams();
+
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+                null /*options*/, null /*request*/, PHASE_BOUNDS, result);
+
+        // Safe region is propagated from positioner1
+        assertEquals(result.mNeedsSafeRegionBounds,
+                positioner1.getLaunchParams().mNeedsSafeRegionBounds);
+        assertEquals(result.mWindowingMode, positioner2.getLaunchParams().mWindowingMode);
+        assertEquals(result.mBounds, positioner2.getLaunchParams().mBounds);
+        assertEquals(result.mPreferredTaskDisplayArea,
+                positioner2.getLaunchParams().mPreferredTaskDisplayArea);
+    }
+
+    /**
+     * Tests only needs safe region bounds are propagated to result done even if there are skipped
+     * and continued results and continue sets true for needs safe region bounds.
+     */
+    @Test
+    public void testDone_ContinueSetsNeedsSafeRegionBounds() {
+        final LaunchParams params1 = new LaunchParams();
+        final InstrumentedPositioner positioner1 = new InstrumentedPositioner(RESULT_DONE, params1);
+
+        final LaunchParams params2 = new LaunchParams();
+        final InstrumentedPositioner positioner2 =
+                new InstrumentedPositioner(RESULT_CONTINUE, params2);
+
+        final LaunchParams params3 = new LaunchParams();
+        final InstrumentedPositioner positioner3 = new InstrumentedPositioner(RESULT_SKIP, params3);
+
+        final LaunchParams params4 = new LaunchParams();
+        params4.mNeedsSafeRegionBounds = true;
+        final InstrumentedPositioner positioner4 =
+                new InstrumentedPositioner(RESULT_CONTINUE, params4);
+
+        final LaunchParams params5 = new LaunchParams();
+        params5.mNeedsSafeRegionBounds = false;
+        final InstrumentedPositioner positioner5 = new InstrumentedPositioner(RESULT_SKIP, params5);
+
+        mController.registerModifier(positioner1);
+        mController.registerModifier(positioner2);
+        mController.registerModifier(positioner3);
+        mController.registerModifier(positioner4);
+        mController.registerModifier(positioner5);
+
+        final LaunchParams result = new LaunchParams();
+
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+                null /*options*/, null /*request*/, PHASE_BOUNDS, result);
+
+        // Safe region is propagated from positioner4
+        assertEquals(result.mNeedsSafeRegionBounds,
+                positioner4.getLaunchParams().mNeedsSafeRegionBounds);
+        assertEquals(result.mWindowingMode, positioner1.getLaunchParams().mWindowingMode);
+        assertEquals(result.mBounds, positioner1.getLaunchParams().mBounds);
+        assertEquals(result.mPreferredTaskDisplayArea,
+                positioner1.getLaunchParams().mPreferredTaskDisplayArea);
+    }
+
+    /**
+     * Tests only needs safe region bounds are set if results are done.
+     */
+    @Test
+    public void testDone_needsSafeRegionBoundsModified() {
+        final LaunchParams params = new LaunchParams();
+        params.mNeedsSafeRegionBounds = true;
+        final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
+
+        mController.registerModifier(positioner);
+
+        final LaunchParams result = new LaunchParams();
+
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+                null /*options*/, null /*request*/, PHASE_BOUNDS, result);
+
+        assertEquals(result, positioner.getLaunchParams());
+    }
+
+    /**
      * Tests preferred display id calculation for VR.
      */
     @Test
@@ -372,6 +528,34 @@
         assertEquals(expected, task.mLastNonFullscreenBounds);
     }
 
+    /**
+     * Ensures that app bounds are set to exclude freeform caption if window is in freeform.
+     */
+    @Test
+    @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS)
+    public void testLayoutTaskBoundsFreeformAppBounds() {
+        final Rect expected = new Rect(10, 20, 30, 40);
+
+        final LaunchParams params = new LaunchParams();
+        params.mBounds.set(expected);
+        params.mAppBounds.set(expected);
+        final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
+        final Task task = new TaskBuilder(mAtm.mTaskSupervisor)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        final ActivityOptions options = ActivityOptions.makeBasic().setFlexibleLaunchSize(true);
+
+        mController.registerModifier(positioner);
+
+        assertNotEquals(expected, task.getBounds());
+
+        layoutTask(task, options);
+
+        // Task will make adjustments to requested bounds. We only need to guarantee that the
+        // requested bounds are expected.
+        assertEquals(expected,
+                task.getRequestedOverrideConfiguration().windowConfiguration.getAppBounds());
+    }
+
     public static class InstrumentedPositioner implements LaunchParamsModifier {
 
         private final int mReturnVal;
@@ -473,4 +657,9 @@
         mController.layoutTask(task, null /* layout */, null /* activity */, null /* source */,
                 null /* options */);
     }
+
+    private void layoutTask(@NonNull Task task, ActivityOptions options) {
+        mController.layoutTask(task, null /* layout */, null /* activity */, null /* source */,
+                options /* options */);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 66d7963..1356718 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -30,7 +30,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 
 import android.content.ComponentName;
 import android.content.pm.PackageManagerInternal;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 8a7e743..2c6884e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -52,7 +52,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -1293,7 +1293,7 @@
 
         // Add secondTask to top again
         mRecentTasks.add(secondTask);
-        verifyZeroInteractions(controller);
+        verifyNoMoreInteractions(controller);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index d3f3269..0101c3c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -284,7 +284,7 @@
         assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
         assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
 
-        overrideWindow.setAnimatingTypes(WindowInsets.Type.statusBars());
+        overrideWindow.setAnimatingTypes(WindowInsets.Type.statusBars(), null /* statsToken */);
         assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
         assertTrue(mPolicy.updateFrameRateVote(overrideWindow));
         assertEquals(FRAME_RATE_VOTE_NONE, overrideWindow.mFrameRateVote);
@@ -304,7 +304,7 @@
         assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
         assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
 
-        overrideWindow.setAnimatingTypes(WindowInsets.Type.statusBars());
+        overrideWindow.setAnimatingTypes(WindowInsets.Type.statusBars(), null /* statsToken */);
         assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
         assertTrue(mPolicy.updateFrameRateVote(overrideWindow));
         assertEquals(FRAME_RATE_VOTE_NONE, overrideWindow.mFrameRateVote);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 33a48aa..fc70b80 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -572,8 +572,8 @@
                 new TestDisplayContent.Builder(mAtm, 1000, 2000).build();
         final InputDevice device = new InputDevice.Builder()
                 .setAssociatedDisplayId(newDisplay.mDisplayId)
-                .setSources(InputDevice.SOURCE_TOUCHSCREEN | InputDevice.SOURCE_TRACKBALL
-                        | InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+                .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+                .setSources(InputDevice.SOURCE_TOUCHSCREEN | InputDevice.SOURCE_TRACKBALL)
                 .build();
         final InputDevice[] devices = {device};
         doReturn(true).when(newDisplay.mWmService.mInputManager)
@@ -596,6 +596,7 @@
         assertEquals(originalTouchscreen, newConfiguration.touchscreen);
         assertEquals(originalNavigation, newConfiguration.navigation);
         assertEquals(originalKeyboard, newConfiguration.keyboard);
+        // TODO(b/399749909): assert keyboardHidden, hardkeyboardHidden, and navigationHidden too.
     }
 
     @Test
@@ -4737,6 +4738,213 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testIsLetterboxedForSafeRegionOnlyAllowed_noManifestProperty_returnsTrue() {
+        setUpLandscapeLargeScreenDisplayWithApp();
+
+        assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+        setupSafeRegionBoundsParameters(/* dw */ 300, /* dh */ 200);
+
+        // For an activity letterboxed only due to safe region, areBoundsLetterboxed will return
+        // false
+        assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+        // Since no manifest property is defined, the activity is opted in by default
+        assertTrue(mActivity.mAppCompatController.getSafeRegionPolicy()
+                .isLetterboxedForSafeRegionOnlyAllowed());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testIsLetterboxedForSafeRegionOnlyAllowed_allowedForActivity_returnsTrue() {
+        setUpLandscapeLargeScreenDisplayWithApp();
+
+        assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+        setupSafeRegionBoundsParameters(/* dw */ 300, /* dh */ 200);
+
+        // Activity can opt-out the safe region letterboxing by component level property.
+        final ComponentName name = getUniqueComponentName(mContext.getPackageName());
+        final PackageManager pm = mContext.getPackageManager();
+        spyOn(pm);
+        updateActivityLevelAllowSafeRegionLetterboxingProperty(name, pm, true /* value */);
+        updateApplicationLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+        final ActivityRecord optOutActivity = new ActivityBuilder(mAtm)
+                .setComponent(name).setTask(mTask).build();
+        optOutActivity.mAppCompatController.getSafeRegionPolicy().setNeedsSafeRegionBounds(true);
+
+        // Since activity manifest property is defined as true, the activity can be letterboxed
+        // for safe region
+        assertTrue(optOutActivity.mAppCompatController
+                .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testIsLetterboxedForSafeRegionOnlyAllowed_notAllowedForActivity_returnsFalse() {
+        setUpLandscapeLargeScreenDisplayWithApp();
+
+        assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+        setupSafeRegionBoundsParameters(/* dw */ 300, /* dh */ 200);
+
+        final ComponentName name = getUniqueComponentName(mContext.getPackageName());
+        final PackageManager pm = mContext.getPackageManager();
+        spyOn(pm);
+        updateActivityLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+        updateApplicationLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+        final ActivityRecord optOutActivity = new ActivityBuilder(mAtm)
+                .setComponent(name).setTask(mTask).build();
+        optOutActivity.mAppCompatController.getSafeRegionPolicy().setNeedsSafeRegionBounds(true);
+
+        // Since activity manifest property is defined as false, the activity can not be letterboxed
+        // for safe region
+        assertFalse(optOutActivity.mAppCompatController
+                .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testIsLetterboxedForSafeRegionOnlyAllowed_notAllowedForApplication_returnsFalse() {
+        setUpLandscapeLargeScreenDisplayWithApp();
+
+        assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+        setupSafeRegionBoundsParameters(/* dw */ 300, /* dh */ 200);
+
+        final ComponentName name = getUniqueComponentName(mContext.getPackageName());
+        final PackageManager pm = mContext.getPackageManager();
+        spyOn(pm);
+        updateActivityLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+        updateApplicationLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+        final ActivityRecord optOutAppActivity = new ActivityBuilder(mAtm)
+                .setComponent(name).setTask(mTask).build();
+        optOutAppActivity.mAppCompatController.getSafeRegionPolicy().setNeedsSafeRegionBounds(true);
+
+        // Since application manifest property is defined as false, the activity can not be
+        // letterboxed for safe region
+        assertFalse(optOutAppActivity.mAppCompatController
+                .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testIsLetterboxedForSafeRegionOnlyAllowed_allowedForApplication_returnsTrue() {
+        setUpLandscapeLargeScreenDisplayWithApp();
+
+        assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+        setupSafeRegionBoundsParameters(/* dw */ 300, /* dh */ 200);
+
+        final ComponentName name = getUniqueComponentName(mContext.getPackageName());
+        final PackageManager pm = mContext.getPackageManager();
+        spyOn(pm);
+        updateActivityLevelAllowSafeRegionLetterboxingProperty(name, pm, false /* value */);
+        updateApplicationLevelAllowSafeRegionLetterboxingProperty(name, pm, true /* value */);
+        final ActivityRecord optOutAppActivity = new ActivityBuilder(mAtm)
+                .setComponent(name).setTask(mTask).build();
+        optOutAppActivity.mAppCompatController.getSafeRegionPolicy().setNeedsSafeRegionBounds(true);
+
+        // Since application manifest property is defined as true, the activity can be letterboxed
+        // for safe region
+        assertTrue(optOutAppActivity.mAppCompatController
+                .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
+    }
+
+    private void updateApplicationLevelAllowSafeRegionLetterboxingProperty(ComponentName name,
+            PackageManager pm, boolean propertyValueForApplication) {
+        final PackageManager.Property propertyForApplication = new PackageManager.Property(
+                "propertyName", /* value */ propertyValueForApplication, name.getPackageName(),
+                name.getClassName());
+        try {
+            doReturn(propertyForApplication).when(pm).getPropertyAsUser(
+                    WindowManager.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING,
+                    name.getPackageName(), /* className */ null, /* userId */ 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void updateActivityLevelAllowSafeRegionLetterboxingProperty(ComponentName name,
+            PackageManager pm, boolean propertyValueForActivity) {
+        final PackageManager.Property propertyForActivity = new PackageManager.Property(
+                "propertyName", /* value */ propertyValueForActivity, name.getPackageName(),
+                name.getClassName());
+        try {
+            doReturn(propertyForActivity).when(pm).getPropertyAsUser(
+                    WindowManager.PROPERTY_COMPAT_ALLOW_SAFE_REGION_LETTERBOXING,
+                    name.getPackageName(), name.getClassName(), /* userId */ 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testAreBoundsLetterboxed_letterboxedForSafeRegionAndFixedOrientation_returnTrue() {
+        setUpLandscapeLargeScreenDisplayWithApp();
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+        final Rect safeRegionBounds = setupSafeRegionBoundsParameters(/* dw */ 500, /* dh */ 200);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        // Activity is letterboxed due to fixed orientation within the safe region
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
+                .isLetterboxedForFixedOrientationAndAspectRatio());
+        assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION);
+        assertFalse(mActivity.mAppCompatController.getSafeRegionPolicy()
+                .isLetterboxedForSafeRegionOnlyAllowed());
+        assertTrue(safeRegionBounds.contains(mActivity.getBounds()));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testAreBoundsLetterboxed_letterboxedForSafeRegionAndAspectRatio_returnTrue() {
+        setUpPortraitLargeScreenDisplayWithApp();
+
+        assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
+
+        final Rect safeRegionBounds = setupSafeRegionBoundsParameters(/* dw */ 200, /* dh */ 300);
+
+        prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_PORTRAIT);
+
+        // Activity is letterboxed due to min aspect ratio within the safe region
+        assertFalse(mActivity.mAppCompatController.getAspectRatioPolicy()
+                .isLetterboxedForFixedOrientationAndAspectRatio());
+        assertTrue(mActivity.mAppCompatController.getAspectRatioPolicy()
+                .isLetterboxedForAspectRatioOnly());
+        assertFalse(mActivity.inSizeCompatMode());
+        assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
+        assertTrue(safeRegionBounds.contains(mActivity.getBounds()));
+    }
+
+    private Rect setupSafeRegionBoundsParameters(int dw, int dh) {
+        final AppCompatController appCompatController = mActivity.mAppCompatController;
+        final AppCompatSafeRegionPolicy safeRegionPolicy =
+                appCompatController.getSafeRegionPolicy();
+        safeRegionPolicy.setNeedsSafeRegionBounds(true);
+        spyOn(mTask);
+        final Rect safeRegionBounds = new Rect(100, 200, 100 + dw, 200 + dh);
+        doReturn(safeRegionBounds).when(mTask).getSafeRegionBounds();
+        return safeRegionBounds;
+    }
+
+    @Test
     public void testAreBoundsLetterboxed_letterboxedForSizeCompat_returnsTrue() {
         setUpDisplaySizeWithApp(1000, 2500);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 3776b036..b558fad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -78,6 +78,7 @@
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.protolog.PerfettoProtoLogImpl;
 import com.android.internal.protolog.ProtoLog;
 import com.android.internal.protolog.WmProtoLogGroups;
 import com.android.server.AnimationThread;
@@ -187,7 +188,10 @@
     }
 
     private void setUp() {
-        ProtoLog.init(WmProtoLogGroups.values());
+        if (ProtoLog.getSingleInstance() == null) {
+            ProtoLog.init(WmProtoLogGroups.values());
+            PerfettoProtoLogImpl.waitForInitialization();
+        }
 
         if (mOnBeforeServicesCreated != null) {
             mOnBeforeServicesCreated.run();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 986532c..ec83c50 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -87,7 +87,8 @@
                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         adjacentRootTask.mCreatedByOrganizer = true;
         final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
-        adjacentRootTask.setAdjacentTaskFragment(rootTask);
+        adjacentRootTask.setAdjacentTaskFragments(
+                new TaskFragment.AdjacentSet(adjacentRootTask, rootTask));
 
         taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
         Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -113,7 +114,8 @@
         final Task adjacentRootTask = createTask(
                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         adjacentRootTask.mCreatedByOrganizer = true;
-        adjacentRootTask.setAdjacentTaskFragment(rootTask);
+        adjacentRootTask.setAdjacentTaskFragments(
+                new TaskFragment.AdjacentSet(adjacentRootTask, rootTask));
 
         taskDisplayArea.setLaunchRootTask(rootTask,
                 new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
@@ -135,7 +137,8 @@
         adjacentRootTask.mCreatedByOrganizer = true;
         createActivityRecord(adjacentRootTask);
         final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
-        adjacentRootTask.setAdjacentTaskFragment(rootTask);
+        adjacentRootTask.setAdjacentTaskFragments(
+                new TaskFragment.AdjacentSet(adjacentRootTask, rootTask));
 
         taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
         final Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -821,7 +824,8 @@
         adjacentRootTask.mCreatedByOrganizer = true;
         final Task candidateTask = createTaskInRootTask(rootTask, 0 /* userId*/);
         final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
-        adjacentRootTask.setAdjacentTaskFragment(rootTask);
+        adjacentRootTask.setAdjacentTaskFragments(
+                new TaskFragment.AdjacentSet(adjacentRootTask, rootTask));
 
         // Verify the launch root with candidate task
         Task actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index ab76ae8..76660bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -784,7 +784,8 @@
                 .setFragmentToken(fragmentToken2)
                 .build();
         mWindowOrganizerController.mLaunchTaskFragments.put(fragmentToken2, taskFragment2);
-        mTaskFragment.setAdjacentTaskFragment(taskFragment2);
+        mTaskFragment.setAdjacentTaskFragments(
+                new TaskFragment.AdjacentSet(mTaskFragment, taskFragment2));
 
         mTransaction.clearAdjacentTaskFragments(mFragmentToken);
         mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE,
@@ -1267,7 +1268,7 @@
     }
 
     @Test
-    public void testTaskFragmentInPip_setAdjacentTaskFragment() {
+    public void testTaskFragmentInPip_setAdjacentTaskFragments() {
         setupTaskFragmentInPip();
         spyOn(mWindowOrganizerController);
 
@@ -1279,7 +1280,7 @@
         verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
                 eq(mErrorToken), eq(mTaskFragment), eq(OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS),
                 any(IllegalArgumentException.class));
-        verify(mTaskFragment, never()).setAdjacentTaskFragment(any());
+        verify(mTaskFragment, never()).setAdjacentTaskFragments(any());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index cc2a76d..7c1d7fec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -67,7 +67,6 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Binder;
-import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
 import android.view.View;
@@ -363,7 +362,7 @@
         doReturn(true).when(primaryActivity).supportsPictureInPicture();
         doReturn(false).when(secondaryActivity).supportsPictureInPicture();
 
-        primaryTf.setAdjacentTaskFragment(secondaryTf);
+        primaryTf.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(primaryTf, secondaryTf));
         primaryActivity.setState(RESUMED, "test");
         secondaryActivity.setState(RESUMED, "test");
 
@@ -390,7 +389,8 @@
         task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         taskFragment0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         taskFragment0.setBounds(taskFragmentBounds);
-        taskFragment0.setAdjacentTaskFragment(taskFragment1);
+        taskFragment0.setAdjacentTaskFragments(
+                new TaskFragment.AdjacentSet(taskFragment0, taskFragment1));
         taskFragment0.setCompanionTaskFragment(taskFragment1);
         taskFragment0.setAnimationParams(new TaskFragmentAnimationParams.Builder()
                 .setAnimationBackgroundColor(Color.GREEN)
@@ -779,7 +779,7 @@
                 .setOrganizer(mOrganizer)
                 .setFragmentToken(new Binder())
                 .build();
-        tf0.setAdjacentTaskFragment(tf1);
+        tf0.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(tf0, tf1));
         tf0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         tf1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         task.setBounds(0, 0, 1200, 1000);
@@ -834,7 +834,7 @@
         final Task task = createTask(mDisplayContent);
         final TaskFragment tf0 = createTaskFragmentWithActivity(task);
         final TaskFragment tf1 = createTaskFragmentWithActivity(task);
-        tf0.setAdjacentTaskFragment(tf1);
+        tf0.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(tf0, tf1));
         tf0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         tf1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         task.setBounds(0, 0, 1200, 1000);
@@ -982,7 +982,8 @@
                 .setOrganizer(mOrganizer)
                 .setFragmentToken(new Binder())
                 .build();
-        taskFragmentLeft.setAdjacentTaskFragment(taskFragmentRight);
+        taskFragmentLeft.setAdjacentTaskFragments(
+                new TaskFragment.AdjacentSet(taskFragmentLeft, taskFragmentRight));
         taskFragmentLeft.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         taskFragmentRight.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         task.setBounds(0, 0, 1200, 1000);
@@ -1051,8 +1052,8 @@
                 .setParentTask(task)
                 .createActivityCount(1)
                 .build();
-        taskFragmentRight.setAdjacentTaskFragment(taskFragmentLeft);
-        taskFragmentLeft.setAdjacentTaskFragment(taskFragmentRight);
+        taskFragmentRight.setAdjacentTaskFragments(
+                new TaskFragment.AdjacentSet(taskFragmentLeft, taskFragmentRight));
         final ActivityRecord appLeftTop = taskFragmentLeft.getTopMostActivity();
         final ActivityRecord appRightTop = taskFragmentRight.getTopMostActivity();
 
@@ -1103,7 +1104,6 @@
                 Math.min(outConfig.screenWidthDp, outConfig.screenHeightDp));
     }
 
-    @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
     @Test
     public void testAdjacentSetForTaskFragments() {
         final Task task = createTask(mDisplayContent);
@@ -1119,7 +1119,6 @@
                 () -> new TaskFragment.AdjacentSet(tf0, tf1, tf2));
     }
 
-    @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
     @Test
     public void testSetAdjacentTaskFragments() {
         final Task task0 = createTask(mDisplayContent);
@@ -1148,7 +1147,6 @@
         assertFalse(task2.hasAdjacentTaskFragment());
     }
 
-    @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
     @Test
     public void testClearAdjacentTaskFragments() {
         final Task task0 = createTask(mDisplayContent);
@@ -1167,7 +1165,6 @@
         assertFalse(task2.hasAdjacentTaskFragment());
     }
 
-    @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
     @Test
     public void testRemoveFromAdjacentTaskFragments() {
         final Task task0 = createTask(mDisplayContent);
@@ -1190,7 +1187,6 @@
         assertFalse(task1.isAdjacentTo(task1));
     }
 
-    @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
     @Test
     public void testRemoveFromAdjacentTaskFragmentsWhenRemove() {
         final Task task0 = createTask(mDisplayContent);
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 51ea498..f22ecb5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
@@ -68,11 +68,8 @@
     public void testPersistAndLoadSnapshot() {
         mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
         mSnapshotPersistQueue.waitForQueueEmpty();
-        final File[] files = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
-        final File[] nonExistsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
+        final File[] files = convertFilePath("1.proto", "1.jpg");
+        final File[] nonExistsFiles = convertFilePath("1_reduced.proto");
         assertTrueForFiles(files, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
         final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
@@ -92,14 +89,9 @@
         taskIds.add(1);
         mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId});
         mSnapshotPersistQueue.waitForQueueEmpty();
-        final File[] existsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
-        final File[] nonExistsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
-                new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
-                new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
+        final File[] existsFiles = convertFilePath("1.proto", "1.jpg");
+        final File[] nonExistsFiles = convertFilePath("1_reduced.proto", "2.proto", "2.jpg",
+                "2_reduced.jpg");
         assertTrueForFiles(existsFiles, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
     }
@@ -112,14 +104,8 @@
         mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId});
         mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
         mSnapshotPersistQueue.waitForQueueEmpty();
-        final File[] existsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
-                new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/2.jpg")};
-        final File[] nonExistsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
-                new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
+        final File[] existsFiles = convertFilePath("1.proto", "1.jpg", "2.proto", "2.jpg");
+        final File[] nonExistsFiles = convertFilePath("1_reduced.jpg", "2_reduced.jpg");
         assertTrueForFiles(existsFiles, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 4b54e44..af06c145 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -75,9 +75,7 @@
     public void testPersistAndLoadSnapshot() {
         mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
         mSnapshotPersistQueue.waitForQueueEmpty();
-        final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
-                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
+        final File[] files = convertFilePath("1.proto", "1.jpg", "1_reduced.jpg");
         assertTrueForFiles(files, File::exists, " must exist");
         final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
         assertNotNull(snapshot);
@@ -140,13 +138,8 @@
         mSnapshotPersistQueue.waitForQueueEmpty();
 
         // Make sure 1,2 were purged but removeObsoleteFiles wasn't.
-        final File[] existsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/3.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/4.proto")};
-        final File[] nonExistsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/100.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/1.proto")};
+        final File[] existsFiles = convertFilePath("3.proto", "4.proto");
+        final File[] nonExistsFiles = convertFilePath("100.proto", "1.proto", "2.proto");
         assertTrueForFiles(existsFiles, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
     }
@@ -427,14 +420,8 @@
         taskIds.add(1);
         mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId});
         mSnapshotPersistQueue.waitForQueueEmpty();
-        final File[] existsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
-                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
-        final File[] nonExistsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
-                new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
+        final File[] existsFiles = convertFilePath("1.proto", "1.jpg", "1_reduced.jpg");
+        final File[] nonExistsFiles = convertFilePath("2.proto", "2.jpg", "2_reduced.jpg");
         assertTrueForFiles(existsFiles, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
     }
@@ -447,13 +434,8 @@
         mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId});
         mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
         mSnapshotPersistQueue.waitForQueueEmpty();
-        final File[] existsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
-                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
-                new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
-                new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
+        final File[] existsFiles = convertFilePath("1.proto", "1.jpg", "1_reduced.jpg", "2.proto",
+                "2.jpg", "2_reduced.jpg");
         assertTrueForFiles(existsFiles, File::exists, " must exist");
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 1e16c97..547fc04 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.ContextWrapper;
 import android.content.res.Resources;
@@ -46,6 +47,7 @@
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
+import com.android.window.flags.Flags;
 
 import org.junit.After;
 import org.junit.AfterClass;
@@ -129,12 +131,33 @@
             return;
         }
         for (File file : files) {
-            if (!file.isDirectory()) {
-                file.delete();
+            if (file.isDirectory()) {
+                final File[] subFiles = file.listFiles();
+                if (subFiles == null) {
+                    continue;
+                }
+                for (File subFile : subFiles) {
+                    subFile.delete();
+                }
             }
+            file.delete();
         }
     }
 
+    File[] convertFilePath(@NonNull String... fileNames) {
+        final File[] files = new File[fileNames.length];
+        final String path;
+        if (Flags.scrambleSnapshotFileName()) {
+            path = mPersister.mPersistInfoProvider.getDirectory(mTestUserId).getPath();
+        } else {
+            path = FILES_DIR.getPath() + "/snapshots/";
+        }
+        for (int i = 0; i < fileNames.length; i++) {
+            files[i] = new File(path, fileNames[i]);
+        }
+        return files;
+    }
+
     TaskSnapshot createSnapshot() {
         return new TaskSnapshotBuilder().setTopActivityComponent(getUniqueComponentName()).build();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 724d7e7..044aacc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -1750,8 +1750,7 @@
 
         primary.mVisibleRequested = true;
         secondary.mVisibleRequested = true;
-        primary.setAdjacentTaskFragment(secondary);
-        secondary.setAdjacentTaskFragment(primary);
+        primary.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(primary, secondary));
         primary.setEmbeddedDimArea(EMBEDDED_DIM_AREA_PARENT_TASK);
         doReturn(true).when(primary).shouldBoostDimmer();
         task.assignChildLayers(t);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 4f310de..e0e65e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -1431,6 +1431,93 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testSetSafeRegionBounds_appliedOnNodeAndChildren() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+        final TestWindowContainer root = builder.setLayer(0).build();
+
+        final TestWindowContainer child1 = root.addChildWindow();
+        final TestWindowContainer child2 = root.addChildWindow();
+        final TestWindowContainer child11 = child1.addChildWindow();
+        final TestWindowContainer child12 = child1.addChildWindow();
+        final TestWindowContainer child21 = child2.addChildWindow();
+
+        assertNull(root.getSafeRegionBounds());
+        assertNull(child1.getSafeRegionBounds());
+        assertNull(child11.getSafeRegionBounds());
+        assertNull(child12.getSafeRegionBounds());
+        assertNull(child2.getSafeRegionBounds());
+        assertNull(child21.getSafeRegionBounds());
+
+        final Rect tempSafeRegionBounds1 = new Rect(50, 50, 200, 300);
+        child1.setSafeRegionBounds(tempSafeRegionBounds1);
+
+        assertNull(root.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child1.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child11.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child12.getSafeRegionBounds());
+        assertNull(child2.getSafeRegionBounds());
+        assertNull(child21.getSafeRegionBounds());
+
+        // Set different safe region bounds on child11
+        final Rect tempSafeRegionBounds2 = new Rect(30, 30, 200, 200);
+        child11.setSafeRegionBounds(tempSafeRegionBounds2);
+
+        assertNull(root.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child1.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds2, child11.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child12.getSafeRegionBounds());
+        assertNull(child2.getSafeRegionBounds());
+        assertNull(child21.getSafeRegionBounds());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testSetSafeRegionBounds_resetSafeRegionBounds() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+        final TestWindowContainer root = builder.setLayer(0).build();
+
+        final TestWindowContainer child1 = root.addChildWindow();
+        final TestWindowContainer child2 = root.addChildWindow();
+        final TestWindowContainer child11 = child1.addChildWindow();
+        final TestWindowContainer child12 = child1.addChildWindow();
+        final TestWindowContainer child21 = child2.addChildWindow();
+
+        assertNull(root.getSafeRegionBounds());
+        assertNull(child1.getSafeRegionBounds());
+        assertNull(child11.getSafeRegionBounds());
+        assertNull(child12.getSafeRegionBounds());
+        assertNull(child2.getSafeRegionBounds());
+        assertNull(child21.getSafeRegionBounds());
+
+        final Rect tempSafeRegionBounds1 = new Rect(50, 50, 200, 300);
+        child1.setSafeRegionBounds(tempSafeRegionBounds1);
+
+        assertNull(root.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child1.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child11.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child12.getSafeRegionBounds());
+        assertNull(child2.getSafeRegionBounds());
+        assertNull(child21.getSafeRegionBounds());
+
+        // Set different safe region bounds on child11
+        final Rect tempSafeRegionBounds2 = new Rect(30, 30, 200, 200);
+        child11.setSafeRegionBounds(tempSafeRegionBounds2);
+
+        assertEquals(tempSafeRegionBounds2, child11.getSafeRegionBounds());
+
+        // Reset safe region bounds on child11. Now child11 will use child1 safe region bounds.
+        child11.setSafeRegionBounds(/* safeRegionBounds */null);
+
+        assertNull(root.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child1.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child11.getSafeRegionBounds());
+        assertEquals(tempSafeRegionBounds1, child12.getSafeRegionBounds());
+        assertNull(child2.getSafeRegionBounds());
+        assertNull(child21.getSafeRegionBounds());
+    }
+
+    @Test
     public void testSetExcludeInsetsTypes_appliedOnNodeAndChildren() {
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java
index dcb6862..c0be214 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java
@@ -36,8 +36,10 @@
 import static org.mockito.Mockito.times;
 
 import android.content.Intent;
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
@@ -46,6 +48,8 @@
 import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 
+import com.android.window.flags.Flags;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -62,6 +66,8 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class WindowContainerTransactionTests extends WindowTestsBase {
+    private final Rect mSafeRegionBounds = new Rect(50, 50, 200, 300);
+
     @Test
     public void testRemoveTask() {
         final Task rootTask = createTask(mDisplayContent);
@@ -209,6 +215,152 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testSetSafeRegionBoundsOnTaskDisplayArea() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        final WindowContainerToken token = taskDisplayArea.mRemoteToken.toWindowContainerToken();
+        // Set safe region bounds on the task display area
+        wct.setSafeRegionBounds(token, mSafeRegionBounds);
+        applyTransaction(wct);
+
+        assertEquals(activity.getSafeRegionBounds(), mSafeRegionBounds);
+        assertEquals(task.getSafeRegionBounds(), mSafeRegionBounds);
+        assertEquals(rootTask.getSafeRegionBounds(), mSafeRegionBounds);
+        assertEquals(taskDisplayArea.getSafeRegionBounds(), mSafeRegionBounds);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testSetSafeRegionBoundsOnRootTask() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        final WindowContainerToken token = rootTask.mRemoteToken.toWindowContainerToken();
+        // Set safe region bounds on the root task
+        wct.setSafeRegionBounds(token, mSafeRegionBounds);
+        applyTransaction(wct);
+
+        assertEquals(activity.getSafeRegionBounds(), mSafeRegionBounds);
+        assertEquals(task.getSafeRegionBounds(), mSafeRegionBounds);
+        assertEquals(rootTask.getSafeRegionBounds(), mSafeRegionBounds);
+        assertNull(taskDisplayArea.getSafeRegionBounds());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testSetSafeRegionBoundsOnTask() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        final WindowContainerToken token = task.mRemoteToken.toWindowContainerToken();
+        // Set safe region bounds on the task
+        wct.setSafeRegionBounds(token, mSafeRegionBounds);
+        applyTransaction(wct);
+
+        assertEquals(activity.getSafeRegionBounds(), mSafeRegionBounds);
+        assertEquals(task.getSafeRegionBounds(), mSafeRegionBounds);
+        assertNull(rootTask.getSafeRegionBounds());
+        assertNull(taskDisplayArea.getSafeRegionBounds());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testSetSafeRegionBoundsOnTask_resetSafeRegionBounds() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        final WindowContainerToken token = task.mRemoteToken.toWindowContainerToken();
+        // Set safe region bounds on the task
+        wct.setSafeRegionBounds(token, mSafeRegionBounds);
+        applyTransaction(wct);
+
+        assertEquals(activity.getSafeRegionBounds(), mSafeRegionBounds);
+        assertEquals(task.getSafeRegionBounds(), mSafeRegionBounds);
+        assertNull(rootTask.getSafeRegionBounds());
+        assertNull(taskDisplayArea.getSafeRegionBounds());
+
+        // Reset safe region bounds on the task
+        wct.setSafeRegionBounds(token, /* safeRegionBounds */null);
+        applyTransaction(wct);
+
+        assertNull(activity.getSafeRegionBounds());
+        assertNull(task.getSafeRegionBounds());
+        assertNull(rootTask.getSafeRegionBounds());
+        assertNull(taskDisplayArea.getSafeRegionBounds());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testSetSafeRegionBoundsOnRootTaskAndTask() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        final WindowContainerToken token = rootTask.mRemoteToken.toWindowContainerToken();
+        // Set safe region bounds on the root task
+        wct.setSafeRegionBounds(token, mSafeRegionBounds);
+        // Set different safe region bounds on task
+        final Rect tempSafeRegionBounds = new Rect(30, 30, 200, 200);
+        wct.setSafeRegionBounds(task.mRemoteToken.toWindowContainerToken(), tempSafeRegionBounds);
+        applyTransaction(wct);
+
+        assertEquals(activity.getSafeRegionBounds(), tempSafeRegionBounds);
+        assertEquals(task.getSafeRegionBounds(), tempSafeRegionBounds);
+        assertEquals(rootTask.getSafeRegionBounds(), mSafeRegionBounds);
+        assertNull(taskDisplayArea.getSafeRegionBounds());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testSetSafeRegionBoundsOnRootTaskAndTask_resetSafeRegionBoundsOnTask() {
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        final WindowContainerToken token = rootTask.mRemoteToken.toWindowContainerToken();
+        // Set safe region bounds on the root task
+        wct.setSafeRegionBounds(token, mSafeRegionBounds);
+        // Set different safe region bounds on task
+        final Rect mTmpSafeRegionBounds = new Rect(30, 30, 200, 200);
+        wct.setSafeRegionBounds(task.mRemoteToken.toWindowContainerToken(), mTmpSafeRegionBounds);
+        applyTransaction(wct);
+
+        // Task and activity will use different safe region bounds
+        assertEquals(activity.getSafeRegionBounds(), mTmpSafeRegionBounds);
+        assertEquals(task.getSafeRegionBounds(), mTmpSafeRegionBounds);
+        assertEquals(rootTask.getSafeRegionBounds(), mSafeRegionBounds);
+        assertNull(taskDisplayArea.getSafeRegionBounds());
+
+        // Reset safe region bounds on task
+        wct.setSafeRegionBounds(task.mRemoteToken.toWindowContainerToken(),
+                /* safeRegionBounds */null);
+        applyTransaction(wct);
+
+        assertEquals(activity.getSafeRegionBounds(), mSafeRegionBounds);
+        assertEquals(task.getSafeRegionBounds(), mSafeRegionBounds);
+        assertEquals(rootTask.getSafeRegionBounds(), mSafeRegionBounds);
+        assertNull(taskDisplayArea.getSafeRegionBounds());
+    }
+
+    @Test
     public void testDesktopMode_moveTaskToFront() {
         final TestDesktopOrganizer desktopOrganizer = new TestDesktopOrganizer(mAtm);
         TaskDisplayArea tda = desktopOrganizer.mDefaultTDA;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index 8606581..669f5d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -24,7 +24,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 
 import android.platform.test.annotations.Presubmit;
 
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 71e84c0..73102c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -1147,7 +1147,8 @@
                 argThat(h -> (h.inputConfig & InputConfig.SPY) == 0));
 
         assertThrows(IllegalArgumentException.class, () ->
-                mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl,
+                mWm.updateInputChannel(inputChannel.getToken(), null /* hostInputToken */,
+                        DEFAULT_DISPLAY, surfaceControl,
                         FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY,
                         null /* region */));
     }
@@ -1217,7 +1218,8 @@
                 eq(surfaceControl),
                 argThat(h -> (h.inputConfig & InputConfig.SPY) == 0));
 
-        mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl,
+        mWm.updateInputChannel(inputChannel.getToken(), null /* hostInputToken */,
+                DEFAULT_DISPLAY, surfaceControl,
                 FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY,
                 null /* region */);
         verify(mTransaction).setInputWindowInfo(
@@ -1244,7 +1246,8 @@
                 eq(surfaceControl),
                 argThat(h -> (h.inputConfig & InputConfig.SENSITIVE_FOR_PRIVACY) == 0));
 
-        mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl,
+        mWm.updateInputChannel(inputChannel.getToken(), null /* hostInputToken */,
+                DEFAULT_DISPLAY, surfaceControl,
                 FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY,
                 INPUT_FEATURE_SENSITIVE_FOR_PRIVACY,
                 null /* region */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 7030d986..6a35fa8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -61,6 +61,7 @@
 import static org.junit.Assert.assertEquals;
 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.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -910,7 +911,7 @@
         final RunningTaskInfo info2 = task2.getTaskInfo();
 
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setAdjacentRoots(info1.token, info2.token);
+        wct.setAdjacentRootSet(info1.token, info2.token);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertTrue(task1.isAdjacentTo(task2));
         assertTrue(task2.isAdjacentTo(task1));
@@ -929,7 +930,6 @@
         assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null);
     }
 
-    @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
     @Test
     public void testSetAdjacentLaunchRootSet() {
         final DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
@@ -1569,6 +1569,51 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testSetSafeRegionBoundsOnRootTask() {
+        Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+                mDisplayContent, WINDOWING_MODE_FULLSCREEN, null);
+        final Task task1 = createRootTask();
+        final Task task2 = createTask(rootTask, false /* fakeDraw */);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        Rect safeRegionBounds = new Rect(50, 50, 200, 300);
+
+        wct.setSafeRegionBounds(rootTask.mRemoteToken.toWindowContainerToken(), safeRegionBounds);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+
+        assertEquals(rootTask.getSafeRegionBounds(), safeRegionBounds);
+        assertEquals(task2.getSafeRegionBounds(), safeRegionBounds);
+        assertNull(task1.getSafeRegionBounds());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SAFE_REGION_LETTERBOXING)
+    public void testSetSafeRegionBoundsOnRootTask_resetSafeRegionBounds() {
+        Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+                mDisplayContent, WINDOWING_MODE_FULLSCREEN, null);
+        final Task task1 = createRootTask();
+        final Task task2 = createTask(rootTask, false /* fakeDraw */);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        Rect safeRegionBounds = new Rect(50, 50, 200, 300);
+
+        wct.setSafeRegionBounds(rootTask.mRemoteToken.toWindowContainerToken(), safeRegionBounds);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+
+        assertEquals(rootTask.getSafeRegionBounds(), safeRegionBounds);
+        assertEquals(task2.getSafeRegionBounds(), safeRegionBounds);
+        assertNull(task1.getSafeRegionBounds());
+
+        // Reset safe region bounds on the root task
+        wct.setSafeRegionBounds(rootTask.mRemoteToken.toWindowContainerToken(),
+                /* safeRegionBounds */null);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+
+        assertNull(rootTask.getSafeRegionBounds());
+        assertNull(task2.getSafeRegionBounds());
+        assertNull(task1.getSafeRegionBounds());
+    }
+
+    @Test
     public void testReparentToOrganizedTask() {
         final ITaskOrganizer organizer = registerMockOrganizer();
         Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 9dc7026..5347f9a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -382,12 +382,17 @@
         assertFalse(tracker.hasResumedActivity(mWpc.mUid));
         assertTrue(mWpc.hasForegroundActivities());
 
-        activity.setVisibility(false);
         activity.setVisibleRequested(false);
-        activity.setState(STOPPED, "test");
-
+        if (com.android.window.flags.Flags.useVisibleRequestedForProcessTracker()) {
+            assertTrue("PAUSING is visible", mWpc.hasVisibleActivities());
+            activity.setState(PAUSED, "test");
+        } else {
+            activity.setVisible(false);
+        }
         verify(tracker).onAllActivitiesInvisible(mWpc);
         assertFalse(mWpc.hasVisibleActivities());
+
+        activity.setState(STOPPED, "test");
         assertFalse(mWpc.hasForegroundActivities());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 59ee2f5..5624677 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -23,9 +23,6 @@
 import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.InsetsSource.ID_IME;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowInsets.Type.statusBars;
@@ -88,7 +85,6 @@
 import android.content.ContentResolver;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
-import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -673,56 +669,6 @@
     }
 
     @Test
-    public void testSeamlesslyRotateWindow() {
-        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
-        final SurfaceControl.Transaction t = spy(StubTransaction.class);
-
-        makeWindowVisible(app);
-        app.mSurfaceControl = mock(SurfaceControl.class);
-        final Rect frame = app.getFrame();
-        frame.set(10, 20, 60, 80);
-        app.updateSurfacePosition(t);
-        assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top));
-        app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true /* requested */);
-        assertTrue(app.mSeamlesslyRotated);
-
-        // Verify we un-rotate the window state surface.
-        final Matrix matrix = new Matrix();
-        // Un-rotate 90 deg.
-        matrix.setRotate(270);
-        // Translate it back to origin.
-        matrix.postTranslate(0, mDisplayInfo.logicalWidth);
-        verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class));
-
-        // Verify we update the position as well.
-        final float[] curSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y};
-        matrix.mapPoints(curSurfacePos);
-        verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
-
-        app.finishSeamlessRotation(t);
-        assertFalse(app.mSeamlesslyRotated);
-        assertNull(app.mPendingSeamlessRotate);
-
-        // Simulate the case with deferred layout and animation.
-        app.resetSurfacePositionForAnimationLeash(t);
-        clearInvocations(t);
-        mWm.mWindowPlacerLocked.deferLayout();
-        app.updateSurfacePosition(t);
-        // Because layout is deferred, the position should keep the reset value.
-        assertTrue(app.mLastSurfacePosition.equals(0, 0));
-
-        app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_270, true /* requested */);
-        // The last position must be updated so the surface can be unrotated properly.
-        assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top));
-        matrix.setRotate(90);
-        matrix.postTranslate(mDisplayInfo.logicalHeight, 0);
-        curSurfacePos[0] = frame.left;
-        curSurfacePos[1] = frame.top;
-        matrix.mapPoints(curSurfacePos);
-        verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
-    }
-
-    @Test
     public void testVisibilityChangeSwitchUser() {
         final WindowState window = newWindowBuilder("app", TYPE_APPLICATION).build();
         window.mHasSurface = true;
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 57ab13f..471b065 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -122,7 +122,6 @@
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
 import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
 
 import org.junit.After;
@@ -289,18 +288,6 @@
         mAtm.mWindowManager.mAppCompatConfiguration
                 .setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(false);
 
-        // Setup WallpaperController crop utils with a simple center-align strategy
-        WallpaperCropUtils cropUtils = (displaySize, bitmapSize, suggestedCrops, rtl) -> {
-            Rect crop = new Rect(0, 0, displaySize.x, displaySize.y);
-            crop.scale(Math.min(
-                    ((float) bitmapSize.x) / displaySize.x,
-                    ((float) bitmapSize.y) / displaySize.y));
-            crop.offset((bitmapSize.x - crop.width()) / 2, (bitmapSize.y - crop.height()) / 2);
-            return crop;
-        };
-        mDisplayContent.mWallpaperController.setWallpaperCropUtils(cropUtils);
-        mDefaultDisplay.mWallpaperController.setWallpaperCropUtils(cropUtils);
-
         checkDeviceSpecificOverridesNotApplied();
     }
 
@@ -1890,7 +1877,7 @@
             mSecondary = mService.mTaskOrganizerController.createRootTask(
                     display, WINDOWING_MODE_MULTI_WINDOW, null);
 
-            mPrimary.setAdjacentTaskFragment(mSecondary);
+            mPrimary.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(mPrimary, mSecondary));
             display.getDefaultTaskDisplayArea().setLaunchAdjacentFlagRootTask(mSecondary);
 
             final Rect primaryBounds = new Rect();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index a02c3db..8907a72 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -17,6 +17,8 @@
 package com.android.server.wm;
 
 import static android.view.InsetsSource.ID_IME;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -35,16 +37,19 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.res.Configuration;
+import android.graphics.Matrix;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
 import android.view.WindowInsets;
 import android.window.WindowContext;
 
@@ -335,6 +340,31 @@
     }
 
     @Test
+    public void testSeamlesslyRotate() {
+        final SurfaceControl.Transaction t = mTransaction;
+        final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+        token.mLastSurfacePosition.x = 10;
+        token.mLastSurfacePosition.y = 20;
+        final SeamlessRotator rotator = new SeamlessRotator(ROTATION_0, ROTATION_90,
+                mDisplayContent.getDisplayInfo(), false /* applyFixedTransformationHint */);
+        clearInvocations(t);
+        rotator.unrotate(t, token);
+
+        // Verify surface is un-rotated.
+        final Matrix matrix = new Matrix();
+        // Un-rotate 90 deg.
+        matrix.setRotate(270);
+        // Translate it back to origin.
+        matrix.postTranslate(0, mDisplayInfo.logicalWidth);
+        verify(t).setMatrix(eq(token.mSurfaceControl), eq(matrix), any(float[].class));
+
+        final float[] curSurfacePos = {token.mLastSurfacePosition.x, token.mLastSurfacePosition.y};
+        matrix.mapPoints(curSurfacePos);
+        verify(t).setPosition(eq(token.mSurfaceControl),
+                eq(curSurfacePos[0]), eq(curSurfacePos[1]));
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
     public void onDisplayChanged_differentDisplay_reparented() {
         final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
index a1d35a7..0749c0b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
@@ -22,7 +22,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -118,7 +118,7 @@
     @Test
     public void trace_discared_whenNotTracing() {
         mWindowTracing.logState("where");
-        verifyZeroInteractions(mWmMock);
+        verifyNoMoreInteractions(mWmMock);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
index 9367941..3da279b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
@@ -22,7 +22,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -128,7 +128,7 @@
     @Test
     public void trace_ignoresLogStateCalls_ifTracingIsDisabled() {
         sWindowTracing.logState("where");
-        verifyZeroInteractions(sWmMock);
+        verifyNoMoreInteractions(sWmMock);
     }
 
     @Test
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 89b9850..a727df7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -43,6 +43,8 @@
 import android.media.permission.Identity;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.os.ParcelFileDescriptor;
@@ -837,6 +839,13 @@
         private final int mBindingFlags;
         private final int mInstanceNumber;
 
+        private static final HandlerThread mHandler;
+
+        static {
+            mHandler = new HandlerThread("Sandbox detection connector");
+            mHandler.start();
+        }
+
         private boolean mRespectServiceConnectionStatusChanged = true;
         private boolean mIsBound = false;
         private boolean mIsLoggedFirstConnect = false;
@@ -883,6 +892,11 @@
             }
         }
 
+        @Override // from ServiceConnector.Impl
+        protected Handler getJobHandler() {
+            return mHandler.getThreadHandler();
+        }
+
         @Override
         protected long getAutoDisconnectTimeoutMs() {
             return -1;
@@ -1151,14 +1165,12 @@
     }
 
     private void updateServiceIdentity(ServiceConnection connection) {
-        connection.run(service -> service.ping(new IRemoteCallback.Stub() {
+        connection.run(service -> service.ping(new ISandboxedDetectionService.IPingMe.Stub() {
             @Override
-            public void sendResult(Bundle bundle) throws RemoteException {
+            public void onPing() throws RemoteException {
                 // TODO: Exit if the service has been unbound already (though there's a very low
                 // chance this happens).
-                if (DEBUG) {
-                    Slog.d(TAG, "updating hotword UID " + Binder.getCallingUid());
-                }
+                Slog.d(TAG, "updating hotword UID " + Binder.getCallingUid());
                 // TODO: Have the provider point to the current state stored in
                 // VoiceInteractionManagerServiceImpl.
                 final int uid = Binder.getCallingUid();
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 9e57fd3..c244168 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -23,6 +23,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Intent;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.BadParcelableException;
@@ -1174,6 +1175,10 @@
                 int callerNumberVerificationStatus,
                 Uri contactPhotoUri,
                 UserHandle originatingUser) {
+            if (extras == null) {
+                extras = new Bundle();
+            }
+            extras.putParcelable(Intent.EXTRA_USER_HANDLE, originatingUser);
             mState = state;
             mTelecomCallId = telecomCallId;
             mHandle = handle;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 29d3942..ebe0078 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1237,6 +1237,10 @@
             builder.append(isLong ? " PROPERTY_IS_DOWNGRADED_CONFERENCE" : " dngrd_conf");
         }
 
+        if ((properties & PROPERTY_CROSS_SIM) == PROPERTY_CROSS_SIM) {
+            builder.append(isLong ? " PROPERTY_CROSS_SIM" : " xsim");
+        }
+
         builder.append("]");
         return builder.toString();
     }
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index bd004e5..07278e7 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Intent;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -687,9 +686,6 @@
             source.readList(conferenceableCallIds, classLoader, java.lang.String.class);
             Bundle intentExtras = source.readBundle(classLoader);
             Bundle extras = source.readBundle(classLoader);
-            if (extras == null) {
-                extras = new Bundle();
-            }
             int supportedAudioRoutes = source.readInt();
             boolean isRttCallChanged = source.readByte() == 1;
             ParcelableRttCall rttCall = source.readParcelable(classLoader, android.telecom.ParcelableRttCall.class);
@@ -700,7 +696,6 @@
             String activeChildCallId = source.readString();
             Uri contactPhotoUri = source.readParcelable(classLoader, Uri.class);
             UserHandle associatedUser = source.readParcelable(classLoader, UserHandle.class);
-            extras.putParcelable(Intent.EXTRA_USER_HANDLE, associatedUser);
             return new ParcelableCallBuilder()
                     .setId(id)
                     .setState(state)
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 50c5a6b6..1491510 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3918,6 +3918,22 @@
             "5g_icon_display_secondary_grace_period_string";
 
     /**
+     * When an NR advanced connection is lost and a Physical Cell ID (PCI) change occurs within
+     * the primary timer{@link #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING}, delay updating the network
+     * icon.
+     *
+     * <p>This delay is implemented because a rapid PCI change often indicates the device is
+     * switching to a nearby cell tower to quickly restore the NR advanced connection. Displaying
+     * an intermediate network icon (like 4G/LTE) might be misleading if the 5G connection is
+     * restored shortly after. This value sets the delay in seconds; 0 disables the feature.</p>
+     *
+     * @hide
+     */
+    public static final String KEY_NR_ADVANCED_PCI_CHANGE_SECONDARY_TIMER_SECONDS_INT =
+            "nr_advanced_pci_change_secondary_timer_seconds_int";
+
+
+    /**
      * The secondary grace periods in seconds to use if NR advanced icon was shown due to connecting
      * to bands specified in {@link #KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY}.
      *
@@ -11222,6 +11238,7 @@
                         + "not_restricted_rrc_con:5G");
         sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
         sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
+        sDefaults.putInt(KEY_NR_ADVANCED_PCI_CHANGE_SECONDARY_TIMER_SECONDS_INT, 0);
         sDefaults.putInt(KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT, 0);
         sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false);
         sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, false);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index f528263..6e23edf 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -614,7 +614,7 @@
 
     /** @hide */
     public static int convertRssiAsuToDBm(int rssiAsu) {
-        if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
+        if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN || rssiAsu == Integer.MAX_VALUE) {
             return CellInfo.UNAVAILABLE;
         }
         if ((rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index e6515f13..850ce3e 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -39,7 +38,6 @@
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.Qos;
 
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.lang.annotation.Retention;
@@ -89,35 +87,30 @@
      * Unsupported. The unsupported state is used when the data network cannot support the network
      * validation function for the current data connection state.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public static final int NETWORK_VALIDATION_UNSUPPORTED = 0;
 
     /**
      * Not Requested. The not requested status is used when the data network supports the network
      * validation function, but no network validation is being performed yet.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1;
 
     /**
      * In progress. The in progress state is used when the network validation process for the data
      * network is in progress. This state is followed by either success or failure.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public static final int NETWORK_VALIDATION_IN_PROGRESS = 2;
 
     /**
      * Success. The Success status is used when network validation has been completed for the data
      * network and the result is successful.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public static final int NETWORK_VALIDATION_SUCCESS = 3;
 
     /**
      * Failure. The Failure status is used when network validation has been completed for the data
      * network and the result is failure.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public static final int NETWORK_VALIDATION_FAILURE = 4;
 
     /**
@@ -360,7 +353,6 @@
      *
      * @return the network validation status of the data call
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public @NetworkValidationStatus int getNetworkValidationStatus() {
         return mNetworkValidationStatus;
     }
@@ -615,7 +607,6 @@
          * @param networkValidationStatus the network validation status of the data call
          * @return The builder
          */
-        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
         public @NonNull Builder setNetworkValidationStatus(
                 @NetworkValidationStatus int networkValidationStatus) {
             mNetworkValidationStatus = networkValidationStatus;
diff --git a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
index 7356cdc..42d09cf 100644
--- a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
+++ b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
@@ -31,7 +31,6 @@
 import android.telephony.ims.ImsManager;
 import android.telephony.satellite.SatelliteManager;
 
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.Preconditions;
 
 
@@ -77,9 +76,6 @@
     // also check through Compatibility framework a few lines below).
     @SuppressWarnings("AndroidFrameworkCompatChange")
     private static boolean hasSystemFeature(Context context, String feature) {
-        // Check release status of this change in behavior.
-        if (!Flags.minimalTelephonyManagersConditionalOnFeatures()) return true;
-
         // Check SDK version of the vendor partition.
         final int vendorApiLevel = SystemProperties.getInt(
                 "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fbba999..7b2e6c2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -691,7 +691,7 @@
             case UNKNOWN:
                 modemCount = 1;
                 // check for voice and data support, 0 if not supported
-                if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
+                if (!isDeviceVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
                     modemCount = 0;
                 }
                 break;
@@ -2814,7 +2814,7 @@
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public int getPhoneType() {
-        if (!isVoiceCapable() && !isDataCapable()) {
+        if (!isDeviceVoiceCapable() && !isDataCapable()) {
             return PHONE_TYPE_NONE;
         }
         return getCurrentPhoneType();
@@ -3371,14 +3371,13 @@
                 return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(),
                         getAttributionTag());
             } else {
-                // This can happen when the ITelephony interface is not up yet.
+                Log.e(TAG, "getDataNetworkType: ITelephony interface is not up yet");
                 return NETWORK_TYPE_UNKNOWN;
             }
-        } catch(RemoteException ex) {
-            // This shouldn't happen in the normal case
-            return NETWORK_TYPE_UNKNOWN;
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing
+        } catch (RemoteException // Shouldn't happen in the normal case
+                | NullPointerException ex // Could happen before phone restarts due to crashing
+        ) {
+            Log.e(TAG, "getDataNetworkType: " + ex.getMessage());
             return NETWORK_TYPE_UNKNOWN;
         }
     }
@@ -15348,11 +15347,15 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING} or
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @RequiresFeature(anyOf = {
+        PackageManager.FEATURE_TELEPHONY_CALLING,
+        PackageManager.FEATURE_TELEPHONY_MESSAGING
+    })
     @SystemApi
     public void notifyOtaEmergencyNumberDbInstalled() {
         try {
@@ -15377,11 +15380,15 @@
      * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
      *
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING} or
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @RequiresFeature(anyOf = {
+        PackageManager.FEATURE_TELEPHONY_CALLING,
+        PackageManager.FEATURE_TELEPHONY_MESSAGING
+    })
     @SystemApi
     public void updateOtaEmergencyNumberDbFilePath(
             @NonNull ParcelFileDescriptor otaParcelFileDescriptor) {
@@ -15405,11 +15412,15 @@
      * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
      *
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING} or
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @RequiresFeature(anyOf = {
+        PackageManager.FEATURE_TELEPHONY_CALLING,
+        PackageManager.FEATURE_TELEPHONY_MESSAGING
+    })
     @SystemApi
     public void resetOtaEmergencyNumberDbFilePath() {
         try {
@@ -15491,11 +15502,15 @@
      * or throw a SecurityException if the caller does not have the permission.
      *
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING} or
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @NonNull
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @RequiresFeature(anyOf = {
+        PackageManager.FEATURE_TELEPHONY_CALLING,
+        PackageManager.FEATURE_TELEPHONY_MESSAGING
+    })
     public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList() {
         Map<Integer, List<EmergencyNumber>> emergencyNumberList = new HashMap<>();
         try {
@@ -15549,11 +15564,15 @@
      * or throw a SecurityException if the caller does not have the permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING} or
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @NonNull
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @RequiresFeature(anyOf = {
+        PackageManager.FEATURE_TELEPHONY_CALLING,
+        PackageManager.FEATURE_TELEPHONY_MESSAGING
+    })
     public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList(
             @EmergencyServiceCategories int categories) {
         Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>();
@@ -15619,9 +15638,13 @@
      * SIM card(s), Android database, modem, network or defaults; {@code false} otherwise.
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING} or
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @RequiresFeature(anyOf = {
+        PackageManager.FEATURE_TELEPHONY_CALLING,
+        PackageManager.FEATURE_TELEPHONY_MESSAGING
+    })
     public boolean isEmergencyNumber(@NonNull String number) {
         try {
             ITelephony telephony = getITelephony();
@@ -15658,7 +15681,8 @@
      * have the required permission/privileges
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING} or
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      *
      * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)} instead.
      * @hide
@@ -15666,7 +15690,10 @@
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @RequiresFeature(anyOf = {
+        PackageManager.FEATURE_TELEPHONY_CALLING,
+        PackageManager.FEATURE_TELEPHONY_MESSAGING
+    })
     public boolean isPotentialEmergencyNumber(@NonNull String number) {
         try {
             ITelephony telephony = getITelephony();
@@ -15686,15 +15713,19 @@
      * Returns the emergency number database version.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
      *
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING} or
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @RequiresFeature(anyOf = {
+        PackageManager.FEATURE_TELEPHONY_CALLING,
+        PackageManager.FEATURE_TELEPHONY_MESSAGING
+    })
     public int getEmergencyNumberDbVersion() {
         try {
             ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index b6f9e1f..0e70306 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -17,7 +17,6 @@
 
 package android.telephony.data;
 
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -32,7 +31,6 @@
 import android.telephony.data.ApnSetting.ProtocolType;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -455,7 +453,6 @@
      *
      * @return The network validation status of data connection.
      */
-    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
     public @PreciseDataConnectionState.NetworkValidationStatus int getNetworkValidationStatus() {
         return mNetworkValidationStatus;
     }
@@ -936,7 +933,6 @@
          * @param status The network validation status.
          * @return The same instance of the builder.
          */
-        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
         public @NonNull Builder setNetworkValidationStatus(
                 @PreciseDataConnectionState.NetworkValidationStatus int status) {
             mNetworkValidationStatus = status;
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index f04e1c9..5baf463 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -17,7 +17,6 @@
 package android.telephony.data;
 
 import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -40,7 +39,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.FunctionalUtils;
 import com.android.telephony.Rlog;
 
@@ -414,7 +412,6 @@
          * @param resultCodeCallback Listener for the {@link DataServiceCallback.ResultCode} that
          *     request validation to the DataService and checks if the request has been submitted.
          */
-        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
         public void requestNetworkValidation(int cid,
                 @NonNull @CallbackExecutor Executor executor,
                 @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index f775de6..c42b29c1 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -17,7 +17,6 @@
 package android.telephony.data;
 
 import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.Service;
@@ -41,7 +40,6 @@
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.flags.FeatureFlagsImpl;
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.FunctionalUtils;
 import com.android.telephony.Rlog;
 
@@ -290,7 +288,6 @@
          * @param resultCodeCallback A callback to determine whether the request was successfully
          *     submitted or not.
          */
-        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
         public void requestNetworkValidation(
                 @NetCapability int networkCapability,
                 @NonNull @CallbackExecutor Executor executor,
@@ -298,15 +295,6 @@
             Objects.requireNonNull(executor, "executor cannot be null");
             Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");
 
-            if (!sFeatureFlag.networkValidation()) {
-                loge("networkValidation feature is disabled");
-                executor.execute(
-                        () ->
-                                resultCodeCallback.accept(
-                                        DataServiceCallback.RESULT_ERROR_UNSUPPORTED));
-                return;
-            }
-
             IIntegerConsumer callback = new IIntegerConsumer.Stub() {
                 @Override
                 public void accept(int result) {
diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
index 685ae9a..c5e7188 100644
--- a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
@@ -47,6 +47,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
index 5f92d7f..22bd458 100644
--- a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
@@ -47,6 +47,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
index 1b90e99..541ce26 100644
--- a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
@@ -47,6 +47,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
index ffdbb02..d2e0219 100644
--- a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
@@ -47,6 +47,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
index ac704e5..e112c82 100644
--- a/tests/FlickerTests/IME/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -49,6 +49,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/Notification/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
index e2ac5a9..e5700c0 100644
--- a/tests/FlickerTests/Notification/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
@@ -47,6 +47,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
index 1a4feb6..4c41a4c 100644
--- a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
@@ -47,6 +47,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
index 1b2007d..27fc249 100644
--- a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
@@ -47,6 +47,8 @@
         <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
         <!-- Disable AOD -->
         <option name="run-command" value="settings put secure doze_always_on 0"/>
+        <!-- Disable explore hub mode -->
+        <option name="run-command" value="settings put secure glanceable_hub_enabled 0"/>
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
         <option name="run-command" value="settings put system show_touches 1"/>
         <option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
index 1b02792..b1d6e96 100644
--- a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
+++ b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
@@ -18,11 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.annotations.RootPermissionTest;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.host.HostFlagsValueProvider;
-import android.security.Flags;
 
 import com.android.blockdevicewriter.BlockDeviceWriter;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -31,7 +27,6 @@
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -48,16 +43,11 @@
  */
 @RootPermissionTest
 @RunWith(DeviceJUnit4ClassRunner.class)
-@RequiresFlagsEnabled(Flags.FLAG_FSVERITY_API)
 public class FsVerityHostTest extends BaseHostJUnit4Test {
     private static final String TARGET_PACKAGE = "com.android.fsverity";
 
     private static final String BASENAME = "test.file";
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            HostFlagsValueProvider.createCheckFlagsRule(this::getDevice);
-
     @Test
     public void testFsVeritySmallFile() throws Exception {
         prepareTest(10000);
diff --git a/tests/Input/assets/testPointerScale.png b/tests/Input/assets/testPointerScale.png
index 54d37c2..781df47 100644
--- a/tests/Input/assets/testPointerScale.png
+++ b/tests/Input/assets/testPointerScale.png
Binary files differ
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
index e99c814..794fd02 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
@@ -214,9 +214,5 @@
         ): Boolean {
             return handler(event, focusedToken)
         }
-
-        override fun isKeyGestureSupported(gestureType: Int): Boolean {
-            return true
-        }
     }
 }
diff --git a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
index 044f11d..890c346 100644
--- a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
@@ -184,6 +184,8 @@
     @get:Rule
     val rule = MockitoJUnit.rule()!!
     @get:Rule
+    val context = TestableContext(ApplicationProvider.getApplicationContext())
+    @get:Rule
     val inputManagerRule = MockInputManagerRule()
 
     @Mock
@@ -194,7 +196,6 @@
     private lateinit var bluetoothBatteryManager: BluetoothBatteryManager
 
     private lateinit var batteryController: BatteryController
-    private lateinit var context: TestableContext
     private lateinit var testLooper: TestLooper
     private lateinit var devicesChangedListener: IInputDevicesChangedListener
     private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
@@ -202,7 +203,6 @@
 
     @Before
     fun setup() {
-        context = TestableContext(ApplicationProvider.getApplicationContext())
         testLooper = TestLooper()
         val inputManager = InputManager(context)
         context.addMockSystemService(InputManager::class.java, inputManager)
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 2799f6c..4f1fb64 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -32,6 +32,7 @@
 import android.hardware.input.InputManager
 import android.hardware.input.InputManagerGlobal
 import android.hardware.input.KeyGestureEvent
+import android.os.Handler
 import android.os.IBinder
 import android.os.Process
 import android.os.SystemClock
@@ -48,9 +49,11 @@
 import androidx.test.core.app.ApplicationProvider
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.R
+import com.android.internal.accessibility.AccessibilityShortcutController
 import com.android.internal.annotations.Keep
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.server.input.InputManagerService.WindowManagerCallbacks
 import java.io.File
 import java.io.FileOutputStream
 import java.io.InputStream
@@ -67,6 +70,8 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
 
 /**
  * Tests for {@link KeyGestureController}.
@@ -102,6 +107,7 @@
         const val SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0
         const val SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL = 1
         const val SETTINGS_KEY_BEHAVIOR_NOTHING = 2
+        const val TEST_PID = 10
     }
 
     @JvmField
@@ -116,11 +122,10 @@
     @Rule
     val rule = SetFlagsRule()
 
-    @Mock
-    private lateinit var iInputManager: IInputManager
-
-    @Mock
-    private lateinit var packageManager: PackageManager
+    @Mock private lateinit var iInputManager: IInputManager
+    @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var wmCallbacks: WindowManagerCallbacks
+    @Mock private lateinit var accessibilityShortcutController: AccessibilityShortcutController
 
     private var currentPid = 0
     private lateinit var context: Context
@@ -207,8 +212,34 @@
 
     private fun setupKeyGestureController() {
         keyGestureController =
-            KeyGestureController(context, testLooper.looper, testLooper.looper, inputDataStore)
-        Mockito.`when`(iInputManager.getAppLaunchBookmarks())
+            KeyGestureController(
+                context,
+                testLooper.looper,
+                testLooper.looper,
+                inputDataStore,
+                object : KeyGestureController.Injector() {
+                    override fun getAccessibilityShortcutController(
+                        context: Context?,
+                        handler: Handler?
+                    ): AccessibilityShortcutController {
+                        return accessibilityShortcutController
+                    }
+                })
+        Mockito.`when`(iInputManager.registerKeyGestureHandler(Mockito.any()))
+            .thenAnswer {
+                val args = it.arguments
+                if (args[0] != null) {
+                    keyGestureController.registerKeyGestureHandler(
+                        args[0] as IKeyGestureHandler,
+                        TEST_PID
+                    )
+                }
+        }
+        keyGestureController.setWindowManagerCallbacks(wmCallbacks)
+        Mockito.`when`(wmCallbacks.isKeyguardLocked(Mockito.anyInt())).thenReturn(false)
+        Mockito.`when`(accessibilityShortcutController
+            .isAccessibilityShortcutAvailable(Mockito.anyBoolean())).thenReturn(true)
+        Mockito.`when`(iInputManager.appLaunchBookmarks)
             .thenReturn(keyGestureController.appLaunchBookmarks)
         keyGestureController.systemRunning()
         testLooper.dispatchAll()
@@ -1270,9 +1301,9 @@
                 )
             ),
             TestData(
-                "BACK + DPAD_DOWN -> TV Accessibility Chord",
+                "BACK + DPAD_DOWN -> Accessibility Chord(for TV)",
                 intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN),
-                KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
+                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
                 intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN),
                 0,
                 intArrayOf(
@@ -1622,6 +1653,52 @@
         )
     }
 
+    @Test
+    fun testAccessibilityShortcutChordPressed() {
+        setupKeyGestureController()
+
+        sendKeys(
+            intArrayOf(KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN),
+            // Assuming this value is always greater than the accessibility shortcut timeout, which
+            // currently defaults to 3000ms
+            timeDelayMs = 10000
+        )
+        Mockito.verify(accessibilityShortcutController, times(1)).performAccessibilityShortcut()
+    }
+
+    @Test
+    fun testAccessibilityTvShortcutChordPressed() {
+        setupKeyGestureController()
+
+        sendKeys(
+            intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN),
+            timeDelayMs = 10000
+        )
+        Mockito.verify(accessibilityShortcutController, times(1)).performAccessibilityShortcut()
+    }
+
+    @Test
+    fun testAccessibilityShortcutChordPressedForLessThanTimeout() {
+        setupKeyGestureController()
+
+        sendKeys(
+            intArrayOf(KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN),
+            timeDelayMs = 0
+        )
+        Mockito.verify(accessibilityShortcutController, never()).performAccessibilityShortcut()
+    }
+
+    @Test
+    fun testAccessibilityTvShortcutChordPressedForLessThanTimeout() {
+        setupKeyGestureController()
+
+        sendKeys(
+            intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN),
+            timeDelayMs = 0
+        )
+        Mockito.verify(accessibilityShortcutController, never()).performAccessibilityShortcut()
+    }
+
     private fun testKeyGestureInternal(test: TestData) {
         val handledEvents = mutableListOf<KeyGestureEvent>()
         val handler = KeyGestureHandler { event, _ ->
@@ -1683,7 +1760,11 @@
         assertEquals("Test: $testName should not produce Key gesture", 0, handledEvents.size)
     }
 
-    private fun sendKeys(testKeys: IntArray, assertNotSentToApps: Boolean = false) {
+    private fun sendKeys(
+        testKeys: IntArray,
+        assertNotSentToApps: Boolean = false,
+        timeDelayMs: Long = 0
+    ) {
         var metaState = 0
         val now = SystemClock.uptimeMillis()
         for (key in testKeys) {
@@ -1699,6 +1780,11 @@
             testLooper.dispatchAll()
         }
 
+        if (timeDelayMs > 0) {
+            testLooper.moveTimeForward(timeDelayMs)
+            testLooper.dispatchAll()
+        }
+
         for (key in testKeys.reversed()) {
             val upEvent = KeyEvent(
                 now, now, KeyEvent.ACTION_UP, key, 0 /*repeat*/, metaState,
@@ -1742,9 +1828,5 @@
         override fun handleKeyGesture(event: AidlKeyGestureEvent, token: IBinder?): Boolean {
             return handler(event, token)
         }
-
-        override fun isKeyGestureSupported(gestureType: Int): Boolean {
-            return true
-        }
     }
 }
diff --git a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewControllerTests.java b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewControllerTests.java
index 5875520..20528f23 100644
--- a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewControllerTests.java
+++ b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewControllerTests.java
@@ -23,7 +23,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
 import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.testing.AndroidTestingRunner;
@@ -60,9 +59,12 @@
     private static final String TAG = "TouchpadDebugViewController";
 
     @Rule
+    public final TestableContext mTestableContext =
+            new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
+
+    @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
 
-    private Context mContext;
     private TouchpadDebugViewController mTouchpadDebugViewController;
     @Mock
     private InputManager mInputManagerMock;
@@ -74,8 +76,6 @@
 
     @Before
     public void setup() throws Exception {
-        mContext = InstrumentationRegistry.getInstrumentation().getContext();
-        TestableContext mTestableContext = new TestableContext(mContext);
         mTestableContext.addMockSystemService(WindowManager.class, mWindowManagerMock);
 
         Rect bounds = new Rect(0, 0, 2560, 1600);
diff --git a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
index 60fa52f..1c366a1 100644
--- a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
+++ b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
@@ -51,6 +50,7 @@
 import com.android.server.input.TouchpadHardwareState;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -70,6 +70,10 @@
     private TouchpadDebugView mTouchpadDebugView;
     private WindowManager.LayoutParams mWindowLayoutParams;
 
+    @Rule
+    public final TestableContext mTestableContext =
+            new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
+
     @Mock
     WindowManager mWindowManager;
     @Mock
@@ -77,14 +81,10 @@
 
     Rect mWindowBounds;
     WindowMetrics mWindowMetrics;
-    TestableContext mTestableContext;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        mTestableContext = new TestableContext(context);
-
         mTestableContext.addMockSystemService(WindowManager.class, mWindowManager);
         mTestableContext.addMockSystemService(InputManager.class, mInputManager);
 
diff --git a/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java b/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
index d16e90e..9d60e7c 100644
--- a/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
+++ b/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
@@ -20,7 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
@@ -123,7 +123,7 @@
         doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
         doReturn(mImeLocales).when(mAppLocaleCollector).getActiveImeLocales();
         doReturn(mSystemSupportedLocales).when(mAppLocaleCollector).getSystemSupportedLocale(
-                anyObject(), eq(null), eq(true));
+                any(), eq(null), eq(true));
         doReturn(mSystemCurrentLocales).when(
                 mAppLocaleCollector).getSystemCurrentLocales();
 
@@ -159,7 +159,7 @@
         doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
         doReturn(mImeLocales).when(mAppLocaleCollector).getActiveImeLocales();
         doReturn(mSystemSupportedLocales).when(mAppLocaleCollector).getSystemSupportedLocale(
-                anyObject(), eq(null), eq(true));
+                any(), eq(null), eq(true));
         doReturn(mSystemCurrentLocales).when(
                 mAppLocaleCollector).getSystemCurrentLocales();
 
diff --git a/tests/PackageWatchdog/src/com/android/server/RescuePartyTest.java b/tests/PackageWatchdog/src/com/android/server/RescuePartyTest.java
new file mode 100644
index 0000000..eda5e86
--- /dev/null
+++ b/tests/PackageWatchdog/src/com/android/server/RescuePartyTest.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.os.RecoverySystem;
+import android.os.SystemProperties;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
+import com.android.server.RescueParty.RescuePartyObserver;
+import com.android.server.am.SettingsToPropertiesMapper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Test RescueParty.
+ */
+public class RescuePartyTest {
+    private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
+
+    private static VersionedPackage sFailingPackage = new VersionedPackage("com.package.name", 1);
+    private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
+    private static final String PERSISTENT_PACKAGE = "com.persistent.package";
+    private static final String NON_PERSISTENT_PACKAGE = "com.nonpersistent.package";
+    private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
+            "persist.device_config.configuration.disable_rescue_party";
+    private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
+            "persist.device_config.configuration.disable_rescue_party_factory_reset";
+
+    private MockitoSession mSession;
+    private HashMap<String, String> mSystemSettingsMap;
+    private HashMap<String, String> mCrashRecoveryPropertiesMap;
+    //Records the namespaces wiped by setProperties().
+    private HashSet<String> mNamespacesWiped;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mMockContext;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PackageWatchdog mMockPackageWatchdog;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private ContentResolver mMockContentResolver;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PackageManager mPackageManager;
+
+    // Mock only sysprop apis
+    private PackageWatchdog.BootThreshold mSpyBootThreshold;
+
+    @Before
+    public void setUp() throws Exception {
+        mSession =
+                ExtendedMockito.mockitoSession().initMocks(
+                        this)
+                        .strictness(Strictness.LENIENT)
+                        .spyStatic(DeviceConfig.class)
+                        .spyStatic(SystemProperties.class)
+                        .spyStatic(Settings.Global.class)
+                        .spyStatic(Settings.Secure.class)
+                        .spyStatic(SettingsToPropertiesMapper.class)
+                        .spyStatic(RecoverySystem.class)
+                        .spyStatic(RescueParty.class)
+                        .spyStatic(PackageWatchdog.class)
+                        .startMocking();
+        mSystemSettingsMap = new HashMap<>();
+        mNamespacesWiped = new HashSet<>();
+
+        when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
+        when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+        ApplicationInfo persistentApplicationInfo = new ApplicationInfo();
+        persistentApplicationInfo.flags |=
+                ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT;
+
+        // If the package name is PERSISTENT_PACKAGE, then set the flags to be persistent and
+        // system. Don't set any flags otherwise.
+        when(mPackageManager.getApplicationInfo(eq(PERSISTENT_PACKAGE),
+                anyInt())).thenReturn(persistentApplicationInfo);
+        when(mPackageManager.getApplicationInfo(eq(NON_PERSISTENT_PACKAGE),
+                anyInt())).thenReturn(new ApplicationInfo());
+        // Reset observer instance to get new mock context on every run
+        RescuePartyObserver.reset();
+
+        // Mock SystemProperties setter and various getters
+        doAnswer((Answer<Void>) invocationOnMock -> {
+                    String key = invocationOnMock.getArgument(0);
+                    String value = invocationOnMock.getArgument(1);
+
+                    mSystemSettingsMap.put(key, value);
+                    return null;
+                }
+        ).when(() -> SystemProperties.set(anyString(), anyString()));
+
+        doAnswer((Answer<Boolean>) invocationOnMock -> {
+                    String key = invocationOnMock.getArgument(0);
+                    boolean defaultValue = invocationOnMock.getArgument(1);
+
+                    String storedValue = mSystemSettingsMap.get(key);
+                    return storedValue == null ? defaultValue : Boolean.parseBoolean(storedValue);
+                }
+        ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean()));
+
+        doAnswer((Answer<Integer>) invocationOnMock -> {
+                    String key = invocationOnMock.getArgument(0);
+                    int defaultValue = invocationOnMock.getArgument(1);
+
+                    String storedValue = mSystemSettingsMap.get(key);
+                    return storedValue == null ? defaultValue : Integer.parseInt(storedValue);
+                }
+        ).when(() -> SystemProperties.getInt(anyString(), anyInt()));
+
+        doAnswer((Answer<Long>) invocationOnMock -> {
+                    String key = invocationOnMock.getArgument(0);
+                    long defaultValue = invocationOnMock.getArgument(1);
+
+                    String storedValue = mSystemSettingsMap.get(key);
+                    return storedValue == null ? defaultValue : Long.parseLong(storedValue);
+                }
+        ).when(() -> SystemProperties.getLong(anyString(), anyLong()));
+
+        // Mock DeviceConfig
+        doAnswer((Answer<Boolean>) invocationOnMock -> true)
+                .when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(),
+                        anyBoolean()));
+        doAnswer((Answer<Void>) invocationOnMock -> null)
+                .when(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()));
+        doAnswer((Answer<Boolean>) invocationOnMock -> {
+                    DeviceConfig.Properties properties = invocationOnMock.getArgument(0);
+                    String namespace = properties.getNamespace();
+                    // record a wipe
+                    if (properties.getKeyset().isEmpty()) {
+                        mNamespacesWiped.add(namespace);
+                    }
+                    return true;
+                }
+        ).when(() -> DeviceConfig.setProperties(any(DeviceConfig.Properties.class)));
+
+        // Mock PackageWatchdog
+        doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog)
+                .when(() -> PackageWatchdog.getInstance(mMockContext));
+        mockCrashRecoveryProperties(mMockPackageWatchdog);
+
+        doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
+
+        setCrashRecoveryPropRescueBootCount(0);
+        SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
+        SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mSession.finishMocking();
+    }
+
+    @Test
+    public void testBootLoopNoFlags() {
+        // this is old test where the flag needs to be disabled
+        noteBoot(1);
+        assertTrue(RescueParty.isRebootPropertySet());
+
+        setCrashRecoveryPropAttemptingReboot(false);
+        noteBoot(2);
+        assertTrue(RescueParty.isFactoryResetPropertySet());
+    }
+
+    @Test
+    public void testPersistentAppCrashNoFlags() {
+        // this is old test where the flag needs to be disabled
+        noteAppCrash(1, true);
+        assertTrue(RescueParty.isRebootPropertySet());
+
+        setCrashRecoveryPropAttemptingReboot(false);
+        noteAppCrash(2, true);
+        assertTrue(RescueParty.isFactoryResetPropertySet());
+    }
+
+    @Test
+    public void testIsRecoveryTriggeredReboot() {
+        for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
+            noteBoot(i + 1);
+        }
+        assertFalse(RescueParty.isFactoryResetPropertySet());
+        setCrashRecoveryPropAttemptingReboot(false);
+        noteBoot(LEVEL_FACTORY_RESET + 1);
+        assertTrue(RescueParty.isRecoveryTriggeredReboot());
+        assertTrue(RescueParty.isFactoryResetPropertySet());
+    }
+
+    @Test
+    public void testIsRecoveryTriggeredRebootOnlyAfterRebootCompleted() {
+        for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
+            noteBoot(i + 1);
+        }
+        int mitigationCount = LEVEL_FACTORY_RESET + 1;
+        assertFalse(RescueParty.isFactoryResetPropertySet());
+        noteBoot(mitigationCount++);
+        assertFalse(RescueParty.isFactoryResetPropertySet());
+        noteBoot(mitigationCount++);
+        assertFalse(RescueParty.isFactoryResetPropertySet());
+        noteBoot(mitigationCount++);
+        setCrashRecoveryPropAttemptingReboot(false);
+        noteBoot(mitigationCount + 1);
+        assertTrue(RescueParty.isRecoveryTriggeredReboot());
+        assertTrue(RescueParty.isFactoryResetPropertySet());
+    }
+
+    @Test
+    public void testThrottlingOnBootFailures() {
+        setCrashRecoveryPropAttemptingReboot(false);
+        long now = System.currentTimeMillis();
+        long beforeTimeout = now - TimeUnit.MINUTES.toMillis(
+                DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN - 1);
+        setCrashRecoveryPropLastFactoryReset(beforeTimeout);
+        for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
+            noteBoot(i);
+        }
+        assertFalse(RescueParty.isRecoveryTriggeredReboot());
+    }
+
+    @Test
+    public void testThrottlingOnAppCrash() {
+        setCrashRecoveryPropAttemptingReboot(false);
+        long now = System.currentTimeMillis();
+        long beforeTimeout = now - TimeUnit.MINUTES.toMillis(
+                DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN - 1);
+        setCrashRecoveryPropLastFactoryReset(beforeTimeout);
+        for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
+            noteAppCrash(i + 1, true);
+        }
+        assertFalse(RescueParty.isRecoveryTriggeredReboot());
+    }
+
+    @Test
+    public void testNotThrottlingAfterTimeoutOnBootFailures() {
+        setCrashRecoveryPropAttemptingReboot(false);
+        long now = System.currentTimeMillis();
+        long afterTimeout = now - TimeUnit.MINUTES.toMillis(
+                DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN + 1);
+        setCrashRecoveryPropLastFactoryReset(afterTimeout);
+        for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
+            noteBoot(i);
+        }
+        assertTrue(RescueParty.isRecoveryTriggeredReboot());
+    }
+
+    @Test
+    public void testNotThrottlingAfterTimeoutOnAppCrash() {
+        when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+        setCrashRecoveryPropAttemptingReboot(false);
+        long now = System.currentTimeMillis();
+        long afterTimeout = now - TimeUnit.MINUTES.toMillis(
+                DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN + 1);
+        setCrashRecoveryPropLastFactoryReset(afterTimeout);
+        for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
+            noteAppCrash(i + 1, true);
+        }
+        assertTrue(RescueParty.isRecoveryTriggeredReboot());
+    }
+
+    @Test
+    public void testExplicitlyEnablingAndDisablingRescue() {
+        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),
+                MITIGATION_RESULT_SKIPPED);
+
+        SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
+        assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+                sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+                MITIGATION_RESULT_SUCCESS);
+    }
+
+    @Test
+    public void testDisablingRescueByDeviceConfigFlag() {
+        SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
+        SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true));
+
+        assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+                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));
+        SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
+    }
+
+    @Test
+    public void testDisablingFactoryResetByDeviceConfigFlag() {
+        SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true));
+
+        for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
+            noteBoot(i + 1);
+        }
+        assertFalse(RescueParty.isFactoryResetPropertySet());
+
+        // Restore the property value initialized in SetUp()
+        SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, "");
+    }
+
+    @Test
+    public void testHealthCheckLevelsNoFlags() {
+        // this is old test where the flag needs to be disabled
+        RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+
+        // Ensure that no action is taken for cases where the failure reason is unknown
+        assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN, 1),
+                PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
+
+        // Ensure the correct user impact is returned for each mitigation count.
+        assertEquals(observer.onHealthCheckFailed(null,
+                        PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+                PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
+
+        assertEquals(observer.onHealthCheckFailed(null,
+                        PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2),
+                PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
+    }
+
+    @Test
+    public void testBootLoopLevelsNoFlags() {
+        RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+
+        assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
+        assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
+    }
+
+
+    private void noteBoot(int mitigationCount) {
+        RescuePartyObserver.getInstance(mMockContext).onExecuteBootLoopMitigation(mitigationCount);
+    }
+
+    private void noteAppCrash(int mitigationCount, boolean isPersistent) {
+        String packageName = isPersistent ? PERSISTENT_PACKAGE : NON_PERSISTENT_PACKAGE;
+        RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+                new VersionedPackage(packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH,
+                mitigationCount);
+    }
+
+    // Mock CrashRecoveryProperties as they cannot be accessed due to SEPolicy restrictions
+    private void mockCrashRecoveryProperties(PackageWatchdog watchdog) {
+        // mock properties in RescueParty
+        try {
+
+            doAnswer((Answer<Boolean>) invocationOnMock -> {
+                String storedValue = mCrashRecoveryPropertiesMap
+                        .getOrDefault("crashrecovery.attempting_factory_reset", "false");
+                return Boolean.parseBoolean(storedValue);
+            }).when(() -> RescueParty.isFactoryResetPropertySet());
+            doAnswer((Answer<Void>) invocationOnMock -> {
+                boolean value = invocationOnMock.getArgument(0);
+                mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_factory_reset",
+                        Boolean.toString(value));
+                return null;
+            }).when(() -> RescueParty.setFactoryResetProperty(anyBoolean()));
+
+            doAnswer((Answer<Boolean>) invocationOnMock -> {
+                String storedValue = mCrashRecoveryPropertiesMap
+                        .getOrDefault("crashrecovery.attempting_reboot", "false");
+                return Boolean.parseBoolean(storedValue);
+            }).when(() -> RescueParty.isRebootPropertySet());
+            doAnswer((Answer<Void>) invocationOnMock -> {
+                boolean value = invocationOnMock.getArgument(0);
+                setCrashRecoveryPropAttemptingReboot(value);
+                return null;
+            }).when(() -> RescueParty.setRebootProperty(anyBoolean()));
+
+            doAnswer((Answer<Long>) invocationOnMock -> {
+                String storedValue = mCrashRecoveryPropertiesMap
+                        .getOrDefault("persist.crashrecovery.last_factory_reset", "0");
+                return Long.parseLong(storedValue);
+            }).when(() -> RescueParty.getLastFactoryResetTimeMs());
+            doAnswer((Answer<Void>) invocationOnMock -> {
+                long value = invocationOnMock.getArgument(0);
+                setCrashRecoveryPropLastFactoryReset(value);
+                return null;
+            }).when(() -> RescueParty.setLastFactoryResetTimeMs(anyLong()));
+
+            doAnswer((Answer<Integer>) invocationOnMock -> {
+                String storedValue = mCrashRecoveryPropertiesMap
+                        .getOrDefault("crashrecovery.max_rescue_level_attempted", "0");
+                return Integer.parseInt(storedValue);
+            }).when(() -> RescueParty.getMaxRescueLevelAttempted());
+            doAnswer((Answer<Void>) invocationOnMock -> {
+                int value = invocationOnMock.getArgument(0);
+                mCrashRecoveryPropertiesMap.put("crashrecovery.max_rescue_level_attempted",
+                        Integer.toString(value));
+                return null;
+            }).when(() -> RescueParty.setMaxRescueLevelAttempted(anyInt()));
+
+        } catch (Exception e) {
+            // tests will fail, just printing the error
+            System.out.println("Error while mocking crashrecovery properties " + e.getMessage());
+        }
+
+        // mock properties in BootThreshold
+        try {
+            mSpyBootThreshold = spy(watchdog.new BootThreshold(
+                    PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
+                    PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS));
+            mCrashRecoveryPropertiesMap = new HashMap<>();
+
+            doAnswer((Answer<Integer>) invocationOnMock -> {
+                String storedValue = mCrashRecoveryPropertiesMap
+                        .getOrDefault("crashrecovery.rescue_boot_count", "0");
+                return Integer.parseInt(storedValue);
+            }).when(mSpyBootThreshold).getCount();
+            doAnswer((Answer<Void>) invocationOnMock -> {
+                int count = invocationOnMock.getArgument(0);
+                setCrashRecoveryPropRescueBootCount(count);
+                return null;
+            }).when(mSpyBootThreshold).setCount(anyInt());
+
+            doAnswer((Answer<Integer>) invocationOnMock -> {
+                String storedValue = mCrashRecoveryPropertiesMap
+                        .getOrDefault("crashrecovery.boot_mitigation_count", "0");
+                return Integer.parseInt(storedValue);
+            }).when(mSpyBootThreshold).getMitigationCount();
+            doAnswer((Answer<Void>) invocationOnMock -> {
+                int count = invocationOnMock.getArgument(0);
+                mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_count",
+                        Integer.toString(count));
+                return null;
+            }).when(mSpyBootThreshold).setMitigationCount(anyInt());
+
+            doAnswer((Answer<Long>) invocationOnMock -> {
+                String storedValue = mCrashRecoveryPropertiesMap
+                        .getOrDefault("crashrecovery.rescue_boot_start", "0");
+                return Long.parseLong(storedValue);
+            }).when(mSpyBootThreshold).getStart();
+            doAnswer((Answer<Void>) invocationOnMock -> {
+                long count = invocationOnMock.getArgument(0);
+                mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_start",
+                        Long.toString(count));
+                return null;
+            }).when(mSpyBootThreshold).setStart(anyLong());
+
+            doAnswer((Answer<Long>) invocationOnMock -> {
+                String storedValue = mCrashRecoveryPropertiesMap
+                        .getOrDefault("crashrecovery.boot_mitigation_start", "0");
+                return Long.parseLong(storedValue);
+            }).when(mSpyBootThreshold).getMitigationStart();
+            doAnswer((Answer<Void>) invocationOnMock -> {
+                long count = invocationOnMock.getArgument(0);
+                mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_start",
+                        Long.toString(count));
+                return null;
+            }).when(mSpyBootThreshold).setMitigationStart(anyLong());
+
+            Field mBootThresholdField = watchdog.getClass().getDeclaredField("mBootThreshold");
+            mBootThresholdField.setAccessible(true);
+            mBootThresholdField.set(watchdog, mSpyBootThreshold);
+        } catch (Exception e) {
+            // tests will fail, just printing the error
+            System.out.println("Error while spying BootThreshold " + e.getMessage());
+        }
+    }
+
+    private void setCrashRecoveryPropRescueBootCount(int count) {
+        mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_count",
+                Integer.toString(count));
+    }
+
+    private void setCrashRecoveryPropAttemptingReboot(boolean value) {
+        mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_reboot",
+                Boolean.toString(value));
+    }
+
+    private void setCrashRecoveryPropLastFactoryReset(long value) {
+        mCrashRecoveryPropertiesMap.put("persist.crashrecovery.last_factory_reset",
+                Long.toString(value));
+    }
+}
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
index 0c3c7e2..e868a6c 100644
--- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -34,7 +34,7 @@
 import org.junit.Test
 import org.junit.rules.RuleChain
 import org.junit.runner.RunWith
-import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.verifyNoMoreInteractions
 
 /**
  * Test for testing revokeTrust & grantTrust for non-renewable trust.
@@ -120,7 +120,7 @@
         trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0, callback)
         await()
 
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
     }
 
     companion object {
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
index f5d4b0c..cc7eebc 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java
@@ -33,7 +33,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -120,7 +120,7 @@
 
         verify(mUsbPortManager).enableUsbData(TEST_PORT_ID,
                 enable, TEST_TRANSACTION_ID, mCallback, null);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
 
         clearInvocations(mUsbPortManager);
         clearInvocations(mCallback);
@@ -131,7 +131,7 @@
         assertFalse(mUsbService.enableUsbDataInternal(TEST_PORT_ID, enable,
                 TEST_TRANSACTION_ID, mCallback, requester, isInternalRequest));
 
-        verifyZeroInteractions(mUsbPortManager);
+        verifyNoMoreInteractions(mUsbPortManager);
         verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
 
         clearInvocations(mUsbPortManager);
@@ -188,7 +188,7 @@
         mUsbService.enableUsbDataWhileDockedInternal(TEST_PORT_ID, TEST_TRANSACTION_ID,
                 mCallback, TEST_SECOND_CALLER_ID, false);
 
-        verifyZeroInteractions(mUsbPortManager);
+        verifyNoMoreInteractions(mUsbPortManager);
         verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
     }
 
@@ -203,7 +203,7 @@
 
         verify(mUsbPortManager).enableUsbDataWhileDocked(TEST_PORT_ID, TEST_TRANSACTION_ID,
                         mCallback, null);
-        verifyZeroInteractions(mCallback);
+        verifyNoMoreInteractions(mCallback);
     }
 
     /**
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 8cd89ce..6b09945 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -75,16 +75,7 @@
      * Baklava introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
      */
     private static boolean isAtLeastBaklava() {
-        Method[] methods = TestLooperManager.class.getMethods();
-        for (Method method : methods) {
-            if (method.getName().equals("peekWhen")) {
-                return true;
-            }
-        }
-        return false;
-        // TODO(shayba): delete the above, uncomment the below.
-        // SDK_INT has not yet ramped to Baklava in all 25Q2 builds.
-        // return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
     }
 
     static {
@@ -247,6 +238,36 @@
         while (processQueuedMessages() != 0) ;
     }
 
+    public long peekWhen() {
+        if (isAtLeastBaklava()) {
+            return peekWhenBaklava();
+        } else {
+            return peekWhenLegacy();
+        }
+    }
+
+    private long peekWhenBaklava() {
+        Long when = mQueueWrapper.peekWhen();
+        if (when != null) {
+            return when;
+        } else {
+            return 0;
+        }
+    }
+
+    private long peekWhenLegacy() {
+        try {
+            Message msg = (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(mLooper.getQueue());
+            if (msg != null) {
+                return msg.getWhen();
+            } else {
+                return 0;
+            }
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException("Access failed in TestableLooper: set - Message.when", e);
+        }
+    }
+
     public void moveTimeForward(long milliSeconds) {
         if (isAtLeastBaklava()) {
             moveTimeForwardBaklava(milliSeconds);
diff --git a/tests/testables/tests/src/android/testing/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
index fd5c4ca..a7e0125 100644
--- a/tests/testables/tests/src/android/testing/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -17,8 +17,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index 4d379e4..bb54a26 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -68,16 +68,7 @@
      * Baklava introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
      */
     private static boolean isAtLeastBaklava() {
-        Method[] methods = TestLooperManager.class.getMethods();
-        for (Method method : methods) {
-            if (method.getName().equals("peekWhen")) {
-                return true;
-            }
-        }
-        return false;
-        // TODO(shayba): delete the above, uncomment the below.
-        // SDK_INT has not yet ramped to Baklava in all 25Q2 builds.
-        // return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
     }
 
     static {
diff --git a/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java b/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
index 6205b98..05f237f 100644
--- a/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
+++ b/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
@@ -19,7 +19,7 @@
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 6608dda..a349080 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -37,10 +37,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index f6123d2..e1a572e 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -21,7 +21,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 8374fd9..7f0cabf 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -24,8 +24,8 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.CALLS_REAL_METHODS;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
index 2b92428..0185931 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
@@ -18,9 +18,9 @@
 
 import static android.net.NetworkProvider.NetworkOfferCallback;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index bd4aeba..c12adcb 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -31,10 +31,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+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.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
index 27c1bc1..3ca84cf 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
@@ -28,7 +28,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -83,7 +82,7 @@
                 .thenReturn(mIpSecPacketLossDetector);
 
         when(mCarrierConfig.getIntArray(
-                        eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), anyObject()))
+                        eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), any()))
                 .thenReturn(new int[] {PENALTY_TIMEOUT_MIN});
 
         mNetworkEvaluator = newValidUnderlyingNetworkEvaluator();
@@ -309,7 +308,7 @@
     public void testSetCarrierConfig() throws Exception {
         final int additionalTimeoutMin = 10;
         when(mCarrierConfig.getIntArray(
-                        eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), anyObject()))
+                        eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), any()))
                 .thenReturn(new int[] {PENALTY_TIMEOUT_MIN + additionalTimeoutMin});
 
         // Update evaluator and penalize the network
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 0d261ab..e51477c 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -249,6 +249,8 @@
 
   // Flag
   std::optional<FeatureFlagAttribute> flag;
+
+  bool uses_readwrite_feature_flags = false;
 };
 
 /**
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 5435cba2..db7dddc 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -664,6 +664,7 @@
     if (!config_value->value) {
       // Resource does not exist, add it now.
       config_value->value = std::move(res.value);
+      config_value->uses_readwrite_feature_flags = res.uses_readwrite_feature_flags;
     } else {
       // When validation is enabled, ensure that a resource cannot have multiple values defined for
       // the same configuration unless protected by flags.
@@ -681,12 +682,14 @@
                                ConfigKey{&res.config, res.product}, lt_config_key_ref()),
               util::make_unique<ResourceConfigValue>(res.config, res.product));
           (*it)->value = std::move(res.value);
+          (*it)->uses_readwrite_feature_flags = res.uses_readwrite_feature_flags;
           break;
         }
 
         case CollisionResult::kTakeNew:
           // Take the incoming value.
           config_value->value = std::move(res.value);
+          config_value->uses_readwrite_feature_flags = res.uses_readwrite_feature_flags;
           break;
 
         case CollisionResult::kConflict:
@@ -843,6 +846,12 @@
   return *this;
 }
 
+NewResourceBuilder& NewResourceBuilder::SetUsesReadWriteFeatureFlags(
+    bool uses_readwrite_feature_flags) {
+  res_.uses_readwrite_feature_flags = uses_readwrite_feature_flags;
+  return *this;
+}
+
 NewResource NewResourceBuilder::Build() {
   return std::move(res_);
 }
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index b0e1855..778b43a 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -104,6 +104,9 @@
   // The actual Value.
   std::unique_ptr<Value> value;
 
+  // Whether the value uses read/write feature flags
+  bool uses_readwrite_feature_flags = false;
+
   ResourceConfigValue(const android::ConfigDescription& config, android::StringPiece product)
       : config(config), product(product) {
   }
@@ -284,6 +287,7 @@
   std::optional<AllowNew> allow_new;
   std::optional<StagedId> staged_id;
   bool allow_mangled = false;
+  bool uses_readwrite_feature_flags = false;
 };
 
 struct NewResourceBuilder {
@@ -297,6 +301,7 @@
   NewResourceBuilder& SetAllowNew(AllowNew allow_new);
   NewResourceBuilder& SetStagedId(StagedId id);
   NewResourceBuilder& SetAllowMangled(bool allow_mangled);
+  NewResourceBuilder& SetUsesReadWriteFeatureFlags(bool uses_feature_flags);
   NewResource Build();
 
  private:
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 2a79216..755dbb6 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -673,11 +673,13 @@
 
               // Update the output format of this XML file.
               file_ref->type = XmlFileTypeForOutputFormat(options_.output_format);
-              bool result = table->AddResource(NewResourceBuilder(file.name)
-                                                   .SetValue(std::move(file_ref), file.config)
-                                                   .SetAllowMangled(true)
-                                                   .Build(),
-                                               context_->GetDiagnostics());
+              bool result = table->AddResource(
+                  NewResourceBuilder(file.name)
+                      .SetValue(std::move(file_ref), file.config)
+                      .SetAllowMangled(true)
+                      .SetUsesReadWriteFeatureFlags(doc->file.uses_readwrite_feature_flags)
+                      .Build(),
+                  context_->GetDiagnostics());
               if (!result) {
                 return false;
               }
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 2e20e81..bac871b 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -414,6 +414,8 @@
         .SetId(res_id, OnIdConflict::CREATE_ENTRY)
         .SetAllowMangled(true);
 
+    res_builder.SetUsesReadWriteFeatureFlags(entry->uses_feature_flags());
+
     if (entry->flags() & ResTable_entry::FLAG_PUBLIC) {
       Visibility visibility{Visibility::Level::kPublic};
 
diff --git a/tools/aapt2/format/binary/ResEntryWriter.cpp b/tools/aapt2/format/binary/ResEntryWriter.cpp
index 9dc205f..0be3921 100644
--- a/tools/aapt2/format/binary/ResEntryWriter.cpp
+++ b/tools/aapt2/format/binary/ResEntryWriter.cpp
@@ -199,6 +199,10 @@
     flags |= ResTable_entry::FLAG_WEAK;
   }
 
+  if (entry->uses_readwrite_feature_flags) {
+    flags |= ResTable_entry::FLAG_USES_FEATURE_FLAGS;
+  }
+
   if constexpr (std::is_same_v<ResTable_entry_ext, T>) {
     flags |= ResTable_entry::FLAG_COMPLEX;
   }
diff --git a/tools/aapt2/format/binary/ResEntryWriter.h b/tools/aapt2/format/binary/ResEntryWriter.h
index c11598e..f54b29a 100644
--- a/tools/aapt2/format/binary/ResEntryWriter.h
+++ b/tools/aapt2/format/binary/ResEntryWriter.h
@@ -38,6 +38,8 @@
 
   // The entry string pool index to the entry's name.
   uint32_t entry_key;
+
+  bool uses_readwrite_feature_flags;
 };
 
 // Pair of ResTable_entry and Res_value. These pairs are stored sequentially in values buffer.
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 1a82021..50144ae 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -502,7 +502,8 @@
         // Group values by configuration.
         for (auto& config_value : entry.values) {
           config_to_entry_list_map[config_value->config].push_back(
-              FlatEntry{&entry, config_value->value.get(), local_key_index});
+              FlatEntry{&entry, config_value->value.get(), local_key_index,
+                        config_value->uses_readwrite_feature_flags});
         }
       }
 
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 0f11685..9156b96 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -1069,4 +1069,23 @@
               testing::IsTrue());
 }
 
+TEST_F(TableFlattenerTest, UsesReadWriteFeatureFlagSerializesCorrectly) {
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .Add(NewResourceBuilder("com.app.a:color/foo")
+                   .SetValue(util::make_unique<BinaryPrimitive>(
+                       uint8_t(Res_value::TYPE_INT_COLOR_ARGB8), 0xffaabbcc))
+                   .SetUsesReadWriteFeatureFlags(true)
+                   .SetId(0x7f020000)
+                   .Build())
+          .Build();
+  ResTable res_table;
+  TableFlattenerOptions options;
+  ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));
+
+  uint32_t flags;
+  ASSERT_TRUE(res_table.getResourceEntryFlags(0x7f020000, &flags));
+  ASSERT_EQ(flags, ResTable_entry::FLAG_USES_FEATURE_FLAGS);
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/link/FlaggedResources_test.cpp b/tools/aapt2/link/FlaggedResources_test.cpp
index dbef776..47a71fe 100644
--- a/tools/aapt2/link/FlaggedResources_test.cpp
+++ b/tools/aapt2/link/FlaggedResources_test.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <regex>
+#include <string>
+
 #include "LoadedApk.h"
 #include "cmd/Dump.h"
 #include "io/StringStream.h"
@@ -183,4 +186,49 @@
       "Only read only flags may be used with resources: test.package.rwFlag"));
 }
 
+TEST_F(FlaggedResourcesTest, ReadWriteFlagInXmlGetsFlagged) {
+  auto apk_path = file::BuildPath({android::base::GetExecutableDirectory(), "resapp.apk"});
+  auto loaded_apk = LoadedApk::LoadApkFromPath(apk_path, &noop_diag);
+
+  std::string output;
+  DumpChunksToString(loaded_apk.get(), &output);
+
+  // The actual line looks something like:
+  // [ResTable_entry] id: 0x0000 name: layout1 keyIndex: 14 size: 8 flags: 0x0010
+  //
+  // This regex matches that line and captures the name and the flag value for checking.
+  std::regex regex("[0-9a-zA-Z:_\\]\\[ ]+name: ([0-9a-zA-Z]+)[0-9a-zA-Z: ]+flags: (0x\\d{4})");
+  std::smatch match;
+
+  std::stringstream ss(output);
+  std::string line;
+  bool found = false;
+  int fields_flagged = 0;
+  while (std::getline(ss, line)) {
+    bool first_line = false;
+    if (line.contains("config: v36")) {
+      std::getline(ss, line);
+      first_line = true;
+    }
+    if (!line.contains("flags")) {
+      continue;
+    }
+    if (std::regex_search(line, match, regex) && (match.size() == 3)) {
+      unsigned int hex_value;
+      std::stringstream hex_ss;
+      hex_ss << std::hex << match[2];
+      hex_ss >> hex_value;
+      if (hex_value & android::ResTable_entry::FLAG_USES_FEATURE_FLAGS) {
+        fields_flagged++;
+        if (first_line && match[1] == "layout1") {
+          found = true;
+        }
+      }
+    }
+  }
+  ASSERT_TRUE(found) << "No entry for layout1 at v36 with FLAG_USES_FEATURE_FLAGS bit set";
+  // There should only be 1 entry that has the FLAG_USES_FEATURE_FLAGS bit of flags set to 1
+  ASSERT_EQ(fields_flagged, 1);
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/link/FlaggedXmlVersioner.cpp b/tools/aapt2/link/FlaggedXmlVersioner.cpp
index 75c6f17..8a3337c 100644
--- a/tools/aapt2/link/FlaggedXmlVersioner.cpp
+++ b/tools/aapt2/link/FlaggedXmlVersioner.cpp
@@ -66,6 +66,28 @@
   bool had_flags_ = false;
 };
 
+// An xml visitor that goes through the a doc and determines if any elements are behind a flag.
+class FindFlagsVisitor : public xml::Visitor {
+ public:
+  void Visit(xml::Element* node) override {
+    if (had_flags_) {
+      return;
+    }
+    auto* attr = node->FindAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag);
+    if (attr != nullptr) {
+      had_flags_ = true;
+      return;
+    }
+    VisitChildren(node);
+  }
+
+  bool HadFlags() const {
+    return had_flags_;
+  }
+
+  bool had_flags_ = false;
+};
+
 std::vector<std::unique_ptr<xml::XmlResource>> FlaggedXmlVersioner::Process(IAaptContext* context,
                                                                             xml::XmlResource* doc) {
   std::vector<std::unique_ptr<xml::XmlResource>> docs;
@@ -74,15 +96,20 @@
     // Support for read/write flags was added in baklava so if the doc will only get used on
     // baklava or later we can just return the original doc.
     docs.push_back(doc->Clone());
+    FindFlagsVisitor visitor;
+    doc->root->Accept(&visitor);
+    docs.back()->file.uses_readwrite_feature_flags = visitor.HadFlags();
   } else {
     auto preBaklavaVersion = doc->Clone();
     AllDisabledFlagsVisitor visitor;
     preBaklavaVersion->root->Accept(&visitor);
+    preBaklavaVersion->file.uses_readwrite_feature_flags = false;
     docs.push_back(std::move(preBaklavaVersion));
 
     if (visitor.HadFlags()) {
       auto baklavaVersion = doc->Clone();
       baklavaVersion->file.config.sdkVersion = SDK_BAKLAVA;
+      baklavaVersion->file.uses_readwrite_feature_flags = true;
       docs.push_back(std::move(baklavaVersion));
     }
   }
diff --git a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
index 4995eeb..fe72dae 100644
--- a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
+++ b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
@@ -155,35 +155,35 @@
         ) {
             val indent = "  "
 
-            writer.appendln("{")
+            writer.appendLine("{")
 
             val intDefTypesCount = annotationTypeToIntDefMapping.size
             var currentIntDefTypesCount = 0
             for ((field, intDefMapping) in annotationTypeToIntDefMapping) {
-                writer.appendln("""$indent"$field": {""")
+                writer.appendLine("""$indent"$field": {""")
 
                 // Start IntDef
 
-                writer.appendln("""$indent$indent"flag": ${intDefMapping.flag},""")
+                writer.appendLine("""$indent$indent"flag": ${intDefMapping.flag},""")
 
-                writer.appendln("""$indent$indent"values": {""")
+                writer.appendLine("""$indent$indent"values": {""")
                 intDefMapping.entries.joinTo(writer, separator = ",\n") { (value, identifier) ->
                     """$indent$indent$indent"$value": "$identifier""""
                 }
-                writer.appendln()
-                writer.appendln("$indent$indent}")
+                writer.appendLine()
+                writer.appendLine("$indent$indent}")
 
                 // End IntDef
 
                 writer.append("$indent}")
                 if (++currentIntDefTypesCount < intDefTypesCount) {
-                    writer.appendln(",")
+                    writer.appendLine(",")
                 } else {
-                    writer.appendln("")
+                    writer.appendLine("")
                 }
             }
 
-            writer.appendln("}")
+            writer.appendLine("}")
         }
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 02da835..c30e7bf 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -23,7 +23,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.argThat;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyLong;
 import static org.mockito.Mockito.doNothing;